Compare commits

..
Author SHA1 Message Date
TermiNexus 15b416b527 fix(type): 统一注入期类归属与导入上下文记账,修占位表爆与属性标记误解析
- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport /
  ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份
  口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本

- 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1):
  _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute
  分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增
  _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争

- VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty
  原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot
  register Translator)

- 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、
  [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin)

根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec
(TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结
(CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
2026-10-02 17:29:38 +08:00
TermiNexus 2c33effee1 fix(type): 前向声明补 import-as 别名反查(LookupModNameByAlias)
_forward_declare_ptr_from_node 的 Attribute 分支此前仅走
ImportsGen.lookup_from_import 反查别名;对 `import X as Y`(from_imports 里存
"Y:X")补一道 StubMergerStore.LookupModNameByAlias 专用反查,避免别名解析不到
→ 拿不到模块 sha1 → 无法前向声明而退回 Ptr(i8)。现场候选:GenTypes.vp 的
`import lib.core.Gen.Type.TypeSpec as TSpec`,字段 `Spec: TSpec.TypeSpec*`
(ve_i.Spec.PtrLevel 属性访问失败)。
2026-10-02 11:48:31 +08:00
TermiNexus 4958fd2c14 debug(attr): [DBG-ATTR] 泛化到任意属性访问失败,打印基座/结构体/字段数(用完删除)
Generator.FuncGenBase 已由前向声明修复;新报错 ve_i.Spec.PtrLevel 属同类症状
(某字段指针的 pointee 不是可查字段的结构体)。去掉 attr=="FuncGenBase" 守卫,
任何属性访问失败都打印 obj_ptr.Ty / struct 名 / 条目 FieldCount / base_class。
仅打印,不改行为。
2026-10-02 02:50:01 +08:00
TermiNexus 87e2356eea feat(type): rawptr[T]/t.CPtr[T] 下标形也走前向声明,并覆盖同文件前向引用
承接 c0d3231(X | t.CPtr 已改前向声明):
- t.CPtr[T] 分支:inner 解析为 None 时先尝试前向声明具名占位并返回
  Ptr(占位.Ty),保住类身份;无法确定类名/模块时才回 None 交多遍收敛。
  rawptr[T] 经 builtins typedef(rawptr = t.CPtr)兜底归一到本分支,故一并覆盖。
- _forward_declare_ptr_from_node 的 Name 分支补"本文件顶层类"回退:用
  StructGen.register_file_classes 登记的"{名字 → 本文件模块 SHA1}"前向声明,
  覆盖同文件前向引用 / 自我引用(如 JsonValue | t.CPtr 自引用字段)。

无编译验证。
2026-10-02 02:48:26 +08:00
TermiNexus c0d3231e02 fix(type): 未注册类指针改前向声明(具名不透明占位)+ 定义侧原位填充
对齐 TPC 语义(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充)。
此前 TPV 对"指向尚未注册类的指针"回退匿名 Ptr(i8),丢类身份且被冻结后
无法修复:现场 CallGenBase.Gen: Generator.Generator* 与 Generator 构成
循环依赖(Gen→Generator,Generator.CallGenBase→CallGenBase),Gen 先于
Generator 注册 ⇒ 冻结成 i8* ⇒ self.Gen.FuncGenBase 属性访问失败
(1.log 取证:Generator 条目本身正确 fcount=47,但 lookup_field 收到
struct_ty=<non-struct>)。

- GenTypes.StructEntry 新增 IsForwardDecl 标记(占位条目)。
- StructGenMixin.ensure_forward_class:按"类名 + 定义模块 SHA1"登记具名
  不透明结构体 {sha}.Class 占位(名称与定义侧 ClassGenDef 同口径,持久池
  分配),幂等;定义侧据此按名定位原位填充。
- TypeGenResolve._forward_declare_ptr_from_node(Attribute/Name 两形,经
  from_imports 与模块名表求 sha1,再 _class_true_def_sha1 校正)+ BinOp
  (X|t.CPtr) 未注册回退:优先返回 Ptr(占位.Ty);仅在无法确定类名/模块时
  才退回 Ptr(i8) 由 AnnotClassName 恢复。
- ClassGenDef:命中前向声明占位时不早退,把新构建的字段体**原位写入同一
  Ty 对象**(*existing.Ty = Struct(...),指针身份不变),登记条目沿用占位
  条目;declare_only 1/0 均适用。所有已发出的 Ptr(占位.Ty) 引用在填充后
  自动指向完整定义。

TPC 侧核对:TPC 本就是"具名占位 + set_body"模型(TypeAnnotationResolver
.CreateFromTypeName / BaseGen._get_or_create_struct / StructGen op 占位),
并有 fail-fast 拒绝类引用降级为 void**,无需改动。
2026-10-02 02:40:05 +08:00
TermiNexus d1c0f6754c debug(gen): 补 [DBG-ATTR] 探针——FuncGenBase 处 obj_ptr 类型与注解类名兜底(用完删除)
已知(1.log 取证):Generator 结构体登记正确(tbl[337] fcount=47 含 FuncGenBase),
真正问题是 `self.Gen` 字段类型退化成 i8*(lookup_field 收到 struct_ty=<non-struct>)。
本探针在报错点补打印:
- obj_ptr.Ty(确认是否为 i8*)
- base_class(_resolve_attr_field_class 取到的 Gen 字段注解类名)
- 用 base_class 走 lookup_field_by_class 找 FuncGenBase 的字段下标

用于判定"注解类名未记录"还是"按类名查询也失败"。仅打印,不改行为。
2026-10-02 02:31:09 +08:00
TermiNexus 01e95b51cf debug(gen): 一次性取证插桩——Generator 结构体登记/查找全链路(用完删除)
覆盖点(全部以 [DBG-GEN] 为前缀,仅打印不改行为):
- StructGenMixin.register_struct:IN(name/sha1/ty/StructCount)、DEDUP(命中条目)
- StructGenMixin.add_field:逐字段打印写入 Generator 条目的字段名与下标
- StructGenMixin.find_struct_by_module:IN / HIT / MISS
- StructGenMixin.lookup_field(field=FuncGenBase):struct_ty 名、按 Ty 指针命中条目的
  全字段名、以及结构体表内所有含 "Generator" 的条目
- VpiReader._inject_class_struct_declare:IN / EXISTING / RENUM-SKIP / EMPTY /
  FIELD-FAIL / REGISTERED
- ClassGenDef.translate_class_def:EXISTING / REGISTER / DONE

目的:一次重建即可判定 Generator.FuncGenBase 查不到是"命中空/旧条目""Ty 错配"
还是"字段名悬空",并看清是哪条登记路径产出的条目。
2026-10-02 02:18:55 +08:00
TermiNexus 1ad7abf33d refactor(TransPyV): 统一自举写法(novirtual / define / rawptr→* / 去续行反斜杠)
- @t.NoVTable class X → novirtual class X
- t.CDefine = N → define N
- rawptr[X]、X | t.CPtr → X*
- t.CPtr → rawptr(裸指针标记)
- t.Callable[...] → callable[...]
- 去掉 if/elif 条件的行续反斜杠(改单行写法)
- w32.win32file.X → win32file.X(已 import as)
- core/__init__.vp 去掉未使用的 import t, c / from stdint import *

涉及:CompilerFactory、IncludesScanner、Repl、core/__init__、
Gen/Call/{CallGenBase,CallGenCBuiltin,CallGenCall,CallGenDispatch,CallGenLeafKit}、
Gen/Class/ClassGenGeneric。
2026-10-02 02:16:37 +08:00
TermiNexus 7e82ed6940 chore(debug): 移除 [DBG-GEN] 一次性插桩(改走静态分析)
按用户要求撤掉 lookup_field 里的临时打印,恢复原函数体。
2026-10-02 02:14:54 +08:00
TermiNexus 1d1efc6288 debug(gen): 一次性插桩打印 Generator.FuncGenBase 字段查找上下文(用完删除)
AsyncGenMixin.vp:58 `self.Gen.FuncGenBase` 报「属性访问失败」,需确认
lookup_field 收到的 struct_ty 是谁、结构体表里 Generator 条目的 FieldCount/sha1,
以判断是字段缺失、Ty 身份不一致还是按到了空/错误条目。仅打印,不改行为。
2026-10-02 02:10:58 +08:00
TermiNexus f865986675 fix(stub): App 预注册注入依赖 vpi 时跳过函数/方法 declare,避免打断多遍收敛
承接上一提交(App 预注册期开始注入依赖 vpi)。注入的依赖 vpi 里,函数/方法
declare 的形参/返回注解可能引用**本遍尚未注册**的类(跨文件注册顺序:被依赖模块
的类可能排在更后,需靠下一遍收敛),resolve 落空会在 declare 注入里 fail-fast,
直接打断 Phase A-pre 的多遍收敛。现场:App 预注册 AssignGenMixin.vp 注入
Translator.vpi 时,InjectTracebackInstallCtor 形参 rawptr[Translator] 解析失败
(Translator 结构体因字段引用尚未注册的 GenTypes 类而本遍未登记)。

改动:VpiReader._ParseVpiIntoModuleImpl 新增 pre_skip_fn_declares(仅当
trans._pre_allow_dep_inject != 0 即 App 预注册时置 1),在「第一遍 B 方法 declare」
与「模块级函数 declare」两处跳过注入。预注册的唯一目的是注册 struct/enum/union
(第一遍 A)与包重导出别名(ImportFrom 段),两者均保留;方法/函数 declare 写在
预注册的瞬时模块里、随即随 mb.reset_to 丢弃,无跨遍作用。全量翻译
(_declare_only==0)不跳过,仍 fail-fast。
2026-10-02 01:47:18 +08:00
TermiNexus 981af07d5c fix(phase): App 预注册期注入依赖 vpi,修 list[ast.ClassDef*] 票名前缀分裂
Phase A-pre(App 预注册)此前不做导入处理、不注入依赖 .vpi,导致包重导出
类的字段注解在预注册期只能保留「包前缀」(1abcd…),并被冻结进结构体布局;
阶段 A 构造函数侧却能借包别名 entry 校正为真定义前缀(e70bd…),两处票名
分裂 ⇒「非基本类型指针不可隐式转换」(现场 ClassGenGeneric.vp:92)。

改动(只放开 App 预注册,不动 includes 预注册那条防依赖环自锁的约束):
- Translator 新增 _pre_allow_dep_inject 开关,并在导入处理门拆成两道:
  ①导入处理 + 自动容器可见性 + LoadDepVpiIntoModule —— 全量翻译与 App 预注册共用;
  ②模块级变量预注册 + 函数前向声明 —— 仍只在全量翻译做。
- BuildPipeline.TranslateFileGetTrans 新增 pre_allow_dep_inject 参数并写入 Translator。
- Phase2 App 预注册调用点传 1;includes 预注册保持默认 0。
2026-10-02 01:03:31 +08:00
TermiNexus 57f8c655c8 chore(debug): 移除 [DBG-P7] 一次性插桩(根因已定位)
定位结论见 Debugger/debug-nested-class-struct.md 的 P7:不是别名登记失败,而是
Phase A-pre(预注册 struct/enum/union,declare_only)解析类字段注解时,被 import
模块的 vpi 一概尚未注入 ⇒ 包重导出别名 entry 不存在 ⇒ 字段侧票名保留包前缀并被
冻结进结构体布局,与阶段 A 构造函数侧的真定义前缀分裂。

删除 96c1102 / 3b8a2b2 引入的全部临时打印(VpiReader.vp / TypeGenResolve.vp /
CallGenCtor.vp 三处),代码回到插桩前状态(-108/+0)。
2026-10-02 00:59:44 +08:00
TermiNexus acd6c9a615 docs(expr): 去掉指向已删除的 wiki/14-oo-refactor.md 的注释引用
该文档(对象化重构计划)已完成并从 wiki 删除,注释只保留「对象化 P3」的来源标记,
避免留下悬空文档引用。
2026-10-02 00:47:17 +08:00
TermiNexus 3b8a2b225f debug(stub): 补全 [DBG-P7] 插桩覆盖所有票名产生路径(定位后删除)
在 96c1102 基础上把"可能产出该票名"的路径一次打全,保证一次重建即可定死根因:

- TypeGenResolve._class_true_def_sha1:MISS 时额外 dump 结构体表里所有名字含
  ClassDef 的条目(下标/名字/归属 sha1/Ty 的 IR 名前缀)——直接看出别名 entry
  到底有没有、挂在哪个 sha1 上。
- TypeGenResolve.extract_class_name_from_annotation:AnnotClassName 通道算出的
  泛型票名(含 ClassDef 才打)。
- TypeGenResolve._resolve_generic_subscript_ty:注解/字段侧算出的泛型特化名
  gen_mangled(含 ClassDef 才打)。
- CallGenCtor._extract_type_args_from_slice:构造函数侧实参串的 raw/norm 两态。
- VpiReader._inject_class_struct_declare:进入 + 各出口
  (already / renum_skip / field_unresolved / zero_fields / registered)。
- VpiReader._inject_reexport_class_declare:调用现场与四个出口
  (already / no_src_entry / alias_reg_fail / alias_ok)。
2026-10-02 00:34:38 +08:00
TermiNexus 96c110242e debug(stub): 一次性插桩定位 ast.ClassDef 票名前缀分裂(定位后删除)
9994d2e 改判据后现象不变,需要在一次重建里同时看到三处事实,避免反复猜:
- TypeGenResolve._class_true_def_sha1:cls 含 "ClassDef" 时打印 HIT/MISS 与候选
  sha1、命中条目的 IR 名前缀(用 strstr 以便同时捕获 "ClassDef*" 形态);
- VpiReader._inject_reexport_class_declare:src/local 为 ClassDef 时打印调用现场
  的「源 entry 是否已注册 / 别名是否已存在」(src_entry=0 ⇒ 别名必然建不起来);
- VpiReader 的 ImportFrom 分支:判定为包 __init__ 时各打印一行(模块名、包前缀、
  子模块名与解析出的 sha1),确认包判定与子模块解析是否成立。

全部带 [DBG-P7] 前缀,便于 grep 与事后清除。
2026-10-02 00:32:05 +08:00
TermiNexus 9994d2e983 fix(stub): 包 __init__ 判定改按模块名,修 ast.ClassDef 票名前缀分裂
存量缺口:VpiReader 判定"本 stub 是否包 __init__"依赖 sha1→rel_path 反查
(strstr(rel_path, "__init__.vp"))。该反查在部分运行形态下取不到条目 ⇒ 判定恒
false ⇒ 包重导出类的别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类限定名在泛型实参
里保留包前缀(幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物恒 opaque),与构造
函数侧的真定义模块前缀(`list[e70bd9b4487d921d.ClassDef*]`)分裂成两个类型,报
「指针隐式转指针已禁止」(现场 ClassGenGeneric.vp:92 的 CastError)。

改法(不再依赖 rel_path 字段,均为模块名表的既有事实):
- StubMergerStore 新增 LookupModNameBySha1:按 SHA1 反查模块名(只读指针)。
- VpiReader 的 ImportFrom 分支:取 level>0 的兄弟模块 S,用其模块名去掉末段得到包
  前缀 P;又因 Utils.PathToModuleName 把 pkg/__init__.vp 记成 "pkg",故
  「本 stub 的模块名 == P」即证明本 stub 就是包 P 的 __init__ ⇒ is_pkg_init_rt=1,
  同时直接用 P 拼出子模块全名(不再从 rel_path 反推目录名)。
- 模块名证据不足时仍回退原 rel_path 路线,行为不变。
2026-10-02 00:07:23 +08:00
TermiNexus b91dfef777 feat(stub): vpi 注入侧递归登记嵌套类为扁平名 {外类}_{内类}
VpiReader 此前只登记顶层 ClassDef,跨模块引用 `外类.内类` 拿不到类型
(TPC 侧 stub.ll 已递归声明,双端不对齐)。现按定义端
ClassGenDef._translate_nested_classdefs 的命名口径补上:

- _inject_class_struct_declare 增加 name_prefix 形参,非空时算出扁平名
  real_name;REPL 分支、已注册检查、命名结构体类型名、register_struct、
  add_visible_struct 一律改用 real_name。
- 新增 _inject_nested_class_declares:遍历类体内缩进 ClassDef 子节点,
  以 real_name 为前缀递归调用(多层嵌套自然展开为 A_B_C)。
- 嵌套登记后再次调用 mark_module_class 复原"正在定义类"窗口:否则内层
  类名会顶掉 CurDefClassName,使外层字段注解的规则 5.5 自引用证据判错、
  且 `外类.内类` 的守卫①失效。
2026-10-01 23:39:11 +08:00
TermiNexus c3f554f1f5 feat(struct): 嵌套 class/struct 发射为独立结构体 {外类}_{内类}
用户 2026-10-01 定:struct 内允许叠加 struct(与 class 内写 class 同口径)。
此前类体里的嵌套 ClassDef 被**静默丢弃**(字段循环只认 AnnAssign、
_translate_oop_methods 只认 FunctionDef),现按 TPC 侧 union/renum 的
`{ClassName}_{Nested}` 命名先例发射为独立结构体,与 TPC 提交 5eef845e 同源。

- ClassGenDef.vp:新增 _translate_nested_classdefs —— 在 enum/union/renum/泛型/
  异常分派之后、existing 检查之前(所有提前 return 之前,故 declare_only=1/0 都递归)
  遍历类体 ClassDef 子节点,把子节点 name 临时改写为扁平名后递归
  translate_class_def(本模块所有名字派生点都读 cd.name ⇒ 改名即全链路一致)。
  两个护栏:扁平名分配在 PermPool(跨 Phase 存活);递归后还原 CurDefModuleSha1/
  CurDefClassName(内层 mark_module_class 会覆盖,否则外层字段解析误判当前定义类)。
- TypeGenResolve.vp:新增 _resolve_nested_class_type 并在 Attribute 分支的
  "base 不是模块" 出口前接上 —— 限定名 `外类.内类` 按扁平名查结构体表即可命中
  (裸名 `外类_内类` 走既有 Name 分支天然命中)。判据两道:base 是已知类
  (find_struct 命中,或正处于该类的定义窗口内);扁平名条目存在且 Ty 非空。

引用两种写法都可用:`外类.内类`、`外类_内类`;多层嵌套递归为 A_B_C。
2026-10-01 23:30:15 +08:00
TermiNexus 3ccc37ca3f fix(type): 泛型实参归一先剥尾随星号,消除包前缀票名分裂
_norm_type_arg_name 规则 4 用 strchr('.') 切出 attr 时把尾随 '*' 一起带进了类名
('ast.ClassDef*' → attr='ClassDef*'),于是 _class_true_def_sha1 按名查不到
entry、真定义模块校正失效,留下包前缀:

  字段侧 ClassGenBase.vp:123 → list[1abcd5277fa442de.ClassDef*](无定义的幽灵名,
                              全部产物里只有 = type opaque,从无 = type {...})
  构造侧 ClassGenGeneric.vp:92 → list[e70bd9b4487d921d.ClassDef*]
  ⇒ ClassGenGeneric.vp:92 报「指针隐式转指针已禁止」。

TPC 侧名称域(SymbolUtils.ResolveTypeArgStr 的 BinOp 分支)本就是「先解析基类型 X,
再 AddPointerLayers 追加 '*'」→ {真定义sha}.X*,TPV 与之对齐。

新增规则 1.6:剥掉全部尾随 '*' → 递归归一基名 → 回贴同样多的 '*'。
i8** / int* / T* / 已带 SHA1 前缀的文本逐字不变。
2026-10-01 22:40:54 +08:00
TermiNexus 02b9429363 fix(type): void* 归并分支的 match 必须守 None,修 ASan 空指针崩溃
BinOp 的 CPtr 分支里,上一笔为 void* 归并新增的 match other_ty 未判空。
other_ty = resolve_annotation_type(另一边) 在 Phase 1a prescan 时**可以是 None**
(本分支下方既有代码就写着 if other_ty is None: ... 回退 Ptr(i8),正说明 None 是
期望值);对 None 做 REnum case 匹配会按其 tag 解引用空指针。

现场:TPV(ASan) 自举 Phase1 报 access-violation on address 0x0(READ),
栈 _prescan_funcdef_signatures → resolve_annotation_type;触发源是用户 WIP 里
新增的 X* 返回注解(parser 把 X* 展开成 BinOp X | t.CPtr,正好走本分支)。

改为 if other_ty is not None: 包住,None 继续走下方既有回退路径。
2026-10-01 22:07:41 +08:00
TermiNexus be14fa5f94 fix(ctor): REnum 变体构造识别取错限定名,导致构造物化豁免失效
_ctor_call_agg_ty 从被调方沿属性链一路走到链根裸名,三段式
llvmlite.LLVMType.Struct(...) 于是被判成限定名 'llvmlite'(非 REnum 类)⇒
返回 None ⇒ ClassGenDef.vp:128 的 *existing.Ty = LLVMType.Struct(...) 被
「指针 → 非基本类型值」禁令拦下。改为取「变体名左边那一段」:两段式取裸名
(EnumType.Variant)、三段式取属性名(llvmlite.LLVMType.Struct → LLVMType)。
2026-10-01 21:44:55 +08:00
TermiNexus 2f6db41b11 fix(void): 形参与字段注解禁止 void,改为编译期 fail-fast
void 只允许用于 void* 与 typedef 不透明结构体别名(用户 2026-10-01 定)。
此前 def f(p: void) / class C: x: void 不走 translate_annassign,会按 LLVM void
直接建形参/字段槽,生成非法 IR 拖到 llc 阶段才暴露。现补 6 处形参落点
(FuncGenDef 前向声明/外部声明/函数体/alloca 各一、ClassGenVTable 签名/alloca 各一)
与 1 处字段落点(ClassGenDef),统一编译期 TypeError。
void* 已在上游归并为 i8*,不受影响。
2026-10-01 21:43:11 +08:00
TermiNexus 59865b2a3a feat(TPV): void 值操作 fail-fast(声明 void 变量 / 把 void 值当值用一律报 TypeError)
承上一笔(裸名 void = t.CVoid 别名,仅 `void*` 与 `typedef 不透明结构体 = void` 可用):

- ExprGenBase.coerce_to_type 顶部:void 值被 coerce 到**非 void** 目标(赋值 / 传参 /
  返回 / 二元运算对齐)直接 fatal_error(TypeError),不再按 LLVM void 硬拼出非法 IR
  ("load void" / "bitcast ... to void");void → void 属无值传递,放行。
- AssignGenMixin.translate_annassign:注解解析结果为 void 时禁止声明该变量
  (否则会按 LLVM void 建槽)。
2026-10-01 21:39:48 +08:00
TermiNexus e0bcf4bcef feat(TPV): 裸名 void 作为 t.CVoid 的别名(void* = i8*)
用户 2026-10-01 需求:`void` 即 t.CVoid 的别名,只允许两种用法——`a: void*` 与
`typedef 不透明结构体 = void`(后者 `X*` 亦为 i8*)。

- TypeGenResolve 内置名表:新增 "void"(与 "CVoid" 同义 → llvmlite.Void)
- TypeGenResolve._is_cvoid_marker:裸名 "void" 与 "CVoid" 同判
- TypeGenResolve BinOp 联合合并:裸名 "void" 直接置 other_is_cvoid(无需 import t);
  解析结果本身是 Void 时亦按 void* = i8* 归并,覆盖 `typedef X = void` 的 `X*`
  (否则会拼出非法的 Ptr(void))
- VpiReader:注解文本里的裸 "void" 段与 "CVoid" 同判(has_cvoid → i8*);
  has_cptr 分支对解析结果为 Void 的 first_ty 同样归并为 i8*(跨模块侧同口径)

TPC 侧无需改动:HandlesBase 的内置名表已有 'void': (t.CVoid, 0),
`void*` 经既有 CVoid+CPtr 合并即 i8*。
2026-10-01 21:37:40 +08:00
TermiNexus 21d5cd1cd1 fix(ExprGenBase): REnum 变体构造纳入「构造物化」豁免;错误报文只报问题本身
1) _ctor_call_agg_ty 增加 REnum 变体构造识别:`EnumType.Variant(...)` 的变体名不是结构体名
   (find_struct 必 miss)⇒ 取属性链最右裸名,若判为基础枚举则返回该枚举的聚合类型。
   否则 `*槽 = <boxed 变体>` 会被「指针 → 非基本类型值」禁令拦下——
   现场 ClassGenDef.vp:128 `*existing.Ty = llvmlite.LLVMType.Struct(...)`。
2) 按用户要求简化 Error 输出:CastError 只报问题(原因 + 源/目标类型),去掉
   ";出错表达式: <形态>(行/列)源码: ..." 后缀(文件与行号已由 TypeGenErr 的 Traceback
   帧给出,属重复展示),并删除随之失去调用点的 _cast_fail_where / _source_snippet。
2026-10-01 21:17:49 +08:00
TermiNexus a714ec9d37 fix(VpiReader): 返回/形参注解切分只在顶层 | 处断段(括号感知)
_resolve_vpi_ret_annot_str 原按裸 `|` 切段,`list[ast.AST | t.CPtr] | t.CPtr` 会被切成
`list[ast.AST` / `t.CPtr]` / `t.CPtr`;首段再经 strrchr('.') 落到 `AST` ⇒ 定义侧类型解析成
`AST*`(元素指针),而正确答案是 list 专化结构体的指针。现场:跨模块调用 coerce 时报
"源类型 list[AST*]* → 目标类型 AST*"(CallGenLib._coerce_args_to_def_params 的 cargs 形参)。

现切分按 `[`/`]` 深度,只在顶层 `|` 处断段(与 _cache_vpi_param_annots_text 的顶层逗号
切分同口径)。改后该段解析不出 ⇒ 落 i8* 通配,与 `list[str] | t.CPtr` 等同形注解的既有
行为一致;配合 CallGen 侧的 bitcast 对齐(见上一笔)即可安全使用。
2026-10-01 20:45:21 +08:00
TermiNexus 8e3811c490 fix(CallGen): 跨模块调用的 declare 形参与实参统一以定义侧为准
问题一(declare 形参被首个调用点冻结):CallGenBase._ensure_method_declare 新建跨模块方法
declare 时形参类型取"首个调用点的实参类型",而这条 declare 被同模块所有调用点共用 ⇒ 后续
调用传入另一种具体指针类型时,被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"。
现场:VarGenMixin.repl_ensure_entry 的 node: ast.AST*,先被 translate_annassign 以 ast.AnnAssign*
建 declare,后 translate_assign 传 ast.AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*"。
⇒ 指针形参与整数同口径,一律采用定义侧(.vpi 注解段)类型;解析不出指针时保留实参类型。

问题二(实参未对齐 declare):declare 改用定义侧类型后实参也必须对齐,否则首个调用点
(由它建立 declare、不走 coerce_to_type)原样传具体指针会与 declare 类型不一致(llc 报
argument type mismatch)。⇒ CallGenLib._coerce_arg_value_to_ty 的同层指针分支改为"类型内容
不一致即 bitcast 到定义侧类型"(原判据只覆盖一侧为 i8*、或模板注解来源两种情形)。
2026-10-01 20:29:15 +08:00
TermiNexus 93e983f74e fix(TPV): 删除残留的"| t.CPtr struct 引用语义吸收"(T* 被降一层的真正根因)
现场(0a81e67 之后仍复现):Test/SlotsBugTest 编译 includes/_list.vp:54
  TypeError: 下标赋值的左值元素是对象 'd1ed020479454541.AST'(48 字节),右值却是指向
  'd1ed020479454541.AST' 的指针
  elem_ptr[0] = item

关键事实:`T*` **不是** `rawptr[T]` 下标形——parser 把尾随 '*' 解析成
BinOp(`T | t.CPtr`)(includes/ast/parser.vp:1528-1556 `_make_ptr_type`、:2700-2707)。
所以它走 resolve_annotation_type 的 **BinOp 联合分支**,0a81e67 只修了
rawptr[T] / t.CPtr[T] 两条**下标**分支,覆盖不到这里。

链路(逐段在源码上核对过):
1. 泛型实参 `rawptr[AST]` 经 _norm_type_arg_name 归一为文本 `{sha}.AST*`;
2. `T` 走 Name 分支的泛型替换 → _parse_type_string 的 `T*` 反解析(:995-1018,
   按尾随 '*' 层数包 rawptr[...])⇒ other_ty = Ptr(AST),本身正确;
3. BinOp 分支尾部的"struct 引用语义吸收":other_ty 已是 Ptr(已注册 struct)
   ⇒ 直接 `return other_ty`,**吞掉 CPtr 这一层** ⇒ `T*` 变成 AST*(应 AST**)。

改动:
- TypeGenResolve.vp BinOp 分支:删除该吸收,恒 `return Ptr(other_ty)`
  (与 rawptr[T] / t.CPtr[T] 下标形、_parse_type_string 的 `T*` 反解析三处同口径)。
- VpiReader.vp:1614-1621 注入侧的同源吸收一并删除(维持"定义侧 / 注入侧 / 调用侧
  三处指针层数口径同步")。
- 两处口径注释同步改为"`| t.CPtr` 恒追加恰好 1 层,无例外"。

安全性(该吸收已是死补偿):它是"裸类名自动升指针"时代的产物——当时裸类名解析成
Ptr(struct),所以要吸收;自 2026-10-01 起裸类名恒为原型 struct,`X | t.CPtr` 由
Ptr(struct) 天然得到 X*。json/__init__.vp:90 的引号注解 `'JsonValue' | t.CPtr`
现在 other_ty 就是 struct ⇒ 吸收本不触发,删除后行为不变(JsonTest 已实测翻译通过并
进入 llc 阶段)。`list[int]`(i32*)、`list[ast.AST]`(struct*)、裸类名 `X | t.CPtr`
(struct*)均不受影响。

待验证:重编译 TransPyV(自举两遍)→ SlotsBugTest / JsonTest / WebTest。
2026-10-01 17:46:53 +08:00
TermiNexus 0a81e67e89 fix(TPV/TypeGenResolve): rawptr[T]/t.CPtr[T] 恒追加 1 层,删除"裸类引用吸收"旧补偿
现场:Test/SlotsBugTest 编译 includes/_list.vp:54 报
  TypeError: 下标赋值的左值元素是对象 'd1ed020479454541.AST'(48 字节),右值却是指向
  'd1ed020479454541.AST' 的指针
  源码: elem_ptr[0] = item

用户判定正确:`elem_ptr: T*` 与 `item: T` 同为 T,赋值本身合法;错在编译器把 `T*`
少算了一层——T 实例化为 rawptr[AST](= AST*)时,`T*` 应为 AST**。

根因:rawptr[T] / t.CPtr[T] 分支里有"切片是裸类引用(解析结果 Ptr(已注册 struct))
就吸收这 1 层"的旧补偿(_ptr_subscript_absorbs_class)。那是"裸类名自动升指针"时代的
产物——当时裸类名解析成 Ptr(struct),于是 rawptr[X] 会多包一层,需要吸收;
自 875a9be 起裸类名恒为**原型(struct 值)**,Ptr(struct) 天然就是 X*,补偿已无对象;
而**泛型形参实例化为指针类型**时(T = rawptr[AST]),切片的解析结果同样是
Ptr(已注册 struct) ⇒ 被误吸收:`elem_ptr: T*` 变成 AST*(应为 AST**)⇒ 左值元素被判成
48 字节的 AST 对象、右值是指针 ⇒ fail-fast。

改动:
- 删除 _ptr_subscript_absorbs_class,以及 rawptr[T] / t.CPtr[T] 两处调用,改为**恒追加
  1 层**:`rawptr[T]` ≡ `t.CPtr[T]` ≡ `T*` ≡ `T | t.CPtr` 四种写法同型(用户口径),
  不再有"裸类引用吸收"例外。
- 同步修正四处引用该函数的口径注释(:407 / :433 / :567 / :1000)。
- **保留** BinOp `| t.CPtr` 分支内的同名吸收:它只对**非裸名**侧可达(Constant/Attribute;
  裸名侧在更早的"Name 必须已注册 struct"守卫处就改走其它分支),故
  includes/json/__init__.vp 的引号注解 `'JsonValue' | t.CPtr` 行为不变。
  仓库内也不存在 `rawptr['X']` / `CPtr['X']` 切片写法(仅注释提及),删除吸收无副作用。

已知遗留(本次未改,待你定夺):`T | t.CPtr` 这一写法在 T 实例化为指针类型或类类型时
仍会退化——Name 侧解析出的 Ptr(struct) 被丢弃,随后按名字 'T' 查 struct 失败 ⇒
返回 Ptr(i8)(位置:本文件 BinOp 分支的 keep_other_ty 判定,约 :3166-3194)。
当前仓库没有使用该写法的现场,故未一并改动。

待验证:重编译 TransPyV(自举两遍)→ SlotsBugTest / JsonTest / NoVTableCrossMixinTest。
2026-10-01 16:51:31 +08:00
TermiNexus f748cae562 fix(TPV/CallGenCall): 判据化重加兄弟 Mixin 方法解析(修 NoVTableCrossMixinTest 未定义符号)
现场:Test/NoVTableCrossMixinTest 链接期报
  ld.lld: error: undefined symbol: 1e19037e6cfdce36.BaseB.helper
    >>> referenced by ...(1e19037e6cfdce36.BaseB.caller)
  ld.lld: error: undefined symbol: 1e19037e6cfdce36.MixinC2.mid2
    >>> referenced by ...(1e19037e6cfdce36.MixinC2.outer2)

这是 455c760 删除"兄弟 Mixin 兜底"的必然结果:NoVTable 多继承下,Mixin 内的
self.<兄弟方法>()(Combined(BaseA, BaseB) 中 BaseB.caller 调 BaseA.helper)本类
MRO 查不到、跨模块 stub 也没有 ⇒ 调用名退化成 {sha1}.BaseB.helper(该符号不存在)。

改动(不是恢复原兜底,而是**判据化解析**,不做"同名即绑"的猜测):
- CallGenCall._translate_method_call:在自身 MRO 查找失败后新增兄弟 Mixin 解析,
  两条**正向判据**缺一不可:
    ① 接收类必须是某个已注册类的**直接父类**(它确实被组合使用,是真 Mixin)——
       挡掉 includes/w32/fileio.vp 的 FileW.__init__ → self.seek() 误绑 File.seek
       (FileW 无人继承,不是任何人的 Mixin);
    ② 候选类与接收类必须同处某个已注册**组合类的 MRO**(真·兄弟)。
  命中后 found_func / mc_anc 都指向定义类 ⇒ 调用名用定义类 SHA1 前缀(符号存在),
  this 由既有的 mc_anc this-adjust 分支按组合布局前移(该分支未改动)。
  只取**本模块内已有函数**的候选(跨模块定义类需经其 text.ll 取签名,本轮不启用);
  接收类为跨模块类时不启用(沿用原 sib_recv_local 守卫,避免误绑跨模块同名方法)。
- 新增两个判据辅助方法:_recv_is_mixin_base / _mixin_sibling_same_composite。

待验证:重编译 TransPyV(自举两遍)→ NoVTableCrossMixinTest / WebTest / FileIoTest。
2026-10-01 15:00:55 +08:00
TermiNexus dc4e06d88b fix(TPV): 本文件自己声明的 extern 裸名优先于依赖 vpi 注入的同名声明
现场:Test/WebTest 编译 includes/socket.vp:236 报
  CastError: 指针隐式转指针已禁止:源 %"56d0ee99ecf0a0b5.SocketAddr"* →
  目标 %"99c88d4cbd933ac6.WSASocketAddr"*
  源码: result = connect(self.fd, c.Addr(addr), 16)

机制(**链接期无冲突**——两边都不是 export,@connect 是同一个 C 符号,最终只有
ws2_32 里一个定义;问题在"编译 socket.vp 时的同一个模块内"):
1. extern/state/export 声明的符号**不带 SHA1 前缀**(FuncGenDef.vp:172-173
   `mangled_name = fd.name`)⇒ socket.vp 与 w32.winsock2 都产出裸名 @connect
   (前者形参 SocketAddr|t.CPtr,后者 WSASocketAddr|t.CPtr)。
2. Translator 的 prologue 先注入依赖 w32.winsock2 的 vpi
   (StubMergerBuild.LoadDepVpiIntoModule → VpiReader.ParseVpiIntoModule →
   _inject_function_declare:create_declare + module_add_function),当前模块里
   先出现了 winsock2 版的 @connect。
3. 随后 forward_declare_functions 处理 socket.vp 自己的 `extern def connect` 时,
   find_func_in_module 命中已存在 ⇒ `continue`(FuncGenDef.vp:178-182)
   ⇒ **本文件自己的声明被静默丢弃**,调用点改按 winsock2 的形参 coerce
   (报错里的目标类型 WSASocketAddr* 正是 winsock2 版的指纹)。
   (同文件 send/recv/socket/listen 亦被顶掉,只是形参是 i8*/u64,不报错——
   即 socket.vp 的 POSIX 分支本来就在调用 winsock2 的声明。)

改动(注入期跳过"本文件已声明的裸名",让本文件自己的声明优先):
- StructGenMixin:新增 FileFuncNames 表 + register_file_funcs(pool, tree) /
  is_file_func(name),与既有的 FileClassNames / register_file_classes /
  is_file_class 同规矩(键与表同池、每文件随 reset_visible_structs 重建)。
- GenApi:新增 register_file_funcs / is_file_func 委托包装。
- Translator._translate_module_level:在 register_file_classes 之后调用
  register_file_funcs(三种模式共用、早于依赖 vpi 注入)。
- VpiReader._inject_function_declare:算出 mangled name 后,若 `use_bare_name != 0`
  且 `is_file_func(fd.name) != 0` 直接返回(不注入)。判据只用**裸名**:带 SHA1
  前缀的依赖函数名与本文件同名声明不会互相顶替,避免误跳过跨模块调用。

待验证:重编译 TransPyV(自举两遍)→ WebTest / JsonTest / FileIoTest。
2026-10-01 14:46:47 +08:00
TermiNexus d979cc8c1f fix(TPV/ExprGenName): 结构体值局部变量在使用处要求同型结构体值时 load 出值
现场:Test/WebTest 编译 includes/socket.vp:121 报
  CastError: 非基本类型的值/指针隐式转换已禁止(构造物化除外):
  不能把指针类型隐式转为非基本类型值(Foo* → Foo)
  源类型 %"56d0ee99ecf0a0b5.SocketAddr"* → 目标值类型 %"56d0ee99ecf0a0b5.SocketAddr"

现场代码:
  class SocketAddr:  family: u16 / port: u16 / addr: u32 / zero: u64   # 纯结构体,无 __new__、无方法
  def _MakeAddr(host: str, port: INT) -> SocketAddr:
      addr: SocketAddr                     # 结构体值局部变量
      string.memset(c.Addr(addr), 0, 16)
      ...
      return addr                          # ← 第 121 行报错

根因:ExprGenName.translate_name_value 对"结构体值局部变量"的右值**恒返回 alloca 地址**
(原实现,注释解释为"对象即指针"语义,避免 ptrtoint %Struct 非法指令);而 _MakeAddr
的返回类型经 resolve_annotation_type 解析为**结构体值**。translate_return 会把函数返回
类型作为 target_ty 下传给 translate_value(StmtGenCifMixin.vp:557-558),于是 `addr`
求值出 %SocketAddr* → coerce_to_type 的"指针 → 非基本类型值"分支
(ExprGenBase.vp:954-970)只对"构造物化"豁免 → 报错。
标量局部变量无此问题(同函数内非 Struct 分支会 build_load 出值),属结构体值局部变量
独有的表示差异。

改动:ExprGenName.translate_name_value 的 Struct 分支增加 target_ty 判据——使用处
**明确要求同一非基本类型(非指针)**时(_same_agg_ty 判据:与"构造物化豁免"同一类型
匹配真源 AssignGen._stg_ty_equal),像标量一样 build_load 出值;否则维持既有"返回地址"。
- target_ty 为指针时不命中(裸类形参 lowering 为 Ptr(struct),见 FuncGenDef)⇒
  Test/StructTest 的 point_sum(p) 这类实参传址行为不变。
- 字段访问基座(ExprGenAttr.vp:743-744,直接 lookup_var 取地址)、c.Addr 取址
  (CallGenCBuiltin.vp:293)、方法接收者均不经本函数取地址,不受影响。
- 同时修正该处已失真的注释(原文举例 `x: Cls | t.CPtr` 实际解析为 Ptr(struct),
  并非 Struct 值局部变量)。

待验证:重编译 TransPyV(自举两遍)→ WebTest / StructTest / ClassArrayTest / JsonTest。
2026-10-01 14:26:18 +08:00
TermiNexus 25c1c9875b fix(TPV/VpiReader): 阻断 vpi 自注入(循环 import)+ __init__ 声明端与定义端同取 void
现场:Test/JsonTest 编译 includes/json/__parser.vp 后 llc 报
  06cb4f5b8fd08841.ll:259:7: error: value doesn't match function result type 'i32'
    ret void
(该函数是 define i32 @"06cb4f5b8fd08841.ParseState.__init__",函数体末尾却是 ret void)

根因两层,都在 vpi 注入侧:
1) 自注入:__parser.vp 与 json/__init__.vp 循环 import(__parser 顶部
   `from . import (JsonValue, ...)`,__init__.vp 底部 `from .__parser import parse`)。
   __parser 编译时,其**自身** vpi(06cb4f5b8fd08841.vpi 里有
   `class ParseState: def __init__(self, src: str, pool: rawptr[...]): pass`)经依赖链
   (deps: t c stdint memhub string json → json 的依赖含 json.__parser)被反向注入
   ⇒ 模块 IR 里先出现 `declare i32 @"06cb....ParseState.__init__"`(IsDeclared=1)。
   该方法随后走 _translate_method 的"复用前向声明"路径,函数体按**定义端**语义发射
   (__init__ → 末尾 ret void),而函数对象自身的 RetTy 仍是注入时的 i32。
2) 类型口径分裂:VpiReader._inject_method_declare 对"方法无返回注解"一律退 i32 默认,
   而定义端 ClassGenVTable._translate_method 对 __init__ 恒 Void ⇒ declare i32 /
   define void。此前只在跨模块出现(不同 .obj,链接器不校验类型,静默 ABI 不一致);
   自注入把两者塞进同一个模块,才被 llc 拦下。

改动:
- VpiReader._inject_method_declare / _inject_function_declare:新增自注入守卫——
  仅全量翻译阶段(trans._declare_only == 0)且 src_sha1 == trans.ModuleSha1 时直接返回。
  本模块自己的 define 由 ClassGenVTable._translate_oop_methods / FuncGenDef 负责发射,
  不需要也不允许经依赖 vpi 反向注入(注入侧类型可能与定义端不一致)。
- VpiReader._inject_method_declare:__init__ 无返回注解 → Void(与定义端一致),
  消除 declare/define 返回类型分裂。__init__ 的返回值在三处调用点
  (CallGenCtor.vp:227 / :589 / :1275)均未被使用,改 void 不影响调用方。

待验证:重编译 TransPyV(自举两遍)→ JsonTest / FileIoTest。
2026-10-01 14:11:45 +08:00
TermiNexus 2bfcb6ef6e fix(TPV): 修 455c760 遗漏——args_node 必须声明在 decl_reuse 分支之外
现场:Test/FakeDuckTest 编译 includes/viperio.vp 第 12 行
  self.data = data   →   NameError: 未定义名称 'data'
(Buf.__init__ 的第一个形参名在方法体里找不到。)

原因:455c760 把整段"参数创建块"置于 if decl_reuse == 0: 之下时,把
args_node: ast.Arguments* = fd.args 一并圈了进去;但函数体段的
"为其他参数创建 alloca 并 store"(ClassGenVTable.vp:904 起)仍要遍历
args_node。复用前向声明(decl_reuse==1,现已是常规路径)时 args_node
未定义 → 形参不进符号表 → 方法体引用形参名即报 NameError。

修法:把 args_node 的声明提到 decl_reuse 分支之前。该块内其余变量
(md_defaults/md_default_count/md_param_count/md_args_node/md_ags/
md_alist/md_dlist/max_funcs/first_pname/first_pname_buf/fd_ags/
fd_first_arg/ags/alist/an/start_idx/ai/arg/param_ty/resolved/pname/
i1_ty_si/i1_ptr_ty_si)已逐一核对,全部只在块内使用,无需外提。

待验证:重编译 TransPyV(自举两遍)→ FileIoTest / FakeDuckTest。
2026-10-01 13:57:04 +08:00
TermiNexus 455c7608b1 fix(TPV): 方法先声明后定义(消除方法查找的源顺序依赖),删除兄弟 Mixin 兜底
现场:Test/FileIoTest 编译 includes/w32/fileio.vp 报
  CastError: 源 %"b0721da228be3d55.FileW"* → 目标 %"b0721da228be3d55.File"*
  (指针隐式转指针已禁止:被指类型是非基本类型)

根因(有旧 IR 佐证,非本轮改动引起):FileW.__init__(源 304 行)里的
self.seek(0, SEEK_END) 被解析成 File.seek,而不是 FileW.seek:
  1) _translate_method 只在方法体翻译时才 create_function,于是方法查找
     (find_func_in_module)依赖方法在类体内的书写顺序:FileW.seek 在源 403 行,
     晚于 __init__ → 查不到 → None;
  2) MRO 无父类 → None;
  3) 兄弟 Mixin 兜底随即遍历全部已注册 struct,取"本模块存在同名方法"的
     第一个候选类 → 命中 File → 绑成 File.seek,self 再被 this-adjust
     bitcast 成 File*。
旧 IR(TransPyV\.tpv_build\temp\b0\b0721da228be3d55.text.ll,mtime 9/27,
早于本轮全部改动)里就是:
  %97 = bitcast %"...FileW"* %96 to %"...File"*
  %98 = call i32 @"...File.seek"(%"...File"* %97, i32 0, i32 2)
即靠一层隐式跨类 bitcast 静默通过;指针规则收紧后以 CastError 报出。
(File.__init__ 的 self.seek 同样查不到,只是兜底恰好扫到 File.seek 而"碰巧正确"。)

改动:
1. ClassGenVTable._translate_oop_methods:翻译方法体之前,先对本类**全部方法**
   跑一遍 sig_only=1 建前向声明(declare + 参数 + 函数表登记)——与
   FuncGenDef.forward_declare_functions 对模块级函数做的事完全同构,
   使方法查找不再依赖类体内的书写顺序。
2. ClassGenVTable._translate_method 新增 sig_only 形参:
   · 签名部分改用 create_declare(IsDeclared=1),已存在同名前向声明则复用;
   · 发射函数体前把 IsDeclared 清零转 define(对齐 FuncGenDef.vp:544-545),
     否则打印端按 IsDeclared 输出 declare、函数体整体丢失;
   · 复用声明时跳过重复的 add_param 与 add_func_to_table;
   · 命中同名函数且 IsDeclared==0(函数体已发射)时直接返回,杜绝重复发射
     (llvmlite.create_function 不做同名去重,旧写法会造 duplicate symbol)。
3. CallGenCall._translate_method_call:按"不兜底、fail-fast"决定,整块删除
   兄弟 Mixin 兜底(遍历 struct 表猜候选类)及其死分支
   (mixin_hit / mixin_ret_ty / mixin_anc_name / mixin_anc_sha1 的声明与消费点)。
   方法未命中即为真未定义,交下方 text.ll / vpi / 本模块豁免路径
   (CallGenCall.vp:962 起的 is_local_mod 分支,从已声明的本模块函数直接取
   返回类型)处理,不再猜候选类。
4. 同步修正 ClassGenVTable 顶部 _translate_method 的注释块与函数文档串。

TPC 侧无需同步:TPC 的声明阶段把类成员全部登记进 SymbolTable,本就不存在
"方法查找依赖源顺序",也没有这段兄弟 Mixin 扫描(HandlesExprCall.py:3877-3914
的兜底带"调用类自身未声明该方法"守卫,与本次删除的 TPV 版不同)。

待验证(不自行执行编译命令):重编译 TransPyV(自举需编两遍),再跑
FileIoTest / NoVTableCrossMixinTest / DictTest / 泛型相关测试。
2026-10-01 13:45:10 +08:00
TermiNexus 875a9beb08 refactor(TypeGenResolve): 裸类名恒为原型(struct 本身),删除 5 处 HasNew 自动升指针
口径(用户 2026-10-01 定):**裸类名注解 = 原型(struct 本身,类型/布局语义)**,
堆对象引用必须显式写 `T*` / `T | t.CPtr` / `rawptr[T]`。旧行为"有 __new__ 的类
(堆对象)裸名 ⇒ Ptr(struct)"是为兼容 `d: dict = dict(bd)` 这类"裸名 + 构造"写法
而设,代价是同一类型两种写法分裂:

  includes/json/__init__.vp:79  `def __getitem__(...) -> 'JsonValue' | t.CPtr`
  左边(引号字符串注解)先被解析成 Ptr(struct),联合分支再包一层 ⇒ JsonValue**
  而 `cur: JsonValue*` 是 JsonValue* ⇒ :90 `return cur` 报
  「指针层数不同(源 1 级 → 目标 2 级)」(660427f 已在联合分支做过吸收,
  本次从源头统一:裸名不再自动升指针,吸收那条规则对裸名侧自然不再触发)。

改动(resolve_annotation_type 的裸名/限定名分支,共 5 处,全部去掉 HasNew 分支):
1. `{16hex}.Class` 已归一前缀名分支(原 :2768-2770)
2. 本模块自有类分支(原 :2806-2814,删掉那段"有 __new__ ⇒ Ptr"的注释与分支)
3. from-import 归属分支(原 :2832-2834)
4. 可见结构体裸名分支(原 :2842-2844)
5. `mod.Class` 限定名分支(原 :3040-3044)

影响面盘点(静态扫描 474 个 .vp):本文件内"裸堆类名注解"16 处、跨文件候选 63 处
(含同名跨模块误报),典型:
  includes/hashtable.vp:45   ht: HashTable = HashTable(mb)
  includes/_dict.vp:29       d: dict = dict(mb)
  Test/BuiltinDecoratorTest/App/main.vp:33/36/68/98/137/180/184/194/199/234/241/248
  TransPyV/Test/App/new_test.vp:81/93/105
  Test/{ClassArrayTest,StructTest,ClosureTest2,StringIterTest}/App/*.vp 若干
这些站点需按新口径显式补 `*`(或改用 `T | t.CPtr`);真实集合以编译器 fail-fast
报错为准(同名跨模块无法静态判定归属)。

TPC 侧核查结论:**无需改动,TPC 本就同口径**。证据:
1. 全仓 `HasNew|has_new` 仅 8 处,全在**构造路径**——HandlesAnnAssign.py:401
   (`list[T] = [...]` 字面量构造)、HandlesExprCall.py:5105/5161/5170/5198/5235/5263
   (`ClassName(...)` 构造,`_HandleClassNewLlvm`);**注解解析链上一处都没有**。
2. 规范注解解析 `TypeAnnotationResolver._from_node_name`(裸 Name)、
   `_from_node_attribute`(mod.Class)、`_from_node_constant`('X' 引号注解)
   全程不查 __new__,裸类名一律 PtrCount=0(原型)。
3. 局部变量路径 HandlesAnnAssign.py:678-683 显式把裸类名 PtrCount 由 1 改回 0
   ("纯结构体名注解应为值类型"),仅保留 vtable 类为指针;模块级全局
   HandlesAssign.py:1840-1856 亦按 struct 值类型建 GlobalVariable。
4. 符号表类条目 PtrCount 默认 0(SymbolTable._InsertClassSymbol:489-518 从不设
   PtrCount;TypeSpec.py:139 默认 0)。

因此 `d: dict = dict(mb)`(dict 有 __new__、非 vtable 类)在 TPC 侧本来就是
struct 值语义,与本次 TPV 改动后的口径一致。json/__init__.vp 的
`'JsonValue' | t.CPtr`(定义端 0+1 层、声明端裸类引用吸收 1 层)两侧都已是
`%"…JsonValue"*`(1 层),本次 TPV 改动后亦对齐。

待验证(不自行执行编译命令):先重编译 TransPyV,再跑
  JsonTest / DictTest / StructTest / BuiltinDecoratorTest / ClassArrayTest
预期先出现一批"注解需补 `*`"的报错——那正是本次口径变更要求的显式化。
2026-10-01 12:38:24 +08:00
TermiNexus 660427f759 fix(TypeGenResolve): X | t.CPtr 的 struct 引用语义吸收这层指针,修 json __getitem__ 报 JsonValue** vs JsonValue* 层数不同
现象(TransPyV 编译 includes/json/__init__.vp):
  line 90  return cur
  CastError: 指针隐式转指针已禁止:指针层数不同(源 1 级 → 目标 2 级)
    源 %"e966d554e91364bc.JsonValue"* → 目标 %"e966d554e91364bc.JsonValue"**

根因链(逐环取证):
1. includes/json/__init__.vp:79
     def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr:
   注意左边是**引号字符串注解**(Constant),不是裸 Name。
2. JsonValue 是带 __new__ 的**堆类**(该文件 :34),按引用语义,裸类名注解在
   resolve_annotation_type 的 Name/Attribute 分支直接给 Ptr(struct)
   (:2806-2813 注释「有 __new__ 的类(堆对象)注解 → Ptr(struct)(引用语义,对齐 TPC)」,
    Attribute/sha1 前缀/from-import 各分支同口径)。
3. 于是 BinOp(`| t.CPtr`) 分支里 other_ty 已经是 Ptr(JsonValue);而 :3176 那段
   「Name 且未注册 struct 才丢弃」的守卫只认 Name —— Constant 绕过了它,
   带着 Ptr(struct) 走到 :3264 `return llvmlite.Ptr(pool, other_ty)` 再包一层
   ⇒ JsonValue**。
4. 裸名写法(不带引号)不会暴露:它会在 :3222-3239 被判 td_res_is_ptr=1 →
   other_ty 置 None → 走 :3254 回退取 struct 再包一层,恰好抵消。
⇒ 同一类型两种写法产出两个层数(`JsonValue*` vs `JsonValue**`),与用户
   2026-09-30 定的统一口径冲突:T* ≡ rawptr[T] ≡ t.CPtr[T] ≡ T | t.CPtr。

改动(TypeGenResolve.resolve_annotation_type 的 BinOp(|t.CPtr) 分支):
- 在 :3264 包装前落实本文件 :3219-3221 注释里早已写明、但此前**未实现**的那条
  规则:「| t.CPtr 恒为追加 1 层,唯一例外是 struct 引用语义时吸收这 1 层
  (判据 = Ptr 的 pointee 是已注册 struct,find_struct_by_type 命中即吸收)」。
- 即:other_ty 已是 Ptr(已注册 struct) 时直接返回 other_ty,不再加一层。
- 副作用(同步修正同族写法):引号字符串写法、以及 Attribute 分支在
  find_struct_by_module 命中 HasNew 时返回 Ptr(struct) 的那条路径
  (:3040-3044),都从「多一层」收敛为 T*。
- 不影响的既有例外:VOIDPTR/CVoid|CPtr 等「指针别名」侧解析出的是 Ptr(i8)
  (i8 非 struct,命中不到 find_struct_by_type)⇒ `VOIDPTR | t.CPtr` 仍是
  i8**(追加一层),与 VpiReader 侧口径一致。

仅 TPV 侧(TPC 侧走 SymbolUtils.AddPointerLayers 的另一套口径,本次不动)。

待验证(不自行执行编译命令):先重编译 TransPyV,再跑
  TransPyV.exe --project ./Test/JsonTest/project.json --clean --clear-cache --rebuild-includes --traceback
预期 :90 不再报层数不同;若还有同族报错请把栈贴回。
2026-10-01 00:51:51 +08:00
TermiNexus 3329c9beae fix(TPV/CallGenBase): text.ll 返回类型的命名指针不再坍缩成通用 i8*,修 FakeDuck parts[0] 报整数隐式转指针
现象(TransPyV 编译 Test/FakeDuckTest):
  main.vp:115  p0: str = parts[0]
  CastError: 整数隐式转指针已禁止…出错表达式: 下标表达式 Subscript
    (parts = s1.split(','),s1: str,split 由 includes/_fakeduck.vp 的 _str 提供)

根因链(逐环取证,均落在产物文件上):
1. _fakeduck.vp:273  `def split(self, delimiter: str) -> list[str]*`。
2. 定义侧产物正确:temp/88/884eaabef9bd161c.text.ll:1775
     define %"b0798adca340002b.list[str]"* @"884eaabef9bd161c._str.split"(...)
   (884eaabef9bd161c = includes/_fakeduck.vp,b0798adca340002b = includes/_list.vp)
3. 调用侧 declare 走了 vpi 注解:temp/88/884eaabef9bd161c.vpi:27
     def split(self, delimiter: str) -> list[str] | t.CPtr: pass
   该联合在 stub 侧解析不出 `list[str]` 这个泛型特化名 ⇒ 坍缩成 i8*(既有设计已知的
   「跨模块 declare 的 list[T] 返回类型被简化」),故 _translate_fakeduck_call 里那条
   「从定义模块 text.ll 读精确返回类型」的兜底(CallGenFd.vp:134-142)必须生效。
4. 但该兜底读到的 `%"b0798adca340002b.list[str]"*` 又被本函数坍缩回 i8*:
   CallGenBase._ParseLLVMRetTyStr 的「含 * 者一律返回通用 Ptr(Int8)」分支
   (原注释:"包括 i8*, i32*, %"name"*, { i32, i8 }* 等")。
5. 于是 parts 的 alloca 退化为 i8* ⇒ parts[0] 走字节下标取到整数 ⇒ 赋给 str 报
   「整数隐式转指针已禁止」(报文指不到真因,正是本问题最难查的地方)。

改动(CallGenBase._ParseLLVMRetTyStr):
- 新增「命名类型 `%"name"*` / `%name*`」分支:剥引号/空格与尾随 `*`(支持多级),
  先 GenApi.get_struct_type(name)、未命中再 GenApi.get_enum_base_type(name),命中即
  按星号层数包 Ptr 返回真实结构体/枚举指针;查不到登记项时才保持原来的通用 Ptr(Int8)
  行为(名字可能来自尚未登记的模块,不引入新的静默降级面)。
- 同步更新该函数顶部说明(原写「含 * 者返回通用 Ptr(Int8)」,现区分字面量结构体 /
  命名类型 / 其余)。
- 收益面不限 FakeDuck:所有经 text.ll 取返回类型的方法调用(CallGenCall / CallGenDispatch
  / CallGenProp / CallGenFd 等)都从「指针一律 i8*」变为「命名类指针精确」,
  调用方 alloca 与下标/字段语义不再退化。

待验证(不自行执行编译命令):用 TransPyV.exe 重跑
  --project ./Test/FakeDuckTest/project.json --clean --clear-cache --rebuild-includes --traceback
预期 115 行不再报错;若仍在 parts2.get(0)(131 行)等处冒新错,请把栈贴回来。
2026-09-30 23:53:18 +08:00
TermiNexus f2f3e5b7bf fix(TypeGenResolve): 裸名内建类型统一为 t.X/c.X,消 CPtr 与 t.CPtr 的分裂与误报 ERROR
内建伪模块 t/c 没有模块 SHA1(规则 5 的模块名→sha1 表查不到),于是 from t import CPtr 之后的裸名 CPtr 被原样保留,而规则 3 保留的是 t.CPtr ⇒ 同一类型两种票名(list[CPtr] 与 list[t.CPtr] 分裂),并额外刷 ERROR [类型实参归属] 裸类名 CPtr 保留裸名(现场 Test/NewAstTest/App/main.vp,log 里 from_imports 明写 CPtr:t)。新增规则 5.2:from_imports 命中模块 t/c 且 map_t_type 认该名 ⇒ 返回 t.X/c.X(规则 3 已定的规范形)。只对"本名就是 t/c 已知内建类型名"生效,别名写法不猜真实符号名、维持原样。
2026-09-30 22:59:01 +08:00
TermiNexus 79f044bc47 fix(TypeGenResolve): _class_true_def_sha1 只取 hex 段,修双点前缀 d1ed…461..AST*
GenApi.sha1_prefix_len 返回含末尾 "." 的 17(其文档写明),前一版按 17 拷贝 hex 段后再交 _concat_sha1_class 拼 ".",得到双点前缀 %"….d1ed020479454541..AST*" ⇒ parser.vp:3355 return scratch.children 处 coerce 报 CastError。改为按 17 判形、比对与拷贝都用 16(hex 段本身)。
2026-09-30 22:45:26 +08:00
TermiNexus 7b0c05b70b fix(TypeGenResolve): 类名前缀校正为真定义模块,消 ast.AST(包) 与裸 AST 的前缀分裂
规则 4b 对 ast.AST(模块限定属名)走"模块名→SHA1"表,对包名回退到 ast/__init__.vp 的 sha1 1abcd5277fa442de(re-export 模块),而裸名 AST(stmts.vp 字段注解)经规则 5 后缀匹配得到真定义模块 d1ed020479454541(ast/base.vp)⇒ list[1abcd….AST*] 与 list[d1ed….AST*] 两个特化体并存,main.vp:63 classify_list(ifn.orelse,…) 报"指针隐式转指针已禁止"。新增 _class_true_def_sha1:在 cand 名下精确找登记项(find_struct_by_module,不按名回落,避免同名类跨模块误改),取其结构体 IR 名 %"<sha>.{类名}" 的前缀作真定义模块(不能取 entry.ModuleSha1——包 re-export 的别名 entry 记的是包 sha1、Ty 复用源 entry);无证据或前缀相同则原样返回 cand(已一致情形逐字不变)。接线:规则 4a/4b/5 各一行。
2026-09-30 22:16:15 +08:00
TermiNexus 0f6e102e02 fix(TypeGenResolve): rawptr[T] 下标形漏折叠致两个 list 特化体(修 _list.vp:83 return self 的 CastError)
前一版把 rawptr 从 _is_ptr_marker_text 移除(避免裸 rawptr 参与联合时名说 T*、类型说 T),但下标容器分支复用了同一判据 ⇒ rawptr[T] 完全没折叠,只有 T|t.CPtr 折叠:现场同时存在 list[<sha>.AST*] 与 list[rawptr[<sha>.AST]] 两个结构体,_list.vp:83 return self 的 self(前者)被 coerce 到返回注解(后者)时报 CastError。新增专用判据 _is_ptr_subscript_container_text(rawptr/t.rawptr/c.rawptr/CPtr 一族),下标分支改用它;联合分支仍用 _is_ptr_marker_text 保持与类型域 _is_cptr_marker 同口径。
2026-09-30 21:39:21 +08:00
TermiNexus b289e93650 fix(泛型实参 T* 形): 补齐 T* 消费侧(VpiReader 段解析 / AnnotClassName 提取 / 构造推断)
唯一规范形 T*(rawptr[T] ≡ t.CPtr[T] ≡ T|t.CPtr,见 83baf5a/2b2b70e)落地后,
所有"读**已归一实参文本**或**结构体名**并做文本解析/嗅探"的点都必须接受 T* 形,
否则会**静默**走错分支(不是报错,更难发现):

- VpiReader._resolve_single_vpi_type_str:尾随 * 段按层重写回 rawptr[...] 文本形
  再走原分支(与既有 rawptr[ 分支逐字等价)。必须补的原因:_subst_template_arg
  用特化类名 [...] 内的**已归一实参**替换模板注解里的 T,喂进来的就是 {sha}.AST*;
  旧代码只认 rawptr[ / t.CPtr[ / 裸 t.CPtr ⇒ 落到 strrchr('.') 取末段 "AST*"、
  查 struct miss ⇒ 返回 None/i8*,跨模块参数/返回宽度被悄悄收窄。
- ExprGenCls / AssignGenMixin 的 _extract_annot_class_name(各 2 处):段尾剔除集
  补 '*',剥后再 find_struct(旧形态 {sha}.AST|t.CPtr 按 '|' 分段能命中,新形态
  不剥则 miss ⇒ 返回 None ⇒ 链式 .get(0).kind() 分派退化)。
- CallGenCtor._infer_generic_ctor_type_arg:新增 _ptr_layer_type_arg —— 先经
  _norm_type_arg_name 归一(保证类名带定义模块 SHA1,否则裸 "AST*" 无法再归一、
  反而新分裂),再按 VarEntry/FieldEntry 的 Spec.PtrLevel 补 '*',使构造侧推出的
  类型实参与注解侧同票名(否则 Box(x) 这类隐式特化分裂成两个结构体)。
- CallGenDispatch.__sizeof__:'*' 判据由 strrchr 改为**尾随**判定 —— 原写法会把
  list[{sha}.AST*](元素为指针的容器)误判成指针并返回 8。

现场:Test/AstTest/App/main.vp:21 的 list[ast.AST | t.CPtr] | t.CPtr 特化链
(→ includes/_list.vp:36 T.__sizeof__())。
2026-09-30 21:16:48 +08:00
TermiNexus 2b2b70e636 fix(CallGenDispatch): __sizeof__ 认归一后的指针规范形 T*(尾随 * 一律按 8)
泛型实参归一为 T* 后,T.__sizeof__() 的实参串如 {sha}.AST* 既不命中 | 也不命中 rawptr[ 前缀,落回把接收者 Name(T) 当实例翻译 → NameError: 未定义名称 T(现场 Test/AstTest/App/main.vp:21 特化 list[ast.AST|t.CPtr] → includes/_list.vp:36)。在既有两条指针判据旁补尾随 * 判据(同一处、同一顺序,均在 dot-strip 之前)。
2026-09-30 20:58:02 +08:00
TermiNexus 83baf5a4b6 fix(TypeGenResolve/ExprGenName): 指针类型实参统一为 T*,消泛型特化名分裂(假报错 CastError)
用户定口径:t.CPtr[T] / rawptr[T] / T|t.CPtr ≡ T*(与 TPC 侧
SymbolUtils.ResolveTypeArgStr+AddPointerLayers 逐字同形,两侧必须同票名)。

- _norm_type_arg_name:联合一侧为裸 CPtr 标记 ⇒ 折成 <另一侧>*;单实参的
  指针标记容器 rawptr[T]/t.CPtr[T] ⇒ 折成 <实参>*(新增 _is_ptr_marker_text /
  _ptr_star_text;刻意只认 CPtr 一族,与类型域 _is_cptr_marker 同判据,
  避免名字说 T*、类型说 T 的不一致)
- _parse_type_string:能反解析尾随 '*'(按层包回 rawptr[...],供 ClassGenGeneric
  克隆注解时替换实参文本)
- ExprGenName._list_elem_obj_ty_from_struct_name:元素段剥尾随 '*' 后再查结构体
  (否则对象元素还原失败、静默退回 Ptr(i8) 丢元素类名)

现场:Test/AstTest/App/main.vp:63 classify_list(ifn.orelse, ...) 源
list[rawptr[<sha>.AST]] vs 目标 list[<sha>.AST|t.CPtr] ⇒ 严格指针规则按
「不同非基本类型」拒绝(假报错);改后同票名 ⇒ 同一结构体。

注意:特化名进结构体名与方法符号名 ⇒ 必须全量重编 includes
(--clean --rebuild-includes --clear-cache)。
2026-09-30 20:19:42 +08:00
TermiNexus f3c708613f refactor(TransPyV): 统一自举写法(字段默认值/novirtual/define/enum/指针注解)
- CallGenBase:字段声明处直接给默认值,InitCallGen 不再逐字段赋值
- CallGenFd/CallGenDispatch:@t.NoVTable 改 novirtual,清理过时注释
- VLogger:LogLevel 改 enum、t.CDefine 改 define 关键字、Logger|t.CPtr 改 Logger*、longjmp 用 rawptr
- Generator:_g_gen 由 Generator|t.CPtr 改为 Generator*
- CallGenProp:合并续行
2026-09-30 19:09:22 +08:00
TermiNexus 849001ae9e fix(AssignGen): 禁止注解声明类型被右值继承上转窄化
TPV 侧「注解退化修复」只挡了裸泛型基类降级,漏挡「注解写明基类、右值是派生类」的上转窄化:node: rawptr[AST] = Name(...) 会把 alloca 重建成 Ptr(Name),随后 node = <返回 rawptr[AST]> 变成被禁止的父→子下转(includes/ast/parser.vp:1919 报 指针隐式转指针已禁止:%AST* → %Name*)。补上与 TPC HandlesAnnAssign._rb_is_upcast 同判据的守卫(复用 ExprGenBase._is_inheritance_upcast)。
2026-09-30 19:08:58 +08:00
TermiNexus a92b38e826 fix(Translator): 文件级类名预登记覆盖三种 _declare_only 模式(修 Phase 1a 的类型实参裸名分裂)
类别:a(编译器缺陷)

现象:编译 includes/ast/base.vp 报
  ERROR [TypeGen]: [类型实参归属] 裸类名 'AST' 本次保留裸名(可能与带前缀形式分裂):
  entry_present=0 entry.Ty=None cur_def_mod=d1ed020479454541 cur_def_cls=ASTFlag
  is_module_class=0 find_struct_by_module=0 from_imports=*:stdint TokOp:ast.tokens
  file=./includes/ast/base.vp

根因链(逐条对应报文):
  报文来自 TypeGenResolve._norm_type_arg_name 规则 5.5:五类证据全落空即返回裸名。
  - ④ entry_present=0 / ① find_struct_by_module=0:'AST' 尚未 register_struct
    (AST 在 base.vp:313;本次归一化发生在更早的模块级函数签名处)。
  - ② is_module_class=0:'AST' 的 mark_module_class 尚未执行(类定义入口按 AST 顺序走)。
  - ③ cur_def_cls=ASTFlag:CurDefClassName 停在更早的 enum ASTFlag(类结束后不复位),
    与裸名 'AST' 逐字不同 ⇒ ③ 不适用(该值同时证明本次不是"泛型特化平移窗口")。
  - ⑤ is_file_class + get_file_module_sha1 本应兜住"本文件自己声明的类名",但
    register_file_classes 的调用被 `if self._declare_only == 0:` 整块门住;而本次是
    Phase 1a(_declare_only == 1)——TranslatorGenMixin 在 1a 会调用
    _prescan_funcdef_signatures **预解析模块级函数签名**(含注解归一化)⇒ 证据⑤ 恒落空。
  ⇒ 该实参特化名被冻结成**裸名**版本,与 1b 全量翻译期解析出的带 SHA1 形式分裂成两个
    特化结构体名,后续以"非基本类型指针不可隐式转换"报出(同类旧现场 base.vp:359)。

修复:把 GenApi.register_file_classes(pool, self.ModuleSha1, tree) 从 `_declare_only == 0`
  块内提到 _translate_module_level 开头,三种模式(1a=1 / 1a-pre=2 / 全量=0)共用。
  该登记只依赖 tree + 本文件模块 SHA1,与导入、类定义窗口、类定义顺序都无关;
  reset_visible_structs 在每个模式的 translate 之前调用(BuildPipeline:226 /
  Phase1Main:643),FileClassNames 与键同池创建,无悬空风险。未放宽任何 fail-fast。
2026-09-30 17:43:15 +08:00
TermiNexus 1cce6c4a47 fix(TypeGenResolve): 「保留裸名」误报的第二入口——逐实参归一时跳过定长数组的元素个数位 + CDefine 名不再算类名
类别:a(编译器缺陷)。`ef4421d` 只补了 `_norm_type_arg_name` 规则 1.5 的"跳过第 2 实参",
但那条规则**拿不到容器上下文**的入口不止一个,实测 still 报:

  ├ ERROR [TypeGen] [类型实参归属] 裸类名 'TRACEBACK_FRAME_BUF' 本次保留裸名 …
    entry_present=0 entry.Ty=None cur_def_mod=ba7cdbd0dec2aef8 cur_def_cls=TracebackState
    … file=./includes/traceback.vp

两处修(互为补充,判据都指向同一条事实:定长数组的第 2 实参是**常量**,不是类名):

1) `extract_class_name_from_annotation` 的 **Tuple 分支**(:2156/:2167,本函数自己按
   `,` 把实参拆开逐个归一,规则 1.5 在这里根本不生效):
   `TracebackState.FrameAddr: t.CArray[uint64, TRACEBACK_FRAME_BUF]`
   ⇒ 先按 `_is_count_arg_container(base_name)` 判容器是 t.CArray/list,是则第 2 实参
   (`ti_sn == 1`)**原样保留、不调用归一**(长度计算与拼接两个循环同步改)。

2) `_is_non_class_type_name` 增 **⑤ CDefine 名**:`NAME: t.CDefine = 128` 是编译期常量,
   名域就是它自身,永远不是类名 ⇒ 命中 `TypeGenCDef.lookup_cdefine_constant` +
   `is_cdefine_found()`(-1 也是合法值,必须看标志)即豁免"保留裸名"取证。
   这条判据落在**所有入口共用**的函数上,彻底覆盖其余"逐实参归一"的点
   (如 CallGenCtor:768 的泛型实参提取)。

静态核验:同形语句片(`self.Gen.TypeGenCDef.lookup_cdefine_constant(...)` 丢弃返回值、
`elt_name: str = raw` 后条件重赋值、`range`/`len` 循环)过 `lib.vast` 解析。
生效需重新自举 TransPyV.exe。
2026-09-30 16:05:29 +08:00
TermiNexus 2a798dd643 fix(类型转换): int→i1 目标改为真值归一化 != 0,不再 trunc 取最低位
类别:a(编译器缺陷)。`b: bool = f()`(f 运行时返回偶数非零,如 2)此前经
coerce_to_type 的窄化分支走 trunc ⇒ 最低位 0 ⇒ false,而同一值在 `if 2:` /
`bool(2)`(icmp ne 0)为真 —— 两处判据相反(静默语义分裂)。

- ExprGenBase.coerce_to_type:整数窄化分支前先判目标位宽 == 1 ⇒ `icmp ne x, 0`
  (C 6.3.1.2 的 _Bool 转换语义)。归一化不丢位,故对 i1 目标不再适用下方
  「窄化截断溢出」常量诊断(先返回)——`b: bool = 2` 由编译期 OverflowError
  变为正确的 true。
- CallGenCtor._translate_t_type_cast:显式 `t.CBool(x)` 与隐式落存储同一判据。

判据与 bool(x)(CallGenDispatch.vp:255-281)、条件归一化(AsyncGenMixin.vp:1611
_normalize_cond)、and/or 操作数三处统一。App/ 与 includes/ 全量检索未见
`: bool = …(& | >> | // | |)` 位提取写法,既有代码无依赖 trunc 取最低位的点。

生效需重新自举 TransPyV.exe(并清缓存)。
2026-09-30 15:14:32 +08:00
TermiNexus ef4421db20 fix(TypeGenResolve): 定长数组容器的「元素个数」实参不参与类名归属归一(消「保留裸名」误报)
【b 机制漏豁免】`_norm_type_arg_name` 规则 1.5 把 `Name[...]` 的**每个**顶层实参都递归
归一(目的是把类名统一成 `{定义模块SHA1}.类`)。但定长数组容器 `t.CArray[T, N]` /
`list[T, N]` 的**第 2 实参是元素个数**(编译期整数:CDefine 名 / 字面量 / 常量表达式,
依据是 TPC 侧 t.CArray 的定义原文"第一个参数为元素类型,第二个参数为数组大小(整数或
常量名)",lib/includes/t.py),对它做类名归属归一既无意义,又会落进规则 5.5 的
"保留裸名"取证 ⇒ 误报"类名归属缺失"。

现场:编译 includes/traceback.vp 时 TracebackState 的字段注解
`t.CArray[uint64, TRACEBACK_FRAME_BUF]`(traceback.vp:532),第 2 实参
'TRACEBACK_FRAME_BUF' 是 traceback.vp:482 的 `TRACEBACK_FRAME_BUF: t.CDefine = 128`
⇒ 每次编译刷
"[类型实参归属] 裸类名 'TRACEBACK_FRAME_BUF' 本次保留裸名…"ERROR。

修法:新增 `_is_count_arg_container(container)`(按**容器短名**判定,兼容 `tt.CArray`
这类别名;只认 CArray / list,泛型类 GSList[T]/Box[T] 的实参全是类型名不受影响),
规则 1.5 的实参拆分循环按序号跳过第 2 实参 —— 原样保留、不递归。
归一结果逐字不变:常量名与字面量既非类名也非 CTypedef,归一恒返回原串;TPC 侧
`ResolveTypeArgStr` 对同位置同样原样保留(Name→NormalizeGenericArgName / Constant→
unparse),两侧仍一致。改变的只是不再触发该取证日志。

配套:上一提交 d63787f 的 `_is_non_class_type_name` 覆盖同注解的**第 1 实参**
(`uint64` = builtins.vp 的 typedef)——两条判据互补:类型位放行非类名,个数位不归一。

未验证:需重新自举 TransPyV.exe 才生效。vast 解析通过。
2026-09-30 14:05:03 +08:00
TermiNexus d63787f8fc fix(TypeGenResolve): 「保留裸名」取证对 CTypedef/内置标量/泛型形参不再报 ERROR
【b 机制漏豁免】取证日志(df1244d 加入)的前提是"这是个**类名**":只有类名存在
"{定义模块SHA1}.类" 这第二个 mangling 形式,留裸名才可能与它分裂。CTypedef /
语言内置标量 / 泛型形参的名域就是它自身,保留裸名即规范形式(TPC 侧
SymbolUtils.NormalizeGenericArgName 同口径),对它报 ERROR 是纯噪声。

现场:编译 includes/traceback.vp 时,TracebackState.FrameAddr 的注解
`t.CArray[uint64, TRACEBACK_FRAME_BUF]`(traceback.vp:532)经规则 1.5 递归规范化
实参 'uint64' ⇒ 规则 5.5 取不到 SHA1 ⇒ 每次编译刷一条
"[类型实参归属] 裸类名 'uint64' 本次保留裸名…"。'uint64' 是 includes/builtins.vp 的
`typedef uint64 = t.CUInt64T`,不进类归属是预期行为,不是缺陷。

修法:新增 `_is_non_class_type_name(pool, name)`(正向白名单,不猜):
  ① CTypedef 动态表命中(Phase1 1b register_ctypedef_from_source 文本扫描注册);
  ② builtins 兜底 `_lookup_builtins_typedef`(与 resolve_typedef_or_map 同一口径:
     builtins 始终在编译链内,但其 typedef 不随 from_imports 注入 ⇒ 表可能 miss;
     命中即幂等注册进表);
  ③ 语言内置标量/字符串(int/char/bool/float/double/str/bytes/void,与
     _resolve_annot_type_name 的跳过清单同口径);
  ④ 泛型形参名(单字母大写 T/U/V…,特化前出现在实参位属正常)。
门控只对「结构体表里根本没有该名」(_bare_entry is None)豁免:若它确是已注册的类
(.vpi 半条目)只是缺定义模块 SHA1,那是真正的归属缺口,照旧报出,不被同名 typedef 掩盖。

未验证:本改动需重新自举 TransPyV.exe 才生效(TPC 侧无对应逻辑)。vast 解析通过。
2026-09-30 13:51:22 +08:00
TermiNexus 0e5ebd07ba fix(TypeGenCType): 撤销在子类类体重复声明父类字段(TypeGenBase 已声明),修指针隐式转整数误报
上一版(6a287b9)为修 Repl.vp:802 在 TypeGenCType 类体补了 5 个字段,但这 5 个
(CTypedefNames / CTypedefTypeStrs / CTypedefNameIdx / CTypedefNameIdxCap /
CTypedefNameIdxBuilt)**本就由父类 TypeGenBase 在类体声明**(TypeGenBase.vp:73-84),
属重复声明(c 源码违规)。

后果(有实证):这 5 个名字进入子类"自有字段"后,HandlesImports._FlattenExt 的
继承前缀前置过滤器(HandlesImports.py:2516-2521,为防重复前置而按名字过滤)
把**同名继承项整条丢弃** ⇒ class_members['TypeGenCType'] 变成 [36 继承项]+[5 自有项]。
被丢掉的 5 项(继承下标 22/23/25/26/27)都排在 LastBuiltinsTypedefResult
(继承下标 32)之前,使其表内下标降为 27;NoVTable 类 base=0 ⇒ 命中 body 槽 27
= CTypedefNameIdxBuilt(i32) ⇒ TypeGenResolve.vp 的
`self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 触发
"指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。

该重复声明还会让父类方法(TypeGenBase.vp InitTypeMixinFields 等)与子类方法
访问同一字段落到不同槽(split-brain)。

撤销后:子类自有字段=0,继承前缀 41 项按原顺序保留,LastBuiltinsTypedefResult
回到表内下标 32 = body 槽 32(i8*,与 TypeGenBase 声明的 str 一致)。

说明:Repl.vp:802 的真实根因(引用模块里继承前缀是否可达)尚未定论,本次只撤销
误修、不掩盖;该类属性链失败现已由 TPC 侧新增记账(a18ca4da)逐条给出失败原因链。
2026-09-30 10:46:43 +08:00
TermiNexus 6a287b955e fix(TypeGenCType): 跨模块可见字段改为在类体声明(修 Repl.vp:802 属性链求值为空)
类别:c 源码违规(跨模块读得到的字段必须在类体声明)

现象:TPC 编译 TransPyV/App/lib/core/Repl.vp:802
  ct_names: list[str]* = GenApi._g_gen.TypeGenCType.CTypedefNames
报「带注解赋值表达式 …(Attribute 形态)编译期求值为空(表达式未解析出目标、无值可存),
赋值将被跳过、变量保持未初始化」。

判据(.vpi 与定义侧不同源,实证):
· TypeGenCType 类体**一个字段都没声明**:CTypedefNames / CTypedefTypeStrs / CTypedefNameIdx
  / CTypedefNameIdxCap / CTypedefNameIdxBuilt 全部只以方法体里的裸 `self.x = ...` 出现
  (:184 `self.CTypedefNames = tn`、:185、:66-73、:118-121)。
· 声明存根(.vpi)只携带**类体 AnnAssign 字段**与 `__init__` 里**带注解**的 self.x;方法体
  里的裸赋值只被**定义模块自己**的成员收集(HandlesClassDef 的 self.x 扫描)收录
  ⇒ 同一字段"定义模块成员表里有、引用模块没有"(实证:temp/05d515186a7c3668.vpi 内不含
  CTypedefNames 条目)⇒ 引用模块的属性链在第三级断掉,整条带注解赋值被判"无值可存"而终止。

修法:把这 5 个字段在**类体**显式声明(类型取自各自赋值处的真实类型):
  CTypedefNames / CTypedefTypeStrs : list[str] | t.CPtr   (:177-178 的 tn / ts 即 list[str]*)
  CTypedefNameIdx : sizet*                                (:64 / :71 的 idx)
  CTypedefNameIdxCap / CTypedefNameIdxBuilt : int
声明后定义侧与 .vpi 两侧字段集一致(类体 AnnAssign 先收集,方法体 self.x 扫描按名去重不再
重复登记)⇒ 布局同源。注意:该类的字段顺序/类型与旧构建不同(字段从"仅扫描收集"变为
"类体声明"),需要清缓存重编译。

附带(另一仓 commit):TPC 侧 `_handle_attr_nested` 的 7 处静默 return 已补 `_NoteAttrNone`
记账(含内层失败原因链),下次同类报错会直接给出"读取侧原因=…"。
2026-09-30 09:59:54 +08:00
TermiNexus c1a22411ca fix(TypeGenResolve): 规则 5.5 增「本文件顶层类名」证据,模块级早于类定义的窗口不再留裸名
类别:a 编译器缺陷(TPV 侧;补齐"第二个裸类名窗口")

现象(与上一轮 e1bb42f 同一现场、不同窗口):编译 includes/ast/base.vp 时 :359
`_append_child(self.children, node, self.pool)` 报"指针隐式转指针已禁止(被指类型是
非基本类型…)",源 list[rawptr[AST]]* 与目标 list[rawptr<定义模块SHA1>.AST]* 差在实参的
SHA1 前缀。

判据(第二个窗口 = 模块级语句早于类定义):
· Translator.translate 的 prologue 先跑模块级 AnnAssign/Assign(Translator.vp:592-599)
  与 forward_declare_functions(:601),**之后**才 translate_children(:602) 逐个翻译类;
  而"正在定义类"窗口(CurDefModuleSha1/CurDefClassName)只在类定义入口
  (ClassGenDef.vp:50)或 .vpi 注入(VpiReader.vp:2659)才建立 ⇒ 前者解析注解时窗口为
  None ⇒ `_norm_type_arg_name` 规则 5.5 的 ①②③④ 全落空 ⇒ 注解里的裸类名(base.vp:148
  `_append_child(..., lst: list[rawptr[AST]])` 的形参即此窗口)以**裸名**形式被首次注册,
  并被后续复用(首写者胜:StructGenMixin 按 Ty 去重、ClassGenDef existing 早退不重算)
  ⇒ 与同一特化在类定义期解析出的带 SHA1 形式分裂。

修法(正向事实,不猜;与窗口/类定义顺序无关):
1. `StructGenMixin` 增文件级登记:字段 `FileClassNames`(本文件顶层 ClassDef 类名表,
   表与键同池、每文件随 `reset_visible_structs` 重建)+ `FileModuleSha1`(本文件模块
   SHA1),以及 `register_file_classes` / `is_file_class` / `get_file_module_sha1`。
   键从 AST 的 name **复制到 pool** 后再登记(AST name 零拷贝指向 src_buf,跨阶段可能
   失效,直接当表的键会 strcmp 到悬空内存)。
2. `Translator.translate` prologue:在模块级变量/前向声明**之前**调用
   `GenApi.register_file_classes(pool, self.ModuleSha1, tree)`(GenApi 增同名委托)。
3. `TypeGenResolve._norm_type_arg_name` 规则 5.5 增**第五类证据**:①②③④ 全落空时,
   若 `is_file_class(name)`(该裸名确由本文件声明)且本文件模块 SHA1 已知 ⇒ 归属本文件。

验证:TPV 侧改动需重新自举 TransPyV.exe 后生效;本轮以代码级证据链
(Translator.vp:592-602 / ClassGenDef.vp:50 / TypeGenResolve.vp 规则 5.5 闸门)自证。
TransPyV 仓另有他人未提交文件,本次只提交本改动的 4 个文件。
2026-09-30 00:23:43 +08:00
TermiNexus e1bb42fade fix(ClassGenGeneric): 泛型特化不再平移"类定义窗口",裸类名实参不再冻结成裸名
类别:a 编译器缺陷(TPV 侧)

现象(反复出现 4 轮):编译 includes/ast/base.vp 时 :359 `_append_child(self.children, node,
self.pool)` 报
「指针隐式转指针已禁止(被指类型是非基本类型…):源类型 %"…list[rawptr[AST]]"* →
目标类型 %"…list[rawptr<定义模块SHA1>.AST]]"*」——同一泛型特化被写成两种拼名(实参一侧
裸类名 `AST`、另一侧带定义模块 SHA1 前缀)。

判据(结构缺陷,代码级可证):
· `_norm_type_arg_name` 规则 5.5 判定"裸类名归属"依赖两个**全局窗口变量**
  `StructGen.CurDefModuleSha1` / `CurDefClassName`(证据 ② `is_module_class`、
  ③ `CurDefClassName` 逐字相同),入口闸门是 `_def_mod is not None`。
· `ClassGenGeneric._specialize_generic_class` 在 :715-720 把 `trans.ModuleSha1` 临时切到
  **泛型模板定义模块**,再于 :729 调 `ClassGenDef.translate_class_def`;后者在
  `ClassGenDef.vp:50` 会 `mark_module_class(pool, trans.ModuleSha1, class_name)`
  ⇒ **窗口被覆盖成"模板模块 + 特化类名"**。原实现返回时只恢复 `trans.ModuleSha1` 与
  `_imported_modules`(:733-735),**没有恢复这两个窗口变量**。
· 后果:调用方文件在此之后解析任何裸类名时,证据 ②③ 都在对着"模板模块 / 特化类"判 ⇒
  全落空 ⇒ 该实参以**裸名**形式被首次注册并被后续复用(首写者胜:`StructGenMixin.vp:455`
  按 Ty 指针去重、`ClassGenDef.vp:100-150` existing 早退不重算)⇒ 与同一特化在别处解析出的
  带 SHA1 形式分裂成两个名字 ⇒ 被"非基本类型指针不可隐式转换"拦下。

修法:
1. `ClassGenGeneric.vp`:窗口与 `ModuleSha1` **同进同出** —— 在切 ModuleSha1 之前保存
   `CurDefModuleSha1`/`CurDefClassName`,:744 之后一并恢复(嵌套特化不再污染调用方窗口)。
2. `TypeGenResolve.vp` 规则 5.5 的取证日志扩展:除原有"命中 struct 条目但 entry.ModuleSha1
   为空"外,**新增触发条件"窗口存在却属于另一个类"**(`_def_mod` 有值且
   `CurDefClassName` 与裸名不同)——正是"窗口被特化平移/污染"或"前向引用未登记类"的签名;
   报文补出 `entry_present` 与 `find_struct_by_module` 两项事实,一轮即可定位来源是否缺失。

验证:TPV 侧改动需重新自举 TransPyV.exe 后生效;本次以静态证据链(上述行号)与取证日志
自证。注:TransPyV 仓另有 7 个他人未提交文件,本次只提交本文件与 TypeGenResolve.vp。
2026-09-30 00:20:30 +08:00
TermiNexus 31f341cf6f fix(transpyv): vpi 注入期登记「本模块声明的类」,裸类名实参不再冻结成裸名
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 的入口闸门是
`_def_sha1 is None and _def_mod is not None`(_def_mod = CurDefModuleSha1),而
VpiReader._inject_class_struct_declare 是在**注入期**解析字段注解的——此刻既没有
"类定义窗口"(CurDefModuleSha1=None ⇒ 闸门整条关闭、四条证据一条都不查),被注入的类
也尚未 register_struct ⇒ 注解里的裸类名实参取不到定义模块 SHA1 ⇒ 特化名被冻结成
**裸名版本**,与该类在自身翻译期(类已注册)解析出的带前缀形式分裂成两个特化名,
最终以"非基本类型指针不可隐式转换"报出。现场:includes/ast/base.vp:359
  _append_child(self.children, node, self.pool)
  源 %"…list[rawptr[AST]]"* vs 目标 %"…list[rawptr<定义模块SHA1>.AST]"*
这也解释了前两轮修复为何无效:③(自引用判据)依赖 CurDefClassName、④(entry.Ty 取
前缀)依赖 struct 条目,而注入期两者都不可用,且此时连日志条件(entry 命中)都不成立。

- GenApi:新增 mark_module_class 委托(与 add_visible_struct / find_struct_by_module 同款)。
- VpiReader._inject_class_struct_declare:在解析字段注解之前调用
  GenApi.mark_module_class(pool, src_sha1, cls_name) —— 一次调用同时完成
  ① 登记"{src_sha1}.{cls_name}"(名字归属,与 struct 注册状态无关);
  ② 把 CurDefModuleSha1/CurDefClassName 指向本次注入的模块与类(打开规则 5.5 的闸门)。
2026-09-29 22:29:48 +08:00
TermiNexus df1244d6f4 fix(transpyv): 裸类名归属补「符号名真源」(entry.Ty 的 {SHA1}.{类} 前缀)+ 取证日志
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 判定裸类名归属只认三条证据——
struct 注册表的 ModuleSha1、ModuleClasses 登记表、以及"正在定义类"。Phase A-pre 没有
"类定义窗口"(CurDefModuleSha1=None),而 .vpi 注入的类条目常常 ModuleSha1 为空 ⇒
三条全落空 ⇒ 实参保留裸名,与后续(类已注册时)解析出的带前缀形式分裂成两个特化名
⇒ 被"非基本类型指针不可隐式转换"拦下。现场:includes/ast/base.vp:359
  _append_child(self.children, node, self.pool)
  源 %"…list[rawptr[AST]]"* → 目标 %"…list[rawptr<定义模块SHA1>.AST]"*

- 规则 5.5 新增证据 ④(表/窗口都不可用时的**符号名真源**):结构体自身的 IR 名本就是
  "{定义模块SHA1}.{类名}"(与本仓全部 mangling 同源:ClassGenDef/ClassGenVTable/
  VpiReader 的 Stub 构造)⇒ 打印 entry.Ty 并取回其 sha1 前缀(复用
  _is_sha1_prefixed 这一唯一判据,不另写判定)。
- 同一处补取证日志:命中 struct 条目却一条 SHA1 证据都取不到时(可疑子集:
  entry.ModuleSha1 为空),把 entry.Ty / cur_def_mod / cur_def_cls /
  is_module_class / from_imports / file 一次打出(只加日志,返回值语义不变)。
2026-09-29 21:28:18 +08:00
TermiNexus bbd0cdbf62 fix(transpyv): 自引用类名归属加「表无关」判据,字段与形参不再分裂成两个特化名
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 判定"正在定义的类"的裸名归属
时只认两条证据——struct 注册表(find_struct_by_module)与 ModuleClasses 登记表
(is_module_class)。自引用字段(includes/ast/base.vp `AST.children:
list[rawptr[AST]]`)在类定义体内解析时本类尚未 register_struct,而 ModuleClasses
的键是 pool 字符串,登记/查询受"表未就绪、键随 pool 回收"影响 ⇒ 两条都可能落空
⇒ 实参留在裸名 `list[rawptr[AST]]`,与同一注解在形参处(类已注册)解析出的
`list[rawptr<定义模块SHA1>.AST]` 分裂成两个特化名 ⇒ 被"非基本类型指针不可隐式
转换"拦下(现场:includes/ast/base.vp:359
`_append_child(self.children, node, self.pool)`,源 %"list[rawptr[AST]]"* →
目标 %"list[rawptr<sha1>.AST]"*)。

- StructGenMixin:新增 CurDefClassName —— 与 CurDefModuleSha1 同源同生命周期,
  由 mark_module_class 在类定义入口(ClassGenDef 第 50 行,早于字段注解解析)
  一并记录;InitStructGen / reset_visible_structs 同步清空。
- TypeGenResolve._norm_type_arg_name 规则 5.5:新增证据 ③(表无关)——查询名与
  正在定义的类名逐字相同即判为本类,用 CurDefModuleSha1 加前缀。类名直接来自类
  定义入口、不经任何表 ⇒ 自引用窗口内必然可判;证据 ② 失败时不再退回裸名。
2026-09-29 20:16:05 +08:00
TermiNexus 1b2324edab fix(transpyv): 修正 mark_module_class 的表生命周期(ASan 悬空键崩溃)
类别:a(编译器缺陷 —— 上一提交自引入的跨文件悬空 HashTable)

现场:ASan access-violation(READ @0x1),rcx=0x1
  string.strcmp (includes/string.vp:86)
  → HashTable._find_slot (_resize) → __setitem__ → set_int
  → StructGenMixin.mark_module_class → ClassGenDef.translate_class_def
  → TranslatorGenMixin.translate_children → Translator.translate → RunPhase1 → main

根因:`ModuleClasses` 原先在 `mark_module_class` 里**懒创建**,并长期挂在 Gen 上;
表体与键都取自当次文件的 pool,而 pool 在文件/遍切换时会被 reset ⇒ 下一个文件里
`_resize` 重新散列旧键时 strcmp 读到已回收内存(0x1)⇒ 崩溃。

改动(与既有 `VisibleStructs` 同口径:表与键同池、同文件生命周期):
1. `ModuleClasses` 改由 `reset_visible_structs`(每文件翻译前调用)**无条件新建**
   (不再区分 strict 分支),与键同池 ⇒ 无悬空。
2. `CurDefModuleSha1` 指向 `trans.ModuleSha1`(同一 pool 的串),同样在该函数里
   清空,避免下一次 strcat/strcmp 读已回收内存。
3. `mark_module_class` 不再懒建表:池或表缺失时只记 `CurDefModuleSha1`、跳过登记
   (宁可退化为改前行为,也不冒悬空风险)。

语义不变:规则 5.5 的证据链仍是 ① 结构体表按名(跨文件/跨遍)→ ② 正在定义类所属
模块 + find_struct_by_module → ③ 本文件已声明类归属表(自引用字段窗口 —— 登记与
查询在同一文件同一遍内,正是该表的作用域)。
2026-09-29 19:14:10 +08:00
TermiNexus 2b8cc2a69b fix(transpyv): 特化名规范化下沉到唯一生成器,裸实参路径不再造第二个名字
类别:a(编译器缺陷 —— 特化名生成点不止一处,实参在"裸名/带前缀"两态并存)

背景(命名源审计,两个独立检索子代理收敛到同一清单):`_norm_type_arg_name`
是唯一规范化器,但"拼 Class[a0,a1]"的构造点有 20+ 处,其中 6 处绕过它。
本轮把规范化**下沉到唯一生成器**,让所有调用方自动同源 —— 该函数对已规范化的串
是幂等的(规则 2/3/6 原样返回,嵌套 rawptr[X] 递归同形)。

改动:
1. ClassGenGeneric._mangle_generic_class_name 增 from_imports 参数:逐实参经
   TypeGenResolve._norm_type_arg_name 归一后再拼串(长度计算与拼接同用归一后列表)。
   唯一调用点 _specialize_generic_class 传 trans._from_imports。
2. ClassGenBase._get_base_name 增 from_imports 参数:Subscript 分支的类型实参
   (原为裸 Name.id)同样归一 —— 该函数产出的父类特化名要拿去 find_struct_by_module
   查、并写入 entry.ParentName,而注册名由生成器产出,不归一必然对不上
   (父类 GSListNode[Value] vs 注册的 GSListNode[{sha1}.Value])。
   三处调用点(_get_parent_class / _collect_parents / _trigger_base_specialization)
   传 trans._from_imports;递归调用透传。
3. _trigger_base_specialization 本身无需改:它的裸实参进入 _specialize_generic_class,
   已由第 1 条统一归一。

收敛效果(连带):_specialize_generic_class 的 spec_name 与
TypeGenResolve._resolve_generic_subscript_ty 自拼的 gen_mangled 现已同形(后者本就
归一),源码注释里"gen_mangled 与特化名可能不一致 ⇒ 用 spec_name_chk 重查"那条
隐性双名自然不再触发。

遗留(未动,见审计清单):ExprGenConst.translate_list_literal /
CallGenCtor._infer_generic_ctor_type_arg 传的内建名不受影响(规则 6 原样保留),
但其"类名"实参现由生成器统一归一;CallGenCall/CallGenDispatch 从已注册名回拆
"[...]" 时用 strchr 按首个逗号切分(深度不敏感)另计。

影响面:ABI 可见(结构体名 / 方法符号后缀段);需 TPV 自举重建 + 回归验证,
重点观察 VpiReader 的泛型键匹配(其键以 T 占位符,初判不受影响)。
2026-09-29 18:53:58 +08:00
TermiNexus 868d48ae22 fix(transpyv): 自引用字段的特化名不再留在裸名(新增"本模块声明类"归属表)
类别:a(编译器缺陷 —— 字段类型在类注册前被冻结成裸名版特化名)

现场(上一轮未修掉的那一处):includes/ast/base.vp:357
    if self.children is None: self.children = list[rawptr[AST]](self.pool, 8)
CastError:源 %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"*
       → 目标 %"7e2b6dda6e423bfb.list[rawptr[AST]]"*

根因:字段 children: list[rawptr[AST]] | t.CPtr 写在**类 AST 自己体内**(自引用),
其类型在 translate_class_def 的字段循环里解析,而本类的 register_struct 在该函数
**末尾**才执行 ⇒ 解析时 struct 表里没有 AST ⇒ 上一轮 5.5 的两条查法
(find_struct 按名 / find_struct_by_module)全部落空 ⇒ 实参仍留裸名 rawptr[AST],
字段类型被冻结成 list[rawptr[AST]](第一遍 A-pre 即发生);而 :357 的构造调用发生在
类注册之后 ⇒ 带 SHA1 ⇒ 两侧分裂。(同一分裂在 stmts.vp:60 表现为形参侧/实参侧,
本处表现为字段侧/构造侧。)

改动:
1. StructGenMixin 新增"本模块声明类"归属表 ModuleClasses(键 "{模块SHA1}.{类名}")
   与 CurDefModuleSha1(正在定义类所属模块 SHA1,登记时同步记录):
   - mark_module_class(pool, sha1, name) / is_module_class(pool, sha1, name)
   - 键含 SHA1 ⇒ 与翻译顺序/模块切换无关、无需每文件重置,也不会把别的模块的类
     错认成本模块。
2. ClassGenDef 类定义入口(add_visible_struct 之后、枚举/联合分派之前)登记
   mark_module_class(pool, trans.ModuleSha1, class_name),覆盖 class/enum/union/
   renum 全部类种类。
3. TypeGenResolve 规则 5.5 补齐证据链:① 按名取 StructEntry.ModuleSha1;
   ② struct 表暂无条目时用 CurDefModuleSha1 走 find_struct_by_module;
   ③ 仍无条目(正是自引用窗口)时查归属表 ⇒ 加 SHA1 前缀。
   CurDefModuleSha1 取代 CurrentModuleSha1 作为"当前定义模块"真源——后者在
   A-pre/StubMerger 路径未设置或留着上一文件旧值(只有 Translator.ModuleSha1 权威)。

影响面:ABI 可见(特化名);需 TPV 自举重建 + 回归验证。
2026-09-29 18:47:35 +08:00
TermiNexus dddaba3254 fix(transpyv): 5.5 步补「类定义中」回退,字段侧特化名不再留在裸名
类别:a(编译器缺陷 —— 同一特化在字段侧/构造侧仍分裂,方向相反)

现场:includes/ast/base.vp:357
    if self.children is None: self.children = list[rawptr[AST]](self.pool, 8)
CastError:非基本类型指针不可隐式转换
  源(构造调用结果) %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"*
  目标(字段声明类型) %"7e2b6dda6e423bfb.list[rawptr[AST]]"*

上一处(stmts.vp:60)是"形参侧裸名 vs 实参侧带前缀",本处反向:**字段侧**
仍是裸名。字段 children: list[rawptr[AST]] | t.CPtr 写在**类 AST 自己体内**
(自引用),解析该字段类型时类正在定义中:find_struct('AST') 按名查不到(登记项
尚未命名)⇒ 5.5 首条查法落空 ⇒ 仍返回裸名;而 :357 的构造调用发生在类已注册之后
⇒ 带前缀,两侧再度分裂。

改动:5.5 步在 find_struct 未命中 / ModuleSha1 为空时,回退用与
resolve_annotation_type 的 Name 分支**同一处证据**认定本模块类:
find_struct_by_module(name, self.Gen.TypeGenBase.CurrentModuleSha1) 命中即以当前
模块 SHA1 加前缀。对同一模块的类,"按名取定义模块 SHA1"与"当前模块身份"给出同一
SHA1,故不引入新分裂。文档串规则表同步补该回退。
2026-09-29 18:38:52 +08:00
TermiNexus 65d7f7cc31 fix(transpyv): 泛型实参里的「本模块裸类名」也加定义模块 SHA1,特化名不再分裂
类别:a(编译器缺陷 —— 同一类型在本模块与被引用模块得到两个特化名)

现场:includes/ast/stmts.vp:60
    _set_parent_list(targets, self)
CastError:非基本类型指针不可隐式转换
  源   %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"*
  目标 %"7e2b6dda6e423bfb.list[rawptr[AST]]"*
外层特化前缀相同,只有类型实参里的类名一个带 SHA1、一个裸名 ⇒ 同一个
list[rawptr[AST]] 变成两个结构体 ⇒ 被"非基本类型指针不能隐式转换"拦下。

根因:_norm_type_arg_name 的第 5 步只查 from_imports(可见性表);**本模块自己
声明的类**不在表内 ⇒ 落到第 6 步原样返回裸名,而被引用模块解析同一类型时第 5 步
命中(from-import 归属模块)带 SHA1 ⇒ 两侧对同一类型产出两个名字。

改动(_norm_type_arg_name 新增 5.5 步):
- 简单裸名(不含 '[' / '.')先经结构体表 find_struct(name) 取该类的
  **定义模块 SHA1**(StructEntry.ModuleSha1)加前缀 —— 与"当前模块"无关,
  故本模块内与跨模块得到同一票名;仅当登记项存在且 ModuleSha1 非 None 时生效。
- 文档串规则表同步补 5.5;第 6 步措辞改为"内置类型名等"。

与第 5 步同口径(都只处理简单裸名):泛型特化名 list[...] 与模块限定名
mod.Class 各有自己的分支,不在 5.5 重复加工。

注:TPC 侧同名规则源(SymbolUtils.ResolveTypeArgStr)是否需要同步,待该现场
回归通过后再核。
2026-09-29 18:31:52 +08:00
TermiNexus 8e7d7feb57 fix(transpyv): rawptr[T] / t.CPtr[T] 对裸类引用吸收一层指针(不再多包一层)
类别:a(编译器缺陷 —— TPV 注解解析与「联合写法 / TPC 两侧」口径分裂)

现场:includes/ast/tokens.vp:217
    ptr: rawptr[Token] = pool.alloc(Token.__sizeof__())
CastError(TPV ExprGenBase.vp:746-753):指针层数不同(源 1 级 → 目标 2 级);
源类型 i8* → 目标类型 %"…Token"**。

根因链:
1. 本文件 Name 分支(:2269-2277):有 __new__ 的堆类裸名注解解析为 Ptr(struct)
   (引用语义,注释明写「对齐 TPC」)⇒ Token → %"…Token"*。
2. rawptr[T] 分支(原 :2865-2871)与 t.CPtr[T] 分支(原 :2928-2939)只做
   Ptr(resolve(T)),不含「裸类引用吸收一层」⇒ %"…Token"**(2 级)。
3. 同一语义的其余实现都有该规则,唯此两处缺失:
   - 联合写法 X | t.CPtr 的 BinOp 分支(:2682-2684 的判据注释 + :2703-2720 回退)
     得 1 级;
   - TPC 侧 LLVMTypeMapper._rawptr_subscript_type_str(内层映射成 %…* 即吸收)
     与 HandlesTypeMerge._MergeAllComponentTypes(IsStruct 时吸收一层)。
   规则真源见 TPC SymbolUtils.IsPtrSubscriptNode 文档:「内层 T 为类引用时该层被
   结构体自身吸收('<sha1>.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 /
   嵌套指针下标)逐层外包」。

改动:
1. 新增判据 _ptr_subscript_absorbs_class(slice, resolved):切片**非**用户写明的
   指针包装(联合 BinOp / 嵌套 rawptr[...]、t.CPtr[...]、裸名 CPtr[...])且解析
   结果已是 Ptr(已注册 struct)(find_struct_by_type 命中,与 BinOp 分支同一判据)
   ⇒ 返回 1(吸收该层)。纯判据,不发射类型。
2. rawptr[T] 与 t.CPtr[T] 两个分支接入该判据。

逐条核对保持不变的形态:rawptr[int]→i32*、rawptr[str]/rawptr[bytes]→i8**、
t.CPtr[t.CPtr]→i8**、rawptr[rawptr[X]]→X**、t.CPtr[int]→i32*、
rawptr[Point(无 __new__ 的栈类)]→Point*。

影响面:ABI 可见(字段 / 形参 / 局部变量类型),需 TPV 自举重建 + 整轮回归验证。
2026-09-29 18:07:37 +08:00
TermiNexus c2d7480f3d fix(transpyv): 槽视图统一 cast 到声明类型(47 处,含 codegen 自身)
同 includes 的同类修正:把一级指针 i8* 隐式塞进二级注解(i8** / str*)。
TransPyV 侧 coerce(ExprGenBase._ptr_cast_reason)没有 TPC 那样的「值侧是显式
指针 cast ⇒ 按 bitcast 重解释」豁免,故一旦用新构建的 TransPyV 编到这些文件
必然 fail-fast(回归测试首爆点在 includes/llvmlite 侧,本仓同形态站点随后必然
同样触发)。

涉及的两种形态:
  · `slot: str | t.CPtr = (t.CPtr)(addr)`   —— cast 目标是一级 t.CPtr
  · `nm_slot: str* = (t.CPtr)(addr)`        —— 注解 str* 即 i8**
统一改为 cast 到声明类型:`(str | t.CPtr)(addr)` / `str*(addr)`(后者与
includes/sys.vp `argv_arr: str* = str*(...)` 同一写法)。

本仓 47 处 / 6 文件:
  App/lib/core/Gen/VarGenMixin.vp 3
  App/lib/core/Gen/TranslatorGenMixin.vp 8
  App/lib/core/Gen/Func/FuncGenMixin.vp 13
  App/lib/core/Gen/AsyncGenMixin.vp 15
  App/lib/core/Gen/DebugInfoGen.vp 6
  App/lib/Stub/StubGen/Converter.vp 2
Converter.vp:107 的 `_g_pyi_cdef_names: str | t.CPtr = None` 保持原样
(None 即空指针,任何指针类型均可,不属本类问题)。

只提交本 6 个文件;本仓其余未提交改动(CallGenBase/CallGenDispatch/CallGenFd/
CallGenProp/Generator/Translator/VLogger)与本迁移无关,保持不动。
2026-09-29 15:10:33 +08:00
TermiNexus 07f2f2e9fc fix(transpyv): CallGenCBuiltin 的 clobbers 改为单级 t.CPtr,并同步该文件语法迁移
【类别 c 源码违规】clobbers 声明的指针层数与 alloc/memset 不一致。

现象:
  翻译 App/lib/core/Gen/Call/CallGenCBuiltin.vp 报
  「指针隐式转换已禁止(层数不同):不能用指针类型 i8* 初始化 clobbers: i8**」
  出错表达式 'pool.alloc(8 * ASM_MAX_CLOBBERS)'(Call 形态,CallGenCBuiltin.vp:955:33)。

判据(三条,缺一不可):
  1. memhub.MemBuddy.alloc(size: sizet) -> t.CPtr(includes/memhub.vp)即 i8* 单级;
  2. str | t.CPtr 是 i8**(CChar + PtrCount 2),即字符串指针数组视图;
  3. MemoryOps._ClassifyPtrImplicitConvert 判定顺序为**先比层数**,层数不同直接禁,
     通用指针通配(void*)不跨层。
  故 i8* 结果不能隐式初始化 i8** 变量 —— 与是否同名 str 无关。

取证补充:同函数下一行 string.memset(clobbers, 0, ...) 首参同样是 t.CPtr
  (includes/string.vp: memset(ptr: t.CPtr, value: int, num: sizet)),
  即便只给 clobbers 加 cast,memset 一样会挂 —— 必须把声明本身改回单级。

修法:clobbers 声明改回单级裸缓冲 t.CPtr,每槽按 8 字节字符串指针的写入沿用
  下方既有的显式 cast 槽视图写法
  (cl_ptr: str | t.CPtr = (str | t.CPtr)(cl_addr)),
  与 includes/_dict.vp 的「缓冲单级 t.CPtr、槽视图 str | t.CPtr 显式 cast」范式一致。
  此改**不引入新 cast、不绕过检查**,只是把声明修正到与真实分配/清零层数相符。

同类扫描:全 App + includes 内 str | t.CPtr = *.alloc(...) 形态仅此一处;
  includes/sys.vp 两处早已是显式 str*(...) 转换。

同时并入本文件他人已完成的语法迁移(同文件合并提交):
  - 常量声明 t.CDefine: T = 值 → define NAME = 值 语法糖(_CEXPORT_MAX /
    ASM_MAX_OPERANDS / ASM_MAX_CLOBBERS / ASM_TEXT_BUF_SIZE / ASM_CONST_BUF_SIZE /
    ASM_ARGS_BUF_SIZE / ASM_LINE_BUF_SIZE / LLVMIR_MAX_OPS / LLVMIR_TEMPLATE_BUF);
  - @t.NoVTable + class → novirtual class;
  - 删除常量块内已失效的注释与多余空行。

1 file changed, 17 insertions(+), 18 deletions(-)
2026-09-29 14:47:42 +08:00
TermiNexus 1f93550b6d revert(transpyv): 还原 *existing.Ty = <boxed 变体构造>(c.DerefAs 是绕过自家检查)
a9e4aaf 把 `*existing.Ty = llvmlite.LLVMType.Struct(...)` 改写成
c.DerefAs(existing.Ty, ...)。但 DerefAs ≡ *p = a(_HandleCDerefAsLlvm 的
「整对象拷贝」分支就是 load + store),二者语义完全等价 —— 换写法只是绕开了
*槽 = <boxed 变体> 触发的严格规则③,并未解决根因。

根因在编译器侧:「构造物化」豁免漏了 REnum 变体构造这一形态。已在 TPC 侧修好
(fix(tpc): 「构造物化」豁免覆盖 REnum 变体构造),本处源码随之还原为直白的
`*槽 = <boxed 变体>` 写法,不再用内建绕开严格检查。
2026-09-28 23:21:36 +08:00
TermiNexus a9e4aafae1 fix(transpyv): ClassGenDef 用 c.DerefAs 物化 REnum 变体(替代 *ptr = <boxed 构造>)
c 源码违规:`*existing.Ty = llvmlite.LLVMType.Struct(...)` 把 **boxed 变体指针**
赋给了值槽(指针→值,严格规则禁止)。REnum 变体构造返回 boxed 指针,仓库既定
写法是用 c.DerefAs(ptr, <boxed 变体>) 做整对象物化(includes/llvmlite/__types.vp:195
的同款 `c.DerefAs(ptr, LLVMType.Struct(fields, fcount, name))`),
_HandleCDerefAsLlvm 的"整对象拷贝"分支正是为此(HandlesExprCall.py:4718-4740)。

用 DerefAs 而非改成 `existing.Ty = <新指针>`:本段的目的就是修 Phase 1a 遗留的
**悬空 Name**(注释 L97-101),Phase A 已把该指针登记进 named-type 表;换指针会让
旧条目继续指向悬空 Name,in-place 覆盖才保留指针同一性、真正修掉悬空。

现场报错:非基本类型的值/指针隐式转换已禁止(构造物化除外):
  不能把指针类型 %"50741ad4b4b0934d.LLVMType"* 隐式转为值类型
  %"50741ad4b4b0934d.LLVMType"
2026-09-28 23:15:46 +08:00
TermiNexus fddadf9d56 fix(transpyv): 修正 _PyiClass 的 type_params 局部变量注解(list[ast.AST*]* → list[str]*)
c 源码违规(注解写错,被严格指针规则抓出):ClassDef.type_params 的真身是
rawptr[list[str]](**名字串**列表,见 includes/ast/stmts.vp:475-483 与
parser.vp:3619-3663 的 `list[str](ps.pool, 8)`);`list[rawptr[AST]]` 是
decorator_list 的类型。Converter._PyiClass 把局部变量注解从 decorator_list
抄错成 list[ast.AST*]* ⇒ `.get()` 被解析到 list[ast.AST*].get,而实参是
list[str]* ⇒ fail-fast:

  调用 650c1c3366d09e39.list[10f3f43ddd447eec.AST*].get 第 0 个实参
  %"650c1c3366d09e39.list[str]"* 不能隐式转为形参
  %"650c1c3366d09e39.list[10f3f43ddd447eec.AST*]"*

已核对全仓其余 8 处 type_params 用点(VpiReader.vp:2253/2274/4345、
Phase2/__init__.vp:317、Phase1Scan.vp:153/318、ClassGenDef.vp:69、
CallGenMixin.vp:415)均写作 list[str]*,本次与之对齐。
2026-09-28 22:54:50 +08:00
TermiNexus aa52cf157d revert(transpyv): 回滚跨模块链表 Next 改访问器(改由编译器沿继承链解析字段名)
回滚 13080f6 的 110 处访问器调用,恢复裸字段访问 `x.Next`。

理由:该批访问器是把访问移到定义模块内执行,**绕开**了"继承字段名解析",
属于规避而非修复;缺陷本体在 TPC 侧(导入侧继承展平把泛型基类的继承字段
记成 __inherited_N 占位名,名字丢失)。TPC 已按语义修正:
  · e6451a07 继承展平沿继承链取真实字段名(泛型特化回退模板名),
    展平表直接落 `[("Next", <槽位类型>), ...]`,属性访问是普通表命中;
  · 3ca46c01 顺带修掉 _get_or_create_struct 里"同名枚举成员遮蔽类定义"的 i32 出口。
因此源码侧恢复为唯一写法(裸字段访问),由编译器负责解析。

注:本 commit 只回滚访问器;13080f6 里夹带的 CallGenProp.vp 续行合并(非访问器改动)
已在工作区保留(未提交)。
2026-09-28 21:48:53 +08:00
TermiNexus 13080f646f fix(transpyv): 跨模块链表 Next 字段改用访问器(消除 __inherited_N 占位名 fail-fast)
c 源码违规:llvmlite 的 Value/ParamNode/Function/Param/GlobalVariable/PhiIncoming/
SwitchCase 的 Next 继承自泛型基类 GSListNode[T],而其泛型特化体不在任何 .vpi 中
⇒ 导入侧 class_members 只有 __inherited_N 占位名;严格属性解析(回退已移除)下
裸写 `x.Next` fail-fast。现场:CallGenCall.vp `cur_fb = cur_fb.Next` 报
"[fast-fail] 属性 Next 在类 ParamNode 中找不到(class_members=[__inherited_0, Ty])"。

按仓库既有约定(_Type.md:链表遍历用 paramnode_get_next/paramnode_set_next;
VarGenMixin/TypeSpec/StructGenMixin/VpiReader 等已如此书写)统一改为访问器:
  value_get_next/value_set_next、paramnode_get_next/paramnode_set_next、
  function_get_next、param_get_next、globalvariable_get_next、
  phiincoming_set_next、switchcase_set_next
覆盖 24 个文件共 110 处(含 CallGenProp.vp:122 的嵌套读 cur_arg_pc.Next)。

访问器在 includes 侧为定义模块内直通实现(None 守卫 + x.Next),语义等价、零行为
变化。已全量扫描 core\Gen 全 47 文件与 Stub/Phase/core/main:App 侧已无裸继承字段
访问(剩余 .Next 命中全部在注释里)。
2026-09-28 21:20:58 +08:00
TermiNexus 75ea6fb9b2 fix(transpyv): 消除隐式向下转型(ast.AST* 实参 → 派生 AST 形参)
c 源码违规(严格指针规则:基类指针传派生类形参属向下转型,须显式转换):

1) TranslatorGenMixin._prescan_funcdef_signatures:形参由 ast.FunctionDef* 改为
   ast.AST*,函数体内显式 `fd: ast.FunctionDef* = ast.FunctionDef*(node)`。
   理由:模块级调度器只持有 ast.AST* 子节点,同一处调用的
   translate_function_def / translate_class_def / _register_cexport_from_funcdef
   形参一律是 ast.AST*,本函数是唯一例外(报错现场:第 2 个实参 %"..AST"* 不能
   隐式转为形参 %"..FunctionDef"*)。改为与兄弟入口同口径,调用点无需改动。

2) CallGenDispatch len() 内置分支:_is_list_builtin_receiver 第 5 形参是
   ast.Call*(仅用于 D25 fail-fast 定位节点),实参 len_arg_node 声明为基类
   ast.AST* ⇒ 显式窄化 `len_arg_cl: ast.Call* = ast.Call*(len_arg_node)`。

附:对 App\lib 全量静态扫描(core\Gen 全 47 文件 + Stub/Phase/core/main)确认
此类「基类指针实参 → 派生形参」的隐式向下转型仅此两处,其余调用点均为同类型
或向上转型(规则允许)。

同文件另含仓库内既有书写整理(续行合并),无行为变化。
2026-09-28 20:20:56 +08:00
TermiNexus af5fba81c5 fix(transpyv): 严格指针规则下的源码级窄化与跨层指针写法修正
c 源码违规(按新严格指针规则的显式书写):

1) AssignGenMixin.vp 属性赋值:target 是基类 ast.AST*,而
   CallGenProp._try_property_setter 形参是 ast.Attribute*。基类指针传派生类形参属
   向下转型,严格指针规则下禁止 ⇒ 在 ASTKind 判据确定为 Attribute 后显式窄化
   `at_tg: ast.Attribute* = ast.Attribute*(target)`,并复用到 fd_an(不再重复窄化)。

2) TypeGenResolve.vp `return (str | t.CPtr)(v)`:`(str | t.CPtr)` 语义是 i8**(char**),
   而该函数声明返回 i8*,声明/返回层数不符 ⇒ 改为 `t.CPtr(v)`(i8* 显式转换)。

同文件另含仓库内既有书写统一(ast.OpKind.USub 全限定名、rawptr(...) 取代
(str | t.CPtr)(...)、空行/续行缩进整理),无行为变化。
2026-09-28 19:40:42 +08:00
TermiNexus 825fe6d326 fix(TransPyV): 指针 cast 的跨层 idiom 修正:(str | t.CPtr)(x) → rawptr(x)
c 类(源码违规):(str | t.CPtr)(x) 按既定语义 = i8**(char**,指针的指针),

而这些位置要的是 i8*。TPC 的 return/赋值检查现在按声明精确校验(本次即

TypeGenResolve.vp:1221 `return (str | t.CPtr)(v)` 声明返回 i8* 却 return i8** 被抳下),

该 idiom 在旧的无条件 bitcast 下一直不可见。

改法:86 处 (str | t.CPtr)(X) → rawptr(X)(rawptr 是 t.CPtr 的内置名,同为 i8*;

整数操作数走 inttoptr,指针操作数走 bitcast)。不用 str(X):整数操作数会编译成 itoa

(Gen/FuncGenMixin.vp:1159 注释已记录该陷阱),精确类型仍由使用处的 `: str` 标注承担。

保留 4 处本就该是 i8** 的用例(未改):Converter.vp:820(_g_pyi_cdef_names 声明为

str | t.CPtr 的指针数组)、CallGenCBuiltin.vp:1194/1215(声明即 str | t.CPtr)、

1411(先 cast 再 [0] 取首元素)。

另:Gen/Type/TypeGenResolve.vp:1221 手工改为 return t.CPtr(v)(该文件含你的未提交改动,未一并提交)。
2026-09-28 18:43:59 +08:00
TermiNexus d579d70113 fix(TransPyV): 修正形参/字段链节点的注解为 llvmlite.ParamNode*
c 类(源码违规):这些变量由 new_param_node 构造、或取自 get_struct_fields /

param_list_append / function_get_param_head,实际是 llvmlite.ParamNode 链节点,

注解却写成 llvmlite.Function*。旧的无条件指针 bitcast 掩盖了该错误,严格指针规则下暴露。

涉及:VarGenMixin / CallGenVt / VpiReader / ClassGenEnum / ClassGenVTable /

ExprGenName / FuncGenMixin / FuncGenDef / TypeGenResolve / TypeSpec /

StmtGenCifMixin / AssignGenMixin(纯注解替换,零语义变化)。15 行。
2026-09-28 17:54:13 +08:00
TermiNexus 70448f4590 fix(CallGenLeafKit): 闭包/函数指针调用的形参链表误注解 Function* → ParamNode*(Vipeye 600/814/818)
param_node/pn_tail/pn_new(600 的 Func 实参、593 的 pn_tail.Next)与 fp_first/fp_prev/pn2(814 paramnode_set_next、818 Func 实参)均为 ParamNode 链节点,原先误注解 llvmlite.Function*(不相关类)→ 严格指针规则下四处实参/字段赋值被判 struct↔struct 隐式转换禁止。改注解即根治。
2026-09-28 17:50:05 +08:00
TermiNexus 29acf97dbc fix(CallGenCBuiltin): 多输出 asm 返回 struct 的字段链表误注解 Function* → ParamNode*(Vipeye 1268)
out_pn_head/out_pn_last/pn5 与 new_param_node 的返回、Struct(fields: rawptr[ParamNode]) 形参、ParamNode.Next 语义一致,原先误注解 Function*(不相关类)⇒ out_pn_last.Next = pn5 / Struct(pool, out_pn_head, ...) 被判 struct↔struct 隐式转换禁止。改注解即根治。
2026-09-28 17:18:41 +08:00
TermiNexus eb1065f387 fix(CallGen): Function 的 Param 形参链与 llvmlite.Func 的 ParamNode 构造链不可互传(Vipeye 329/401/1065)
① 新增 CallGenBase._FuncTyFromFunction(pool, f_ref):Function.Params 是 Param 链(__function.vp),LLVMType.Func 的类型构造链是 ParamNode 链(__types.vp,new_param_node/paramnode_set_next),两者是不相关 class ⇒ 严格指针规则下 Func(pool, ret, p_head, cnt) 的实参传递直接报错。改为逐个 new_param_node 重建 ParamNode 链(语义等价,只取各参数类型)。CallGenCBuiltin 329/401 + ExprGenOps 878(第 3 处同型缺陷)三处统一走该 helper。

② CallGenCBuiltin 1065 asm-out 全局变量回退:原把 find_global_in_module 的 GlobalVariable* 直接赋给 llvmlite.Value* 变量(不相关类;且 GlobalVariable 字段序为 Name→Ty,Value 为 Ty→Name,旧无条件 bitcast 会让 .Ty 读到 Name,后续 alloca.Ty.Pointee 全是错位数据)⇒ 改走 c.Addr(模块全局) 分支同款写法 make_global_ref(pool, Name, Ty) 构造带引号的 Ptr(Ty) Value 引用,同时修掉该错位读取。
2026-09-28 17:16:57 +08:00
TermiNexus 23428b63a5 fix(CallGenCall): 虚表/函数类型参数链表节点误注解 Function* → ParamNode*(Vipeye 143/146/179/184)
includes/llvmlite/__types.vp: new_param_node -> rawptr[ParamNode]、paramnode_set_next/Struct/Func 的 params 形参均为 rawptr[ParamNode];本处 vt_first/vt_prev/pn/fp_first/fp_prev/fpn 误注解为 llvmlite.Function*(不相关类)⇒ paramnode_set_next / Struct / Func 三处实参被判 struct↔struct 隐式转换禁止。

修法:6 处注解改 llvmlite.ParamNode*(改类型即根治,不用强转)。
2026-09-28 17:04:57 +08:00
TermiNexus 4807012bd8 fix(CallGenBase): struct 字面量字段链表节点误注解 Function* → ParamNode*(Vipeye 922/927)
事实:includes/llvmlite/__types.vp 里 new_param_node -> rawptr[ParamNode]、paramnode_set_next(node: rawptr[ParamNode], nxt: rawptr[ParamNode])、Struct(fields: rawptr[ParamNode], ...),而本处 th_st/tt_st/en_st 被注解成 llvmlite.Function*(Function 与 ParamNode 是不相关类)⇒ 严格指针规则下这两处实参传递被判「struct↔struct 隐式转换已禁止」。

修法:把注解改成真实类型 llvmlite.ParamNode*(不需要、也不应该 t.CPtr 强转——变量本来就是 ParamNode,强转只是把类型错误重新掩盖)。
2026-09-28 17:02:35 +08:00
TermiNexus 9c427aa548 fix(CallGenCall): 拆开 vtable_base 类型推断的变量复用(LLVMType* 与 StructEntry* 不可同一变量)
严格指针规则下 struct↔struct 隐式转换已禁止(仅允许派生→基类上转),原写法把 get_struct_type_from_value 的返回(LLVMType*)赋给 vc_entry: StructEntry* 再转手交给 find_struct_by_type,靠的是旧的无条件 bitcast。

改为各用独立变量(vb_ty / sp_ty),语义与改前逐字一致(仅去掉两次隐式 bitcast)。
2026-09-28 16:54:25 +08:00
TermiNexus 3bed3a70a6 docs(len): len() 两条 fail-fast 报文措辞与 TPC 逐字对齐(行为零改动)
补上「只支持 定长数组/数组指针、i8*(str/bytes)、带 __len__ 的结构体」+「非 i8 裸指针没有长度语义」的正确做法提示,与 TPC HandlesExprBuiltin._LEN_SUPPORTED_HINT 一致,便于用户对照两侧报文。
2026-09-28 15:53:09 +08:00
TermiNexus be1acdb602 feat(cast): 放行数组退让([N]T* → T* 单向),与 TPC 同口径
新增 _is_array_decay(复用 is_array_type + LLVMType.Array 解构 + AssignGen._stg_ty_equal),在 _pointee_convert_reason 的非基本分支里紧接 _is_inheritance_upcast 之后调用

与 TPC 真值表一致:数组→元素指针 ALLOW、反向 DENY、多维只退一层;其余规则(通用指针/层数/完全相同/None/构造物化/标量无损/继承上转)未动
2026-09-28 15:38:42 +08:00
TermiNexus e2c4c7645e fix(cast): 补继承上转豁免(Derived*→Base* 放行),与 TPC 同口径
新增 _class_name_of_ptr_pointee(复用 StructGen.get_class_name_by_type)与 _is_inheritance_upcast(复用 StructGen.get_mro_chain:目标类名命中源类 MRO 链自身之后的祖先即放行,含多级)

在 _pointee_convert_reason 落入“非基本类型”分支前插入上转豁免;其余规则(通用指针通配/层数不同即禁/完全相同放行/None 任意/构造物化豁免/标量无损)未动;Base*→Derived* 与无关结构体仍禁

注:本提交顺带带入 App/main.vp 的既有版本号改动(0.0.5-dev.479,非本次会话产出)
2026-09-28 15:29:42 +08:00
TermiNexus f03aea15c7 fix(cast): 规则③两侧对齐——聚合值↔指针也上闸,仅豁免构造物化(p: Point = Point())
TPV 的 coerce_to_type 两条“聚合值↔指针”路径不再无条件放行:新增 _ctor_call_agg_ty(AST 层识别构造调用,经 _value_node_of 下沉 + find_struct 取聚合类型)与 _same_agg_ty(委托 _stg_ty_equal);非构造物化一律 fatal_error,报文前缀与 TPC 逐字一致

无损口径以本文件 _scalar_convert_reason 为真源(int↔int 全向、float↔float 全向、int→float 需目标浮点位宽≥源整数位宽、float→int 有损),未动

已知未覆盖:泛型构造(Box(42))静态拿不到特化 struct 名 ⇒ 不豁免;部分 coerce 调用点不传 node ⇒ 可能误报(需实机确认)
2026-09-28 15:17:37 +08:00
TermiNexus ee50ce61fe feat(cast): 指针隐式转换按“同级+被指类型无损”判定(通用指针 t.CPtr/i8* 通配),并修诊断错位
_ptr_cast_reason 统一判据:层数不同→禁;完全相同类型→放行;任一侧通用指针(i8*/t.CPtr)→放行;否则按被指类型:非基本类型→禁,基本标量按既有无损规则(int↔int、float↔float、int→float 需目标浮点位宽≥源整数位宽、float→int 有损)

4 处禁令与新增的“指针→指针”处统一走 _value_node_of/_cast_fail_where:外层 Assign(kind=3) 下沉到真正被转换的值节点;列号按有符号打印(-1/未知打 -,不再出现 4294967295);附源码片段

未动“聚合值↔指针”那条路径(构造返回指针 load 回结构体,如 p: Point = Point()):LLVM 层无法与真实 Foo*→Foo 区分,禁止会直接编不过;规则③在该轴上的收紧待 AST 层区分构造调用后另做
2026-09-28 15:10:30 +08:00
TermiNexus 59a9fc5b1f fix(cast): None 允许隐式转换为任意类型(修 p.fd_used[fd] = None 被“指针隐式转整数已禁止”拦下)
coerce_to_type 顶部新增单一短路判据 IsNoneValue(val, node):AST 为 CONST_NONE,或已物化的 null 常量(实机报错里 node 是外层 Assign kind=3、col=-1,拿不到 None 字面量节点,故以值判定为主)

指针目标保持既有 bitcast 形态(与 T* x = None 初始化一致);非指针目标按目标类型物化零值:整数 0 / 浮点 0.0 / 聚合与其余用 ConstZero

覆盖范围:App/** 下真正 fatal 的指针/整数禁令只有 coerce_to_type 内 4 处,均在短路之后;CallGenDispatch/ExprGenAttr/CallGenLib 等命中都是注释或不报错的原样返回,无遗漏

未动指针层级/被指类型的转换规则(待确认后另行实现)
2026-09-28 14:59:31 +08:00
TermiNexus dfae2f090c refactor(return): 移除 @c.CReturn 处理,改为遇到即 fatal_error(只保留 -> tuple[...])
FuncGenBase:整体删除 _resolve_creturn_ret_ty,新增 RejectCReturnDecorator(兼容 @c.CReturn(...) 与裸 @c.CReturn 两形态;命中即 TypeGenErr.fatal_error,报文指向 -> tuple[ty1, ty2, ...])

FuncGenDef:前向声明与定义两处调用点改为调用 RejectCReturnDecorator,删除随之失效的注释;周围类型解析主干(-> 注解、State/CExport、无注解 fail-fast)逐字未动

Converter:删除 _PyiCreturnArgs 与其“无 -> 注解则合成 -> tuple[...]”的调用点,改为 _PyiRejectCReturnDecorator(vpi 中仍带 @c.CReturn 即报错,提示用 --clean 重新生成该 .vpi)

注:TPV.vp.txt 是生成的单文件打包产物,需重新生成(本次未手改)
2026-09-28 13:57:05 +08:00
TermiNexus d06c4d2c0e refactor(main): 旧写法 a: static int 迁移为前置形式 static a: int
static 修饰符改为语句起始前置(TPC 与 includes 同步改动)后,本文件是 TPV 自身源码,必须一并迁移,否则新编译器编不过自己
2026-09-28 13:49:30 +08:00
TermiNexus f2d7b748ea fix(func): 末尾收尾的可达性判据只认 _ctrl_merge_reachable(删掉“穷尽通配臂即不可达”的错误推断)
症状:includes/llvmlite/__types.vp::_TypePrintDepth 末句 match ty:(各臂只 snprintf 后 br 到 merge)被生成 match.end: unreachable;llc 不给可达 unreachable 生成任何指令 ⇒ 执行掉进布局紧随其后的同一 switch 跳转表,用上一条 snprintf 的返回值 3 当 tag 取 table[3]=Array 臂 ⇒ 把 Int 的 Bits(32) 当指针递归解引用 ⇒ AV at 0x20(裸 0xC0000005;ASan 下同一崩溃被转为 0x80000003 = -2147483645)

根因:last_is_ctrl and (_ctrl_merge_reachable == 0 or MatchHasWildcardArm(...)) 的“追加92”通配臂子句——穷尽性只保证匹配总能命中某臂,不保证各臂都终止且不 br 到 merge;而 _ctrl_merge_reachable 恰是逐臂真正 br end_bb 时才置 1 的精确信号(MatchGenMixin:717 写回)

修法:两处收尾判据(FuncGenDef 模块级函数 / ClassGenVTable 方法)都只在 _ctrl_merge_reachable == 0 时 build_unreachable;merge 可达时走既有分支正常收尾(void→ret void;无注解→ConstZero;有非 void 注解→fail-fast,不发明返回值)

删除随之失效的 MatchHasWildcardArm 与其私有 helper _IsWildcardPattern(全仓已无引用)
2026-09-28 12:41:04 +08:00
TermiNexus 5e100e2195 fix(match): REnum 变体字段绑定不再静默跳过(__types.vp bits NameError)
根因:_handle_renum_match step6 用 renum_name 查 struct 且不校验 Ty;拿不到 entry/Ty 时 step7 的 _bind_variant_fields 被整段静默跳过 -> case 绑定名在 body 里未定义,最终只报 NameError(bits),真因藏到下游

1) 名字回退:变体表与 struct 注册都按裸类名,而 renum_name 来自注解 SourceName(Attribute 可带模块限定名)-> 精确查找失配;与 tag 查表同一裸名推导(tag_cls_bare)

2) 新增 StructGenMixin.find_struct_entry_with_ty:跳过 D30 重注册时就地中和的旧 entry(Name[0]=0/Ty=None/FieldCount=0),避免命中它拿到 Ty=None

3) 禁止静默:仍有 REnum 判据为真却无法绑定时 fail-fast,打印 class/查表名/entry/FieldCount/ty_present(对齐 D30/D32 的响亮失败口径)
2026-09-27 23:58:15 +08:00
TermiNexus a79e723e8d fix(dwarf): DIFile 改用绝对路径,栈转储显示完整路径并回读源码行
根因:CurrentFile 是项目相对路径(./Test/LLvmLiteTest/App/main.vp),LLVM 把该目录写成 CU 的 DW_AT_comp_dir,主文件条目用 dir_index: 0 指向 comp_dir;stackmap 写入端只解析 --debug-line(无 comp_dir)⇒ 行记录退化成裸名 main.vp,运行期既显示不全也无法按路径重读源码行

改法:DIFile filename 保留整条绝对路径(相对路径先经 w32file.GetCurrentDirectoryA 前置 CWD,与 Repl.vp 同款低层调用,不经 os._mbuddy);directory 另存目录前缀。无论 LLVM 保留整条 name 还是拆成 dir+basename,写端都得到绝对路径,故无需改写入端
2026-09-27 19:18:16 +08:00
TermiNexus bb864dff6f fix(traceback): stackmap 的文件名全部退化为 ?? —— _tb_parse_dec 不跳前导空格
dwarfdump 的方括号索引右对齐(file_names[  1] / include_directories[ 12]),_tb_bracket_idx 把 '  1]' 交给 _tb_parse_dec,首字符非数字使循环体一次不进 -> 全部 pidx=0;行记录 fidx=1..N 永不匹配 -> 每条记录落到 '??'(实证 9146/9146 条共用同一个 '??' 串,而函数名/行号均正确)

_tb_parse_dec 增跳前导空格与制表符(修复文件名与目录索引两处)

dir_index: 偏移 st+9 指向冒号本身,恒解析为 0 -> 相对文件名拼不出绝对路径;改为 st+10
2026-09-27 19:01:23 +08:00
TermiNexus 31898ab4a5 fix(stubmerger): define→declare 降级时剥掉行尾函数级元数据附加(!dbg !N)
llc 对 declare 不接受元数据附加,报 expected = here(实证 LLvmLiteTest 949685246c8e241c.ll:898:104 的 declare ... !dbg !71)

根因:降级只删尾部 ' {',源 define 头行的 ' !dbg !71' 被原样抄下;declare 无函数体,!dbg 无意义(FunctionPrint 对 declare 从不打印 !dbg)

判据:参数表之后、括号深度 0、不在双引号内的 ' !';连同前导空白一起截断
2026-09-27 18:30:39 +08:00
TermiNexus 66e697e8df feat(codegen): 模块级 extern 全局生成外部声明(@裸名 = external global T)
注解含 t.CExtern/t.State → is_global_extern:裸名(无 SHA1 头)、Linkage=external、不写初始值、不建 storage

与 export 全局(本模块定义 + 裸名)区分;配合 includes f854f7e 的 export/extern 变量语法
2026-09-27 17:56:51 +08:00
TermiNexus d82f24e17a fix(diag): AST 解析失败日志行内联 行/列 + 原因
四处解析失败上报点(Phase2 的 [Phase A-pre-vpi]、Phase1 的 [Phase 1b-pre] 与单文件、main.vp 单文件)此前只打 AST 解析失败,看不到定位与原因;现内联 第 N 行第 M 列: <原因>,与 dump_parse_traceback 的完整 traceback 同源(原因由 includes/ast/parser.vp 的 _record_* 与 parse_tokens 出口兜底补记提供)。另:App/main.vp 同时带上你工作区既有的 define 迁移/版本号改动。
2026-09-27 17:32:34 +08:00
TermiNexus e65f04f16b fix(codegen): 字段表↔槽位一致性护栏豁免联合体
union 的字段表有 N 条(每成员一条)但 LLVM 布局只有 1 个槽位(成员全偏移 0、按最大成员定宽),二者本不相等;字段访问走 bitcast 到字段类型后的 GEP,不依赖槽位下标。误报实证:EdgeCaseTest 的 class Data(t.CUnion)(i_val/f_val 两成员 → 1 槽)被护栏拦下。
2026-09-27 17:26:04 +08:00
TermiNexus 0279965074 fix(codegen): 指针强转基座 T*(x) 的字段读写补按需过滤类名通道
(ast.Call*(vinner)).IsVolatile 报属性写入失败:T*(x) 被 parser 的 _consume_ptr_suffix_call 解析成 (T | t.CPtr)(x) 强转调用,结果类型是联合简化后的 i8*,lookup_field(i8,...) 必失败,而按需过滤的类名提取只认 Name/Attribute。新增 _base_class_from_cast_base:从强转注解(func 是 BitOr 联合)取裸类名,读路径、写路径、属性链递归父节点三处接入。取裸名而非模块限定名——lookup_field_by_class 的模块限定分支对「包名可解析、类注册在子模块」的形态会 fail-fast。
2026-09-27 16:02:04 +08:00
TermiNexus beccb36b66 feat(codegen): 单目 & 支持对构造调用/函数调用结果取址
&ViperKernel() 此前被左值检查拒绝(只在 Name/Attribute/Subscript 放行)。构造调用 _translate_struct_ctor 返回的就是入口 alloca 的栈存储指针,取址即原样返回该指针;其它返回值落到入口临时槽 alloca+store。复用 translate_unaryop 已求值结果,避免构造被求值两遍(__before_init__/__init__ 跑两次)。
2026-09-27 15:15:59 +08:00
TermiNexus 4255ab9d32 fix(fstr): f-string 改回每站点栈缓冲 + 禁止逃逸,逃逸点改 stdlib 拷贝
ExprGenFstr._fstr_finalize:结果缓冲改回每站点栈 alloca(线程安全、零运行时依赖;每站点各建一个,站点之间不再共享),docstring 与文件头写明语义:f-string 结果是当前函数内的临时值。

GenApi 新增唯一判据 fail_if_fstring_escape 与唯一拷贝出口 escape_fstring_copy(stdlib.malloc + string.strcpy);逃逸位置接护栏:translate_return(函数返回值)、translate_assign(字段/元素/全局变量赋值)、handle_module_level_var(模块级 AnnAssign 初始化)。

编译器自身 7 处逃逸点(ExprGenBase._cast_fail_where 6 处、AssignGenMixin.const_assign_msg 1 处)改为 escape_fstring_copy 显式拷贝——正是造成 ASan stack-buffer-underflow in vsnprintf 的那条路径。

残留(已在 GenApi 头注释诚实记录):间接逃逸(把 f-string 传进会保存它的函数、或放进容器后带走容器)静态判不出;类字段默认值路径未接护栏。全仓自查:TransPyV/App、includes、ViperApps 均无 容器/字段/全局 存 f-string 的写法,也无残留 return f-string。
2026-09-27 14:45:28 +08:00
TermiNexus 72a3c483e4 fix(fstr): f-string 结果缓冲改为模块级全局,修 return f-string 的悬空栈指针
现象:--asan 下 ASan stack-buffer-underflow(READ size 1,地址落在栈左红区)in vsnprintf,栈为 ExprGenBase.coerce_to_type:409 的 f-string 报文;非 asan 构建表现为随机乱码或 0xC0000005。

根因:_fstr_finalize 用 _alloca_at_entry 在当前函数开一个 [FSTR_BUF_SIZE x i8] 栈缓冲并返回其指针;而 f-string 结果在全仓被大量 return 带出函数(ExprGenBase._cast_fail_where、AssignGenMixin.const_assign_msg、CallGenLib 报文构造等),返回后再作 %s 喂给 snprintf 即读已弹栈的帧;且同一函数内多个 f-string 共用同一个 entry alloca 会互相覆盖。

修法:结果缓冲改为每个 f-string 站点一个模块级可写全局 internal unnamed_addr global [N x i8] zeroinitializer,经 bitcast 取 i8*(与 ExprGenConst.create_global_string 同一构造路径,符号链接期解析)。被否决的中间方案记录在注释里:编译期池缓冲 + inttoptr 常量地址会把编译进程的宿主地址烘进 .ll,对产出程序无效,绝不可用。

已知取舍(注释中写明,与 C 的 static char buf[N] 惯用法同):同一站点反复求值且同时保留多个结果时后者覆盖前者;多线程避免共享同一站点结果。彻底消除需运行时字符串分配器(当前无)。
2026-09-27 14:33:54 +08:00
TermiNexus ca5473304f fix(gen): 结构体字段表与 LLVM 槽位一致性 fail-fast 护栏
新增 StructGenMixin.verify_field_slot_consistency:字段表条数必须等于结构体槽位数,且每条目 Ty 与 Index 槽位同类(指针性/整数位宽/浮点位宽);不一致即 fatal_error 并指名 类/字段/下标/两侧类型。由 lookup_field 第一遍调用。

背景(真实崩溃):模块 8a96a2885bae09ca(ExprGenOps)把 ast.Name.id 编译成 GEP 下标 9(该槽是 ctx,i32),同一次构建里 320b55907b889d59(AssignGenMixin)用下标 8(i8*),两者共用同一 %struct 布局。后果:nm.id 读成 ctx(ASTCtx.Store=1)- inttoptr i32 1 - strlen(0x1) - ASan access-violation at 0x1。
2026-09-27 14:00:18 +08:00
TermiNexus 12bd7b7a16 fix(gen): 收敛 static 跨模块拦截为独立方法,消除类型推断污染
新增五个独立方法承载 static 检查与登记:_CheckStaticCrossModule(mod.name 取值)、_CheckStaticCallCrossModule(mod.fn 调用)、_CheckStaticFromImport(from-import)、_RegisterStaticModuleVar(模块级变量登记)、_RegisterStaticFunc(函数登记)。调用点只保留一行调用——内联新增分支会改变超大函数的类型推断,曾导致 translate_attribute 后续 match obj_ptr.Ty 报指针隐式转整数已禁止。

删除 _resolve_module_attribute_global 内与入口检查重复的 static 拦截(其两个调用点均在 _CheckStaticCrossModule 之后)。
2026-09-27 13:12:22 +08:00
TermiNexus dd56580855 feat(gen,static): static 符号禁止跨模块使用(定义侧登记 + 引用侧三处拦截)
语义 = C 的 internal linkage:static 符号(函数/变量)只在定义模块内可见,任何跨模块引用一律 fail-fast(NameError)。

- 登记表:CallGenBase 新增 StaticSha1Arr/StaticNameArr/StaticCount(PermPool 定长槽,与 CexportSha1Arr 同款)+ RegisterStaticSymbol/IsStaticSymbol(按 (符号名, 模块 SHA1) 精确配对,避免跨模块同名误判)。

- 定义侧登记:FuncGenDef 识别注解形态(-> t.CVoid | t.CStatic)与装饰器形态(@t.CStatic;裸 static def 已由解析层归一为装饰器)→ 登记函数名;TranslatorGenMixin.handle_module_level_var 识别注解形态(static x: int 已归一为 t.CStatic | int)→ 登记变量名。

- 引用侧拦截(三处,覆盖全部跨模块形态):ExprGenAttr.translate_attribute 入口统一拦截 mod.name(含 import 别名还原);CallGenMixin.translate_call 入口拦截 mod.fn(...) 调用(按 AST 形态一次覆盖 found_func/vpi/裸名兜底各分支);ImportsGenMixin.translate_import_from_names 拦截 from mod import name。

- 性能:三处检查均以 StaticCount > 0 快速短路——项目中无 static 符号时零开销(不触碰逐条线性扫描与 sha1 解析)。

- 同模块自引用(sha1 相同)与非模块基座(self.method / obj.field / Class.static_method)一律放行。
2026-09-27 12:58:51 +08:00
TermiNexus 424dbd7379 chore(gen): 加 [MODSHA1] 临时诊断定位 string.split 模块名解析失败(定位后删除)
在 translate_call 的模块限定分支加两条探针:

- [MODSHA1]:mod_ret_sha1 为 None 时打印 mod_path_ret / func_name / exact / suffix / ctrl_stdint(对照模块,必在 includes)——可区分 store 整体为空 与 store 有内容但缺该模块。

- [MODSHA1-D]:裸名兜底分支里,若 cl.func 仍是 Attribute(模块限定却解析不出模块的异常组合)则打印 mod_path_ret 与 func_name——覆盖 mod_path_ret 为 None 的情形。

同时把 mod_path_ret 的声明从 if 块内移到 if 外(与 mod_ret_sha1 一致),避免下游分支读未声明局部取到栈垃圾。

证据链:报错来自本函数 861 行(全仓唯一 fatal_if_unknown=1 的调用点),条件恒为 _resolve_module_sha1 对 string 返回 None;但 _sha1_map.txt 有 includes/string.vp、其 vpi 有 def split -> int、Phase1Main:390 是唯一写 map 处 ⇒ 静态无法解释,需运行时诊断。
2026-09-27 12:52:03 +08:00
TermiNexus 87f5b1026c chore(gen): 删除 CallGenDispatch 的 [MDISP] 临时探针及其专用 import stdio
该探针(并排取证 __with_ctx.__enter__ 与 pool.alloc 的 H6 判据命中差异)为定位 MpoolOopTest 的「跨模块调用 pool.alloc 找不到模块 SHA1」临时加入,现缺陷已定位,探针与其唯一使用者 import stdio 一并移除;非 local 对象分派逻辑不变。
2026-09-27 12:32:52 +08:00
TermiNexus 9455db0eec feat(gen,type): callable[] / callable[[]] / callable[[A,B],R] 与 t.Callable 逐点等价
- 判据单一真源:_SubscriptAttrIsCallable 扩为 _IsCallableSubscriptValue,同时认 Attribute(attr=Callable) 与裸名 Name(callable | Callable)。

- Subscript 分支在 Name 路径(泛型特化 / CArray / builtins typedef 兜底)之前拦截裸名 callable/Callable → i8*;否则 callable[] 会先撞 slice=None 的 fail-fast、callable[[]] 会被当泛型类名查空。

- TypeGenErr.try_extract_callable_ret 改用同一判据:callable[[A, B], R] 现在也会注册返回类型 R(可间接调用),无签名形态([]、[[]])仍返回 None。
2026-09-27 12:30:47 +08:00
TermiNexus a76cf55a2d feat(gen,type): 注解位置的小写裸名 callable 等价 t.Callable(i8*)
支持 func: callable = 函数名 的写法:Name 分支兜底 map_t_type 命中 callable → Ptr(Int8),与 t.Callable / t.Callable[] / t.Callable[[]] 同一 LLVM 表示(无签名函数指针,只取地址不作间接调用)。

同时明确大写裸名 Callable 同时服务 from t import Callable 与 Attribute 分支的 t.Callable。
2026-09-27 12:28:58 +08:00
TermiNexus c387926977 feat(gen,type): 支持无签名函数指针 t.Callable / t.Callable[] / t.Callable[[]]
三种写法统一解析为 i8*(与带完整签名的 t.Callable[[A,B], Ret] 同表示;函数值按 TPV 惯例 bitcast 为 i8* 存储):

- map_t_type 新增 Callable → Ptr(Int8):裸 t.Callable 此前落空导致 AnnAssign 注解类型解析失败。

- resolve_annotation_type 的 Subscript 分支:slice 为 None 时先放行 Callable(t.Callable[] 走空下标解析),其它类型仍 fail-fast 缺少 slice。

- 新增 _SubscriptAttrIsCallable 判据。

无签名形态不注册返回类型(try_extract_callable_ret 对非 Tuple slice 返回 None)⇒ 只适用于取地址/内联汇编操作数,不作编译器间接调用。
2026-09-27 12:25:52 +08:00
TermiNexus 7c53a8744d fix(gen,match): 穷尽 match 不再误报流程掉出末尾;default 臂直译不再经 def_arr 反查
现象:函数末句为 match 且含 case _: 穷尽臂、各臂均 return 时,仍报 TypeError 函数流程掉出函数末尾(ioctl 4 具体臂 + case _)。

- FuncGenDef / ClassGenVTable:收尾判据由「_ctrl_merge_reachable == 0」扩为「或末句 match 含无 guard 的 case _ 通配臂」。穷尽 match 不存在全臂不命中的路径,与 If/While 全分支 return 同语义,应生成 unreachable 而非 fail-fast。

- MatchGenMixin 新增 MatchHasWildcardArm / _IsWildcardPattern:识别 MatchAs(pattern=None)(含 as/or 组合),带 guard 的 case _ if c 不算穷尽。

- MatchGenMixin:_handle_simple_match / _handle_renum_match / _handle_sequence_match 三条路径改为 pass1 显式记录 default_case_node,default 块直译该节点;原实现经 def_arr[di] 反查 cases_list[di],collected 下标与原始下标在「有臂被跳过」时错位 → default_bb 空转 → merge 误判可达(同时 case _ 臂不生成 IR)。
2026-09-27 12:17:14 +08:00
TermiNexus cf815b17de feat(gen): volatile 构造写入落 LLVM volatile(逐字段 store + 构造三方法屏障)
值位置 volatile 前缀(a: int = volatile f() / a: S = volatile S(...))此前只在调用点无落点。LLVM 的 call 指令没有 volatile 限定符,故语义只能落在内存存取上:

- AssignGenMixin:赋值目标的结果槽按 volatile 存取(本次 store 立即带 volatile,并登记 VarEntry.IsVolatile 使后续 load/store 亦带);对已存在的 global/nonlocal/mod_glob 条目在分支前统一登记。

- CallGenCtor:_translate_struct_ctor / _translate_struct_ctor_kw / _translate_renum_construct / _apply_struct_defaults 增加 is_volatile 参数,清零、tag、默认值、逐字段 store 全部带 volatile;_translate_generic_ctor_call 直接从 cl.IsVolatile 取。

- CallGenCall._call_method_on_ptr 增加 is_volatile 参数:__new__/__before_init__/__init__ 的写入发生在被调函数内部,调用点无法逐条附加 volatile,故在调用后对该 self 指针追加一次 i8 volatile load 作为内存屏障,使构造写入不被优化器消除或跨越重排。

- CallGenMixin / CallGenDispatch:结构体、关键字、泛型、REnum 构造调用点统一透传 cl.IsVolatile。
2026-09-27 12:01:22 +08:00
TermiNexus 7d28c9649e feat(gen,err): CastError 定位到出错表达式;vpi 生成 fail-fast;函数属性接受直接形态
- ExprGenBase:新增 _cast_fail_where,指针/整数隐式转换失败时打印出错表达式与列号(多实参调用可定位到具体实参,如 Name 'toggle_ptr')

- Converter:import / from-import 别名列表为空、Alias 指针为空、name 为空分别 fail-fast,不再写出 import ,  这类残缺 vpi 行(该残缺会让包重导出注册失效,报出与根因无关的 NameError)

- FuncGenBase.extract_func_attrs:接受直接 @t.attr.<name> 装饰器形态(与类侧 _has_decorator 同口径),使 inline def 在无 import c 的文件里也生效

- 附:CallGenDispatch 内保留一处临时 [MDISP] 探针(MpoolOopTest pool.alloc 定位用)
2026-09-27 11:41:11 +08:00
TermiNexus f084da7c01 feat(gen): 重复声明改为无条件失败(不再比较类型),global 后再注解同样报错
- 判据改为:模块级已有同名变量 + 函数内再次用注解声明同名变量 ⇒ TypeError 重复声明,不再比较注解类型是否与全局一致(此前仅在类型不一致时报错)

- 有 global/nonlocal 声明后再带注解声明同名变量也报错(注解与既有全局声明重复)

- 检查点前移到注解解析之前(紧跟 REPL 分支),保证优先于 coerce_to_type 报错

- 合法写法只剩两种:global + 无注解赋值修改全局;或换不重名的局部变量
2026-09-27 08:55:08 +08:00
TermiNexus 78fc7313fb feat(gen): 同名全局 + 注解冲突优先报重复定义
- translate_annassign: 函数内(无 global/nonlocal、非模块级)再次注解同名变量时,若注解类型与模块级变量类型内容不一致 ⇒ TypeError 重复定义 fail-fast,不再延后到 RHS 的 coerce_to_type 报无关的「指针隐式转整数」(PointerTest/App/main.vp:46 _g_ptr: int = None,全局已是 t.CUInt32T | t.CPtr)

- 注解与全局一致时仍按 Python 语义遮蔽为独立局部变量(既有行为不变)

- 该检查走 _stg_ty_equal 内容比较(TypePrint),非地址比较,避免 Int32()/Ptr() 新实例造成误判
2026-09-27 08:50:18 +08:00
TermiNexus f0309e4904 fix(gen,renum): 指针操作符目标写入落盘 + REnum tag 三侧口径统一,修 kind() CastError
- AssignGenMixin: 补 UnaryOp(Deref) 赋值目标分支(*p = v / *&x = *&y),非 Deref 的 &x/-x/~x fail-fast;*p += v 真正实现;Tuple/List 目标合并

- CallGenDispatch: kind() 改四级 REnum 判据(变体表 / struct __tag / pointee 带 __tag / X.field 声明类型反推);修正 build_load 第一参数误传指针类型 rk_tag_p(生成 load i32*, i32* 使返回值 Ty 为 i32*,导致 CallGenLib.vp:355 vk_a: int = val.Ty.kind() 报指针隐式转整数的 CastError),rk_load 分支同步改 rk_ty.Pointee

- CallGenCtor / MatchGenMixin: __tag 读/写/比统一取字段声明类型 FieldEntry.Ty

- Test/FieldReadTest: 字段读取探针
2026-09-27 00:19:44 +08:00
TermiNexus bcf66edebe fix(vpi): 防环访问表表满不再静默丢标记,改为 ×2 扩容 + fail-fast
表满时曾静默 return(丢标记)⇒ 防环失效 ⇒ 递归闭包反复重解析同一批 .vpi,每个 vpi 的 Lexer/Token/AST 都从池分配且本段不回收 ⇒ 单次 translate 吃数百 MB ⇒ 1 GiB 主池 OOM

改为可扩容(ResetVpiTypedefVisited 同步复位容量),分配/扩容失败立即报错终止
2026-09-26 21:25:23 +08:00
TermiNexus 373a20c3e5 perf(prof,gen): 编译热点优化 + 内存池 OOM 诊断接线 + REnum 指针载荷修正
CallGenBase: 模块返回类型文本表改单趟登记+查找,find_func_in_module 合并为单趟链表遍历(PROF 热点)

BuildPipeline: ensure_dir 增加快速路径(已存在目录直接返回)

AssignGenMixin/Translator: alloc 失败打印 Pool/PermPool 统计与阶段上下文(SetAllocCtx)

CallGenCtor: REnum 指针载荷补显式 build_ptrtoint 再 coerce/store;新增 DebugInfoGen.vp

其余为历史累积改动一并入库
2026-09-26 19:27:23 +08:00
TermiNexus 676c19c362 feat(asm,err,typedef): 支持 __asm__/__llvmir__ 裸名与 typedef 语法糖;解析报错统一 Python 风格 traceback
CallGenCBuiltin/CallGenDispatch: __asm__/__llvmir__ 裸名调用,asminp/asmout/irinp/irout 与裸 ASM_DESCR。

AssignGenMixin: 支持 define NAME: T = V 形式的 CDefine 浮点值恢复;负浮点(-1e308)剥一层 UnaryOp(USub)。

Phase1Main/Phase2: AST 解析失败改调 GenApi.dump_parse_traceback(Traceback + File/行 + 源码行 + caret)。

TypeGenResolve/TypeGenCType: typedef 文本扫描统一支持 typedef 语法糖(builtins/库/单文件三处共用同一解析)。
2026-09-25 13:11:16 +08:00
TermiNexus a7af34bd7e 修正了部分代码的表达方式,最后可用版本,mlirlite 已基本完善 2026-09-23 17:41:49 +08:00
TermiNexus 951b89be36 修正了部分代码的表达方式,最后可用版本,正在为 mlirlite 做准备 2026-09-23 16:09:32 +08:00
TermiNexus a5ceb3d4eb 增加 JIT REPL 2026-09-21 16:17:19 +08:00
TermiNexus 9910e05760 修复了一些错误,更改了一点语法 2026-09-18 20:00:40 +08:00
TermiNexus 9e2ccbfab7 将部分 | t.CPtr 修正为 rawptr[T] 2026-09-18 17:56:24 +08:00
TermiNexus eb01a45024 修复了一些问题 2026-09-17 14:51:18 +08:00
TermiNexus cb0836de8a 清理了 t.CVoid 2026-09-16 16:06:48 +08:00
TermiNexus 9a191daa8d 错拼 rowptr 改为 rawptr 2026-09-16 11:42:28 +08:00
TermiNexus 7cba4cbc95 chore: 清理超长历史注释,并一并提交并行会话的代码改动
注释清理(65 文件 / 约 -2578 行 / 约 516 处):删除/压缩叙述式历史注释——多行根因叙事、工作代号段(D25/D30/N11/B5/S4/阶段E 等)、改前改后与先例叙述、逐条推导块,压成一行讲清作用信息;契约类注释(用途、参数与返回、关键约束与不变量、IR 结构说明)与被注释掉的代码一律保留

存疑 标记 6 处一字未动:StubMergerStore.vp、VpiReader.vp、VLogger.vp、AssignGenMixin.vp、CallGenLib.vp、CallGenLeafKit.vp

本次一并带入并行会话的代码改动(同批文件,按文件粒度无法拆分):fileio.File -> rowptr[fileio.File] 等

已知副作用/残留:① CallGenDispatch.vp 有 2 行非注释行的行尾被 LF->CRLF 规范化(纯字节变化,无文本改动);② core/Gen 区约 24 处压缩因并行会话整文件覆写被写回原样,尚未生效(StmtGenCifMixin/ClassGenVTable/FuncGenDef/ClassGenEnum/StructGenMixin/TypeSpec 等)

清单与复跑判据见 Debugger/comment_cleanup_summary.md(该目录不受版本控制)
2026-09-16 11:27:05 +08:00
TermiNexus b28f20cce7 chore: 清理调试打印与失效 import
CallGenCall.vp: 前次清理 DBG 打印后 import stdio 已无使用者,删除(该文件全文无 stdio. 调用,亦无经本模块的外部访问)

Test/App/nested_match_test.vp: 删除 _init_mbuddy 与 test_single_match 中的 8 处 [DBG] 进度打印;该测试的判定只看 --- Test N --- / OK / FAIL 行,test_main.vp 亦只取返回码,无断言引用这些文本

未改动任何逻辑(保留 FAIL: malloc for arena failed 等错误分支的真实输出)
2026-09-16 10:32:40 +08:00
TermiNexus 429de97083 fix(call): 泛型特化类跨模块 declare 按模板 vpi 注解对齐形参口径
特化名({sha}.list[str].__init__)在 vpi 参数注解缓存中无精确键——键段取自 class 行文本,只存模板 list[T]——定义侧形参注解取不到,调用点 declare 停在调用点实参口径(elem_size 字面量 i32 vs 定义侧 t.CSizeT=i64;池实参 MemBuddy*/i8* vs 定义侧 MemManager*),并经 .stub.ll 传播到未调用该函数的消费方模块

新增 VpiReader._resolve_vpi_param_annot_seg_template:按 {sha1}.{base}[T].{method} 定位模板条目、用特化实参替换段内类型形参 token 后再解析,未命中返回 None 不兜底;_coerce_args_to_def_params 在精确键未命中且类名含 [ 时改走模板退查并置 tpl_src,使 _coerce_arg_value_to_ty 对同为具体结构体指针但基类型不同补 bitcast;_call_method_on_ptr 的跨模块分支补建 declare 前的 coerce(先例 a83d183 的三处调用点)

同时把 {sha}.{cls}.{method} 的切分由首个 . 改为末个 .(CallGenLib/CallGenBase 两处):method 不含 .,首个 . 会把含模块限定名的类型实参截断(list[3571eabe5a83d798.AST|t.CPtr] 截成无 ] 的类名 ⇒ 模板退查必 miss)

另修 VpiReader 一处 double-free:_subst_template_arg 早退分支返回入参本身,替换前释放原缓冲需先判同一性(三条路径各释放一次)

效果: 危险级签名不一致 TransPyV 8->4->0、Test/AstTest 6->2->0,ZlibTest/WebTest/FakeDuckTest/ListTest/GenericTest2/VectorGenericTest 保持 0;4 个目标符号 define/declare 共 148 处全部统一为 MemManager*+i64(0 处旧口径);自举达全树逐字节不动点(exe sha256 相同、temp/** 635 文件零差异);AstTest 及既有测试全 PASS 且无 ASan 报告
2026-09-16 09:31:57 +08:00
TermiNexus a83d183abb fix(gen/call): 跨模块调用 SSA 整型实参按定义侧形参扩宽
declare 由调用点实参链建立,而 _coerce_arg_value_to_ty 对非常量(SSA)整型此前原样返回,导致 declare/call 停在调用点窄宽度(string.strncpy 第三参 i32),与定义侧(vpi 注解 n: t.CSizeT => i64)不一致,并经 .stub.ll 传播到未调用该函数的消费方模块

现按源符号性 zext/sext 扩宽(真源 ExprGenBase._zext_or_sext_val,先例 CallGenCtor._translate_t_type_cast),实参 AST/trans 下传;self_off 模块级 0、方法 1

效果: WebTest 危险级签名不一致 1 => 0
2026-09-15 23:58:40 +08:00
TermiNexus 31b8dccffd fix(stub): declare 侧识别 t.CPtr[...] 段,与定义侧指针层级口径对齐
t.CUInt8T | t.CPtr[t.CPtr] 在定义侧(TypeGenResolve._is_cptr_subscript)按指针层级合并得 i8**,但声明侧段判据只认整段等于 t.CPtr/CPtr,下标段落入普通段解析后取首段 => i8,造成跨模块 define/declare 签名不一致(实参 1 字节 vs 写入 8 字节)

新增 t.CPtr[/c.CPtr[ 段的识别与递归解析(与同文件 rowptr[...] 分支同构),数外层 Ptr 层数 N 后按 Ptr^N 合并;裸 t.CPtr 段路径未动,未放宽 fail-fast

注: TPC 侧 LLVMTypeMapper._resolve_bitor 与 SymbolUtils.IsCPtrNode 同源缺陷未改,两套编译器需同时修以免产物分叉
2026-09-15 23:58:39 +08:00
TermiNexus de86a1ed70 fix(gen/for): 负数字面量 stop 的 range 循环退回有符号谓词,修复 _find_provider 死循环
stop 边界为负数字面量(如 range(top - 1, -1, -1))时,3.6 的边界推导把无符号 start(UINT64 字段)判为无符号循环,负步长分支随即由 sgt 覆盖为 ugt;而比较在 stop 类型(i32)上进行,-1 在无符号域是 UINT32_MAX,条件恒假 => 循环体成为死代码。_withcontent._find_provider 因此恒返回 NULL,with 上下文的 __mbuddy__ 注入永不生效(FakeDuckTest Test 9 FAIL,随后 _str.upper 解引用 NULL vtable 触发 ASan READ)

新增 3.7: stop 为负数字面量时撤销无符号谓词(判定与既有 step_neg_const 同口径),退回 slt/sgt;正步长非负 stop 的既有 ult 优化与 range(stop) 单参形态不受影响
2026-09-15 22:45:28 +08:00
TermiNexus 296f6a739e fix(gen/renum): REnum 补齐 Assign 形式无载荷变体的变体表注册
根因: _translate_renum_def / _register_renum_variants 只认嵌套 ClassDef 变体,Empty = 1 / None_ = 0 这类无载荷 Assign 变体既不计入 variant_count 也不写变体表 => match 查不到 tag(-1) => 本会话硬校验 fail-fast,此前则静默丢 case(Test\EdgeCaseTest 旧产物 ac0d686cb2cc783b.text.ll:117-119 仅 1 个 case,臂体 match.case.11.1 结构性不可达)

修复: 按 TPC 口径(HandlesClassDef._EmitREnumLlvm / SymbolExtractor)两遍处理——先取 Assign 显式值上界,ClassDef 变体从 max(显式值)+1 起编号(避免与显式值撞号),Assign 变体 tag = 显式整数值;表槽号与 tag 值解耦。纯 ClassDef REnum(含 LLVMType)生成结果不变。硬校验保留不动
2026-09-15 22:45:28 +08:00
TermiNexus 617d4ea911 fix(gen/assign): 模块级与 nonlocal 分支补写赋值注解上下文通道
模块级 AnnAssign 的 mod_glob 分支与 global 语句分支、以及 nonlocal 分支此前均未写入 trans.CurAssignAnnot/CurAssignNode,导致模块级/闭包内 s: Stack[int] = Stack() 在泛型裸名构造处 fail-fast、x: int = c.Deref(p) 退化为 i8 单字节读取

按主分支既有先例(AssignGenMixin set 在 RHS 翻译前 / clear 在返回前)在三处分支补齐同款 set/clear,并覆盖全部分支的提前 return 与末尾出口,避免通道残留污染后续 c.Deref 读取宽度与泛型构造类型实参推断

影响面: 全仓 模块级/global/nonlocal 的 X: T = c.Deref(...) 与 X: Generic[...] = Ctor(...) 均 0 处,属纯增量
2026-09-15 21:52:42 +08:00
TermiNexus 5a59432733 fix(gen): 泛型裸名构造补齐注解上下文与关键字实参两条推断来源
- 赋值目标/返回注解上下文作为最后一级类型实参来源(s: Stack[int] = Stack()、-> Stack[int] 里 return Stack()),注解解析复用 _extract_type_args_from_slice,以 CurAssignNode/CurRetNode identity 限定直接值避免嵌套误用

- 仅关键字实参构造按 __init__ 形参名定位槽位(Box(v=42)、Pair(b=string, a=1) 乱序),泛型构造码生成不再静默丢弃 cl.keywords(此前会生成少一个实参的错误调用);形参名查无/槽位空洞 fail-fast

- 抽取共用 __init__ 形参遍历(_get_ctor_init_params),位置对齐语义保持不变
2026-09-15 21:52:41 +08:00
TermiNexus 6394ea30e2 fix(call): 泛型类裸名构造按 __init__ 签名对齐类型实参并禁止静默默认 int
- 删除按序号消费「过滤纯 int 的 hint 列表」的压缩逻辑(整型实参被剔除后 hint 下标与形参序号错位,Pair(1,字符串) 被误判为 Pair<str,str>),改为取 __init__ 中注解为该类型形参的形参位次所对应的位置实参推断(Vector[T].__init__(self,pool,capacity,_hint:T) => T 对齐第 3 个实参)

- _infer_generic_ctor_type_arg 对 Name/Attribute 实参改取真实类型(变量注解类名/字段注解类名),取不到即 fail-fast,不再回退 int
2026-09-15 21:33:54 +08:00
TermiNexus b27633c86e fix(Gen/Type): long/unsigned long 按目标平台取宽,Windows 对齐 TPC LLP64(ULONG/LONG => i32)
- map_t_type 对 CLong/CUnsignedLong 原为硬编码 Int64(64 位平台假设),导致 Windows 目标下 ULONG/LONG 出成 i64:83 个 Win32/posix 声明的 long 形参宽度错误,且 CRITICAL_SECTION 等结构体布局为 48B(正确 40B,真实 Win64 ABI)

- 新增 _long_llvm_ty:target triple 含 windows/win32/mingw/msvc => Int32,其余(LP64/裸机 ELF)保持 Int64;规则对齐 TPC lib/includes/t.py configure_platform;triple 嗅探写法对齐 StmtGenCifMixin.vp:95-109

- 定义侧与 stub/declare 侧共用同一 map_t_type,单点改动两侧同变;未改 includes/stdint.vp(ULONG=t.CUnsignedLong 的意图正确,错在宽度)
2026-09-15 20:56:52 +08:00
TermiNexus 813f0157e3 fix(Gen/Expr): 类名不再被当模块级全局变量制造引用,消除 ASan 构建 undefined symbol
ExprGenAttr._resolve_module_attribute_global 在未命中已注入全局时会新建 @<sha>.ClassName = external global i8* 并 load;该 load 无使用者,非 ASan 构建被 llc 删除,ASan 插桩后其全局地址参与 shadow 检查无法删除 => ld.lld: undefined symbol 481fd55266ff1187.LLVMType (.refptr)。若属性名是已注册类/结构体/REnum(StructGen.find_struct 命中)则直接返回 None,交给类/REnum 路径。
2026-09-15 20:47:15 +08:00
TermiNexus 592400a1ea fix(asan): 并行 llc 任务在 ASan 模式下改用插桩后的 .asan.ll 作输入
Phase B 的 init_llc_task 先前用未插桩的 ir_path 拼 LlcCmd,opt 产出的 .asan.ll 只写进 OptCmd,worker 先跑 opt 再跑 llc 却读回原 .ll,导致 App 侧 76/76 个 obj 无 __asan 符号(ASan 内存排查半盲)。现将 llc 输入统一为 compile_input,与串行路径 compile_ll_to_obj 及 TransPyC 参考实现同口径;非 ASan 分支命令逐字不变。
2026-09-15 20:47:14 +08:00
TermiNexus 8312e653e8 fix(CodeGen): 裸结构体形参按 C ABI 按值、修正 |t.CPtr 指针层级,对齐 TPC 并消除签名发散
- FuncGenDef/VpiReader._AggregateParamTy: C ABI 外部声明(t.CExtern/t.State)的裸结构体形参按值 %Struct(可取完整定义时),真 opaque 降级 i64;TPV 内部函数/方法维持 Ptr(struct)。三处(定义侧/函数注入侧/方法注入侧)同口径

- TypeGenResolve/VpiReader: BinOp(A|t.CPtr) 的指针别名守卫改为 pointee 为已注册 struct 才吸收,否则追加一层 => VOIDPTR|t.CPtr 由 i8* 修正为 i8**(对齐 TPC)

- 效果: SetConsoleScreenBufferSize 等 6 个符号由 i64/%COORD* 双形态收敛为按值,同符号多形态数 193->186
2026-09-15 20:47:14 +08:00
TermiNexus 31df298341 fix(gen): 修 REnum match case 静默丢弃与 CDefine 负 64 位常量截断,补 void 注解放行、移植类型定义前移
- MatchGenMixin: 2a 路径查变体 tag 改用裸类名(注解书写形态带模块前缀,与变体表裸名注册口径不一致致 tag=-1、case 被静默丢弃、match 全走 default),并在 build_switch 前硬校验 应提交 case 数==sw_count,不再静默丢

- ExprGenCdef: _const_int_for_cdefine 补 value < -2147483648 下限判据,修正 (0xA5A5A5A5<<32)|0xA5A5A5A5 等负 64 位模式被物化为 i32(写入 inttoptr 零扩展 vs 比较 sext 不一致,致 MemBuddy canary 假阳性 rc=6)

- FuncGenDef/ClassGenVTable: 掉尾检查新增注解级 void 判据(t.CVoid/裸名 CVoid/None),不再因 REnum match 退化误判 void 函数缺 return

- StubMergerBuild: 移植 TPC 的 类型定义前移 遍,满足 llc 对 by-value 具名类型需先定义的要求(invalid type for function argument)

- main.vp/Phase1Main.vp/project.vpj: 新增 --detection-deep 深诊断开关与 canary 哨兵、ASan 静态库声明
2026-09-15 19:11:54 +08:00
TermiNexus 60100fa6af fix(type): 联合类型 A|t.CPtr 判据由白名单改为解析成功即保留,stub 侧同步
定义侧 TypeGenResolve.resolve_annotation_type 的 BinOp(A | t.CPtr) 分支:裸名
pointee 的保留判据由“硬编码名字白名单 + 必须是已注册 struct”改为“以解析是否
成功为准”——命中统一口径 resolve_typedef_or_map(CTypedef 动态表 → builtins
→ stdint/w32.win32base 库源 → map_t_type)且结果非指针即保留该已解析类型。
于是 sizet | t.CPtr ⇒ i64*(原静默退化为 i8*)。

修复前的故障:TPV2 Phase1 的 includes/ast/tokens.vp _kw_index_put 按 i8* 处理
哈希槽,slot[0] 只做 1 字节读写,指针被截断成低字节 0xd0,随后被当 KwEntry*
解引用 → ASan 读地址 0xd0 崩溃(TPC 侧同 sha 为 alloca i64*/load i64/store i64)。

stub/declare 侧 VpiReader._resolve_vpi_ret_annot_str 同步同一判据:新增
first_td 标记,对裸名(无 '.')、非白名单、非指针的解析结果复用同一个
GenApi.resolve_typedef_or_map 判定,命中即保留 pointee。声明侧与定义侧现在
同型(sizet/uint64/INT/u32 等别名两侧都得到 i64*/i32*),同型即收敛,
不再需要把 declare 侧坍缩为 i8* 去迁就定义侧。

既有路径行为不变:泛型参数名、内建标量白名单(int/char/str/bytes/bool/float/
double)、解析失败与裸枚举名的 i8* 兜底;指针别名(BYTEPTR/HANDLE/VOIDPTR)
因解析结果为 Ptr(...) 不置位 first_td,仍坍缩 i8*,与定义侧 td_res_is_ptr
守卫对称。
2026-09-15 13:52:21 +08:00
TermiNexus c1859a111c fix(tpv): 撤销 llc 输出重定向,回归继承父控制台并保留 GetLastError 与退出码诊断
病因:_run_cmd_wait 置 STARTF_USESTDHANDLES 后所有 llc 均无法启动(CreateProcessA 返回 0,GetLastError=87 ERROR_INVALID_PARAMETER),整条构建被阻断。已逐项核对生成的 IR 并排除:STARTUPINFOA 布局(偏移 80/88/96、size 104 正确)、si.cb=104、GEP 字段索引 11/15/16/17 正确、CreateFileA 的 access/share/disposition/flags 正确、dwFlags=256 正确、CreateProcessA 十个参数正确;静态证据无法再推进,故撤回重定向。

移除:_run_cmd_wait 的 log_path 形参、CreateFileA 打开 .llc.log、STARTF_USESTDHANDLES 与三个 hStd 句柄赋值、父侧 CloseHandle 守卫;连带删除失效链路 _log_captured_output、CMD_OUTPUT_MAX、LlcTask.LogPath 及其 init/free 与成功路径 DeleteFileA。进程创建回到继承父控制台(dwFlags 保持 memset 的 0),llc 输出直接外显。

保留诊断(用户要求):CreateProcessA 失败后立即取 GetLastError 并以 VLogger.warning 打印(worker 线程内不能用 error,它会 sys.exit);串行 compile_ll_to_obj 与并行 run_llc_parallel 失败时均打印 模块sha1、退出码、完整命令;LlcTask.ExitCode 保留。
2026-09-14 23:02:59 +08:00
TermiNexus 05d5bfbf37 fix(tpv): 修 CreateProcessA 重定向未启动外部命令,并把捕获输出移到 error 之前
主因:_run_cmd_wait 置 STARTF_USESTDHANDLES 时只设了 hStdOutput/hStdError,hStdInput 仍是 memset 后的 NULL(0);Windows 要求三个句柄都有效且可继承,故 CreateProcessA 直接返回 0(表现为 exit=4294967295 即 -1,且 .llc.log 恒空、一次也没有 llc 输出)。日志文件改以 GENERIC_WRITE|GENERIC_READ 打开并令 hStdInput 同指该文件(llc 不读 stdin),三态句柄均不泄漏;无重定向时不置 USESTDHANDLES、退回继承控制台的行为不变。

另在 CreateProcessA 失败处立即取 w32.win32base.GetLastError 并以 VLogger.warning 打印错误码+命令+重定向文件路径(warning 而非 error:error 会 sys.exit,而此处运行在 llc worker 线程内)。

同时修正一处使功能完全失效的顺序问题:_log_captured_output 原先排在 VLogger.error 之后,而 error 内部 sys.exit(1),故捕获到的 llc stderr|stdout 永远不会打印(这正是只看到一行 llc 编译失败、.llc.log 留空的原因)。串行 compile_ll_to_obj 与并行 run_llc_parallel 两处均改为先打印捕获输出、再报错。
2026-09-14 22:14:45 +08:00
TermiNexus 82f16127c0 fix(tpv): llc 日志文件句柄初始化改用 NULL,避免整数常量赋给指针类型
_run_cmd_wait 里 log_fh 原以 w32.win32base.INVALID_HANDLE_VALUE 初始化,而该常量是 CDefine = -1(整数),赋给 HANDLE 指针类型触发"整数隐式转指针已禁止"的 fail-fast(BuildPipeline.vp 原 595 行)。

改为以 None(NULL) 初始化;仓库既有惯例是只把 INVALID_HANDLE_VALUE 用于比较(全仓 20 余处同写法),故其余比较判断不变。因 NULL 与 INVALID 都不应参与关闭,父侧 CloseHandle 增加 None 守卫,避免对 NULL 调 CloseHandle。
2026-09-14 21:19:20 +08:00
TermiNexus 785741026f fix(tpv): llc 失败日志附带 stderr|stdout、退出码与完整命令
此前 llc 失败只打 模块sha1 与命令行:llc 的 stdout/stderr 因继承控制台而外显,程序拿不到文本,导致无法定位 verifier 报错。

_run_cmd_wait 新增可选 log_path:非空时用可继承安全属性把子进程 stdout|stderr 重定向到临时文件(不用管道:写文件不会因缓冲满阻塞、天然无死锁;且 worker 线程禁用池分配,故不复用 includes/subprocess.vp),CreateProcessA 成功后立即关闭文件句柄;log_path 为空时行为与原先逐字节一致。

LlcTask 末尾追加 LogPath/ExitCode(构造点均按 __sizeof__ 与整体 memset 处理,既有字段偏移不变);worker 记录退出码;run_llc_parallel 与串行 compile_ll_to_obj 在失败时打印 模块sha1、退出码、完整命令 与捕获输出(上限 8KB,带截断标记;输出为空/打开失败/无文件 三态如实打印,不伪造),成功时删除捕获文件、零额外输出。

包装层(compile_module_to_obj 摘要、run_pipeline 的 ErrorMsg、Phase2 PhaseB/PhaseB+ 聚合终止语)本身拿不到输出,保持不动,详情由深层打印。
2026-09-14 20:21:57 +08:00
TermiNexus 51daf28297 fix(expr): 三元表达式 else 分支按 then 的指针类型下传目标类型
VpiReader.vp:1963 的 rel_path_rt if rel_path_rt is not None else 单字符问号 触发 CastError(探针 kind=35 IfExp、val.Ty=i32、target_ty=i8*)。根因并非跨模块函数返回类型未解析:then 分支 rel_path_rt 已是 i8*(解析成功,赋值处与已有 fail-fast 均未触发,三条独立证据);真正的整数来自 else 分支——单引号单字符字面量在词法层被判定为 CONST_CHAR,而 translate_ifexp 翻译 else 时未下传目标类型上下文,遂物化为 i32(63),随后在类型对齐阶段被按 then 的指针类型 coerce,撞上整数隐式转指针的 fail-fast。

改为:仅当 then 分支是指针时,把其类型作为 else 的 target_ty 下传;单字符字面量随即走 ExprGenConst 的指针目标分支物化为 1 字符字符串(对齐 TPC 的引号推断),两分支同为 i8*,不再进入 coerce。非指针 then 不下传,既有常量物化与位宽行为逐字节不变(如 base64.vp 中 then 为 char 的两处不受影响)。

另经核查:模块级函数返回类型并无静默默认 i32 的问题——「依赖已就绪却解析不到」已是 fail-fast(CallGenMixin 跨模块函数路径与方法侧 _fail_method_ret_ty 同构),保留 i32 的仅是多遍收敛中间态的占位,故本次不动。
2026-09-14 19:26:46 +08:00
TermiNexus 7fabf886fc fix(vpi): 返回注解索引重建的键长常量拼写修正为 VPI_KEY_LEN
_vpi_rebuild_ret_idx 里把键长写成了不存在的 VPI_RET_KEY_LEN(参数侧另有 VPI_PARAM_KEY_LEN=224,本文件仅定义 VPI_KEY_LEN=224 与 VPI_PARAM_KEY_LEN)。此前该未定义标识符被旧规则按类型零值兜底编成 0,使重放偏移 ri*0 恒为 0(每条键都从第 0 条键位置读取),返回注解索引重建结果错误;因探测用 live 键比较,表现为未命中回退线性扫描——结果仍正确但索引形同无效。

这解释了此前实测中 _lookup_vpi_ret_annot 加索引后仍有约 913K 次 strcmp(返回侧索引一直未生效),而参数侧索引生效并从榜单消失。改为本文件既有且返回侧一致使用的 VPI_KEY_LEN(malloc/memset/查找/写入各处均用该常量)。
2026-09-14 16:54:36 +08:00
TermiNexus a46391bf42 fix(expr): coerce_to_type 拓宽方向判定改传 builder.Pool,修正未定义实参 pool
coerce_to_type 作用域内并无 pool(其参数是 builder),L425 却把 pool 作为第 1 个实参传给 _zext_or_sext_val。此前该实参翻译失败会被静默按类型零值兜底,使传给 ExprGenOps.should_zext_widen 的池为空、变量注解符号性判定失效,从而在无符号拓宽时错走 sext(即该处注释所述的无符号扩展缺陷根因)。

改为传 builder.Pool(与同函数内 llvmlite.const_int64(builder.Pool, 0) 等一致,是共同值所处的池)。该未定义实参由本会话新增的「实参翻译失败即 fail-fast」规则暴露。
2026-09-14 15:48:09 +08:00
TermiNexus b918321f77 perf(callgen): text.ll 存在性改属性查询 + 返回类型按模块一次性缓存
第 1 项:_dep_text_ll_exists 原以 fileio.File(MODE.R) 只读打开探测存在性(33,244 次调用,占总 create_file_utf8 46.6%,每次约 57us 的开句柄成本)。改用 GetFileAttributesA 并显式排除目录(对齐 IncludesScanner._PathExistsA 先例),无输出缓冲、零句柄。

第 2 项:_LookupMethodRetTyFromTextLL(29,974 次、约 380,505 tick/次、self 11.41e9 即 8.9%)原每次重开加重读加逐字节重扫整份 text.ll(实测约 2.47 tick/字节,均值 154KB)。新增 PermPool 缓存表(mangled 名 到 返回类型文本):某模块 text.ll 首次成功读入时登记其全部 define 行,同模块后续查询 O(1);命中后用调用方 pool 经 _ParseLLVMRetTyStr 重建类型,与原路径等价。

关键安全点:只存返回类型文本、绝不存会随每文件 reset_to 失效的 LLVMType*;同名只记首次(保持首个匹配优先);仅成功读到文件后才回写,未找到或文件暂不存在绝不缓存(Phase A 顺序生成依赖,负缓存会出错);缓存或分配失败一律回退原扫描。原扫描逻辑一字未改,保留为未命中路径。
2026-09-14 15:26:10 +08:00
TermiNexus ce612c8e57 perf(lookup): 恢复名字索引可信未命中即确无的短路(Ty 索引保持停用)
4ed7f39 因 Ty 索引不等价,把名字索引的未命中短路一并回退了,导致 find_struct(8.25M)/lookup_ctypedef(5.64M) 等又回到全表扫描。

经逐处自证不变量后仅恢复名字键索引:名字或键只在注册时写入一次(中和仅置空名,空名查询显式回退扫描);索引在所有写入路径同步维护,判据 Built==Count 失败即失效回退;探测用 live 键且同键条目沿链按注册顺序排列(无空槽夹层)⇒ 可信未命中即原线性扫描亦无所获,短路逐字节等价。

恢复点:StructGenMixin.find_struct、TypeGenCType.lookup_ctypedef、CallGenLeafKit.find_func_in_table/find_func_entry_global、TypeGenCDef.lookup_cdefine_{constant,double,string}、VpiReader._lookup_vpi_param_annots/_lookup_vpi_ret_annot(后者保留泛型特化归一化兜底)。

find_struct_by_type 的 Ty 索引判定保持停用(事故根因);首字符预筛、Function.NameLen/FUNC_SIZE、includes 均未改动。
2026-09-14 12:59:27 +08:00
TermiNexus 4ed7f3909a fix(lookup): 索引未命中一律回退原线性扫描,停用不可信的 Ty 索引判定
降低「索引可信且未命中即判定确无」语义带来的风险面(该语义是本轮构建 fail-fast 的最高嫌疑):索引只用于加速命中,未命中一律回退原线性扫描,使各查找函数在所有输入上的返回值与优化前逐字节一致。

StructGenMixin.find_struct:去掉「索引可信且查询名非空则跳过 O(n) 扫描」分支,未命中恢复原精确匹配全表扫描。

StructGenMixin.find_struct_by_type:整体停用 Ty 索引判定 —— 条目 Ty 会被深拷贝改写,槽按注册时 Ty 落位,改写后与 live 键脱钩,命中不保证是原线性首命中(不等价),故恢复原线性精确扫描;get_class_name_by_type(REnum.kind 识别路径)、register_struct 去重、lookup_field 首遍随之回到优化前语义。

CallGenLeafKit.find_func_in_table/find_func_entry_global、TypeGenCType.lookup_ctypedef、TypeGenCDef.lookup_cdefine_{constant,double,string}、VpiReader._lookup_vpi_param_annots/_lookup_vpi_ret_annot:可信未命中不再直接返回,一律落入原线性扫描。

未触碰:Function.NameLen/FUNC_SIZE、各处首字符预筛、includes/string.vp。
2026-09-14 12:41:27 +08:00
TermiNexus e7f47735ed perf(callgen): find_func_in_module 以 function_get_name_len 替代 strlen 热点
第二趟后缀匹配对每个函数节点调用 strlen(cur_name)(实测 2373 万次、占全程序 strlen 73.7%)改为读取 Function.NameLen;两处替换点:CallGenBase 约 563 与 597。

并在外部函数返回类型白名单登记 function_get_name_len 返回 i64(CSizeT),避免 stub 未注入时按默认 i32 导致签名不一致。匹配语义与返回顺序不变。
2026-09-14 08:26:18 +08:00
TermiNexus cb430bf00a perf(gen): 结构体/CTypedef 查名加索引,热点线性查名补首字符预筛
StructGenMixin.find_struct:索引可信且查询名非空时,未命中即判定确无同名条目,跳过 O(n) 精确扫描(此前索引只短路命中、未命中仍全表扫描,实测 527 万次逐条 strcmp 即源于此)。

TypeGenCType:新增 CTypedef 名索引(开放寻址、槽存 index+1、Built==len 才可信、不可信或未命中回退线性扫描、clear_ctypedefs 同步复位),消除 lookup_ctypedef 约 445 万次 strcmp。

补首字符预筛:StubMergerIR._FindSha1ByModName、CallGenBase._ensure_method_declare、VarGenMixin.find_var、CallGenLeafKit.find_func_entry_in_table(_owned)(首字符不等即判不等,匹配结果与顺序不变)。
2026-09-14 04:15:36 +08:00
TermiNexus e8db102b9e perf(gen): 结构体按模块查找与 ctypedef 跨度查找加首字节预筛
StructGenMixin.find_struct_by_module 的名与 ModuleSha1 两次 strcmp、TypeGenCType.lookup_ctypedef_span 的逐项名比较,均加首字符/首字节必要条件短路(首字节不等即判不等),并省去原逐项 strlen。匹配结果与返回顺序逐字节不变。
2026-09-14 00:09:06 +08:00
TermiNexus 16472a61d9 perf(lookup): 热点线性查找表加开放寻址名索引,O(n) 降为期望 O(1)
VpiReader 参数/返回注解键表(查表 + 写去重共用)、CallGenLeafKit 全局函数表、TypeGenCDef CDefine 表:由线性扫描改为开放寻址索引,对应热点约 117M + 17.8M + 10.5M 次 strcmp。

索引槽只存下标+1(不存键/节点指针),键表 realloc/移动后槽下标仍有效,无悬空指针;可信判据 Built==count,每次注册同步维护,不可信或未命中一律回退原线性扫描(正确性保证)。每次注册占一槽以保持首注册优先(探测链首个键相等者即原首个命中)。CallGenLeafKit 快速路径仅限 owner_sha1 为 None 的全局表查询,保持 owner 优先语义不变。
2026-09-14 00:05:12 +08:00
TermiNexus a9819e4123 fix(gen): 方法分派与 list 内建判定改用接收者实际特化名,根治 list[int].get 截断
_translate_method_call 原以注解来源 class_name 构造被调名;同一函数作用域内同名变量被声明/赋值两次时该名退化(get_class_name_by_type 的类型后缀匹配会把 list[sha.AST] 误判为元素类 AST),使 list[ast.ASTptr].get 被误绑为 list[int].get(返回 i32)后 inttoptr 截断指针,读垃圾 AST 节点崩溃(MatchGenMixin._handle_renum_match / _handle_simple_match)。

现统一以接收者实际 LLVM 结构体类型名(IR 真值)为准:基名为泛型模板时,含类型实参则以实际特化名覆盖;退化为裸泛型基类则 fail-fast(D25 口径,禁止静默默认 list[int])。

抽出 CallGenProp._strip_module_sha1_prefix / _resolve_generic_receiver_name / _is_list_builtin_receiver 共用实现;CallGenDispatch 8 处 list 内建判据同步改为基于接收者实际类型(真实 list 命中与 __elem_size__/hit_box 行为不变,实际类型不可得时保留注解名回退)。lookup_name 缓冲改为按精确长度分配。
2026-09-13 21:11:33 +08:00
TermiNexus 76df3f0225 perf(gen): 结构体/类型查找去线性化并守卫全量遍历残量
StructGenMixin: 加「名 | Ty 指针值 -> 首注册下标」索引,find_struct/find_struct_by_module/find_struct_by_type 走 O(1) 快速路径;每次注册占槽保序,中和/改名后键不等自然跳过,未命中或索引不可信回退原扫描。并补 _struct_name_hash/_struct_ty_hash(32 位 FNV,常量均小于 2^63 以避开大十进制字面量解析风险)。

TypeSpec.InternSpec: 加「文本键 -> 驻留下标」开放寻址索引,去重由 O(n) 降为期望 O(1);命中返回原同一实例,注册顺序/容量 fail-fast 语义不变。

register_struct/lookup_field/get_class_name_by_type 的按 Ty 指针首匹配改走 find_struct_by_type;CallGenLeafKit/CallGenCall 的按序号全量遍历补守卫注释(避免后续误改为按名查找)。
2026-09-13 18:56:16 +08:00
TermiNexus 0dd7511a9b fix(tpv): 修常量折成0致alloc(0)与多处fail-fast,并落地热点优化
TypeGenCDef: CDefine 表名深拷贝(vpi 注入名为零拷贝指针且 vpi_buf 提前释放,悬空名致常量查出错误值 0)

VpiReader: 模块级 SHA1_LEN(16) 改名 SHA1_HEX_LEN,消除与 Phase1.SHA1_LEN(17) 的跨模块同名冲突

CallGenCall: 实参翻译失败/漏传必需实参改 fail-fast,禁用零值兜底(堵住 SHA1_LEN 未解析变 alloc 0 的通路)

MatchGenMixin: 序列 match 的 case _ 分支补容量守卫(原先会越界写穿 pool 块、破坏空闲链)

Phase1Main/Phase1Scan: 预扫描缓冲分配失败改 fail-fast;加临时取证打点

CallGenBase: find_func_in_module 4 趟链表扫描合并为 2 趟

ImportsGenMixin: import 查找加哈希索引(键为 NUL 结尾副本,未命中回退原扫描)

TypeGenBase/TypeGenResolve: 库 typedef 按模块一次性门禁与缓存

VpiReader: vpi 注解解析补 rowptr[T] 分支
2026-09-13 17:50:03 +08:00
TermiNexus af2c66e7d7 chore(tpv): 永久池扩容至 1 GiB
PERM_POOL_SIZE 由 512MB (536871176) 调整为 1 GiB (1073741824),为 REnum 注册表挂到 PermPool 后留足余量。
2026-09-12 23:14:52 +08:00
TermiNexus eef84657a1 fix(tpv): 修正 REnum 表按名查找与 match 分派,恢复 str() 指针强转
str() 对整数(sizet/i64)走 snprintf 十进制格式化(Python 语义),不是 i8* 强转;用它读 sizet|t.CPtr 联合表元素再 strcmp 永不相等 => _is_renum_class_name 恒 0、变体名查不到(__types.vp:407 bits / :159 Void 根因)。IR 实证 output/a8891afb66d3eda4.text.ll L5551-5558。

- ClassGenEnum: RenumClassNames/RenumVariantNames 读侧统一改 (str)(t.CVoid(x, t.CPtr));新增 _is_renum_by_entry 与 RenumTableDump;REnum 注册静默早退改 fail-fast

- MatchGenMixin/CallGenMixin: match 分派 2a/2b 与变体构造入口并上 by_entry 判据

- TypeGenResolve: 泛型下标 Phase B 解析不出特化时 fail-fast

- CallGenProp: 泛型特化名末段按括号外点切分

- CompilerFactory/ClassGenBase: REnum 注册表改挂编译期单例

- ClassGenDef: CEnum/REnum 分派歧义 fail-fast
2026-09-12 23:14:23 +08:00
TermiNexus e0e024ee93 fix(tpv): 注解裸名按 from-import 归属查找(D26 闭环)
stub 注入注解时 trans.ModuleSha1 是正在翻译的文件,而注解里的类归属 stub 源模块;仅靠当前模块 sha1 加全局可见性在 App 严格模式下查不到(AsyncGenMixin 未 import ast.Expression),导致 ast/__init__.vpi 的 parse_expression -> rowptr[Expression] 返回类型解析失败 fail-fast。TypeGenResolve 的 Name 分支在 own 之后、可见性之前插入 from_imports 归属查找(LookupModNameByAlias 到 LookupSha1ByModName 到 find_struct_by_module)。TPV 自举 Phase1 从 28/53 推进到 40/53
2026-09-12 19:37:55 +08:00
TermiNexus ea2abba574 refactor(tpv): 删除 _deps_pending 置位面与 Phase A pending 多遍收敛机制
69 个工程成功编译且 _phaseA_pending.log 恒 0 字节,证实 14 个置位点全部零命中。删除 14 个 _MarkDepsPending 调用点(原「依赖未就绪」分支保留默认类型或原报错路径);删除 _MarkDepsPending 与 _AppendPhaseAPending 及 _phaseA_pending.log 读写;删除 Phase A 出口「pending 则不写盘、回滚 MethodsEmitted、continue」整段;删除 _deps_pending 与 _pf_dbg_pend_flagged 字段及全部读取分支;清理遗留探针 [PF-DBG] 与 [METHDBG]。16 files changed, +37 / -558
2026-09-12 19:24:52 +08:00
TermiNexus 17f109e204 fix(tpv): 修复 vpi 重导出包前缀与 D25 变量名悬垂,补齐 fatal_error 刷盘
VpiReader 包 __init__ 重导出补包前缀,类注册与方法声明两遍化,按需注入依赖 vpi(先登记后注入防环,失败撤销登记);VarGenMixin 的 add_var 深拷贝变量名,符号表条目不再依赖 AST tree 生命周期;CallGenCall 的 list 内建与泛型基类判定类名来源回退到接收者实际 struct 名;TypeGenErr 的 fatal_error 退出前 fflush;StubMergerBuild 每模块重置 vpi 注入去重表
2026-09-12 19:00:19 +08:00
TermiNexus 626f79cbd1 fix(tpv): vpi 相对导入补回包前缀(D26)
- vpi 只存 module=base(前导点记在 level),from-import 映射写字面 base 时,解析端按精确 LookupSha1ByModName 查不到 ast.base ⇒ 裸类名(AST 等)解析失败 ⇒ rowptr[AST] 等注解 fail-fast(TPV→TPV 自举 includes/ast/parser.vp parse 根因)

- 按 stub 自身相对路径还原包名(ast/parser.vp → ast),level==1 拼成 ast.base 再登记;_stub_add_imported 与 _stub_add_from 两处同步
2026-09-12 15:59:37 +08:00
TermiNexus 3c45895878 fix(tpv): alloca 退化守卫收窄到「裸泛型模板基类」形态(避免误杀同名重声明换类型)
- parser.vp:964 'node: rowptr[AST] = Slice(...)' 命中上一版守卫(旧 alloca 是 Tuple*)——但 Tuple* vs AST* 是同名重声明换具体类,两者都是 8 字节指针槽,属合法用法,不该 fatal

- 收窄判据:仅当旧 alloca 的 pointee 是**裸泛型模板基类**(剥 sha1 前缀后不含 '[' 且 _find_generic_template 命中)时才 fail-fast —— 这正是 D25 的退化形态(丢类型实参 → 方法调用按错误元素类型特化 → 截断)
2026-09-12 15:24:23 +08:00
TermiNexus 590a6a2bbe fix(tpv): 复用的 alloca 与注解类型不一致时 fail-fast(D25 alloca 退化根治)
- get_or_create_sym 只用「名字+作用域」复用(ty 不参与判定),可能命中类型不同的旧 alloca(同名重声明 / 退化 alloca,如裸 list* 对上 list[ast.ASTptr]*)→ alloca 类型与使用点注解类型分裂 → 方法调用按错误元素类型特化 → inttoptr 截断

- 只比较「双方都是指针且 pointee 都是结构体」这一最典型退化形态:结构体名不同即 fatal_error,报出变量名与两个类型,直接暴露退化点(不兜底)
2026-09-12 15:13:41 +08:00
TermiNexus b206e7d9c1 fix(tpv): 泛型类型实参禁止静默默认 int(D25 fail-fast 两处)
- CallGenMixin: 裸名泛型构造推断不出类型实参时不再默认 int,改 fatal(与 _infer_generic_ctor_type_arg 注释「绝不默认 int」对齐)

- CallGenCall._translate_method_call: 泛型模板基类(剥 sha1 前缀后不含 '[')上的非 list 内建方法调用改 fatal —— 此前静默按默认 list[int] 特化,元素按 i32 处理 → 方法返回 i32 → inttoptr 截断指针 → 读垃圾 AST 节点(D21/D25 崩溃族)

- list 内建方法仍走内联路径(运行时 __elem_size__),不受影响
2026-09-12 14:52:23 +08:00
TermiNexus ced50bd78b fix(tpv): 注解 mangled 名算键统一——补回丢失的 from_imports(D25 根因第 1 步)
- extract_class_name_from_annotation 的 Subscript 分支递归取 base 时漏传 from_imports(同函数其余调用点都传了),导致同一注解在「派发名 / 查表键 / 注册键」三处算出不同 mangled 名

- 症状:MatchGenMixin._handle_renum_match 两遍对同一裸 list* alloca 分别按 list[ast.ASTptr] 与 list[ast.ClassDefptr] 解释 → .get 误分派 → 节点指针被截断 → StmtGenMixin._translate_stmt_impl ASan 崩溃(D21 复发,见 Debugger/_Type.md D25)
2026-09-12 14:32:57 +08:00
TermiNexus 967095df5b refactor(tpv): _deps_pending 置位收敛到唯一入口 _MarkDepsPending(S4 第一步)
- CallGenBase 新增 _MarkDepsPending(trans,kind,cls,method,dep,extra):一处实现「置标志 + 统一格式 [DEPDBG] + 持久化 _phaseA_pending.log」三连

- 14 处置位点(D7-1 清单:attr_read/attr_write/method_ret/mro_ret/mro_anc/special_ret/prop_ret/dispatch_ret/dispatch_sp/func_ret/func_mangled/global_stub/class_parent/class_field_ty)全部改经该入口

- 行为不变;DEPDBG 行格式统一为 pending(<kind>): mod=.. cls=.. method=.. dep=.. extra=..,供下一步按 kind 统计命中数、删除死点
2026-09-12 13:53:20 +08:00
TermiNexus 7e68e76ec4 refactor(tpv): SHA1 前缀判定收敛到 GenApi 唯一真源(C10/D3 收尾)
- CallGenCall: list 泛型前缀剥离(719) 与类型名提 sha1(907/1190) 改 GenApi.sha1_prefix_len,不再盲取前 16 字节当 sha1

- CallGenCBuiltin._asm_name_is_mangled / CallGenLib._is_sha1_mangled_call: 改真源(去大写 A-F、去扫描任意位置 '.')

- CallGenFd: 类型名提 sha1 加前缀格式校验(裸名不再误取前 16 字节)

- CallGenMixin: CExport declare 前缀判定改真源

- StructGenMixin / TypeGenResolve: 类型名、模块段判定改真源;StructGenMixin 补漏掉的 GenApi import(L596/L943 引用原会 fail-fast)
2026-09-12 12:59:01 +08:00
TermiNexus 471bedbcf4 fix(tpv): 去掉条目指针数组往返,改上界/量长直写(修自举 ASan strcat -1 崩溃) 2026-09-12 12:43:08 +08:00
TermiNexus 82eea2719f refactor(tpv): rowptr[T] 迁移 + D 表定长缓冲改精确分配 + SHA1 前缀判定收敛(D1/D3/D4/D5/D6/D7/C10) 2026-09-12 11:09:35 +08:00
TermiNexus 7471cc64f9 docs(tpv): 精简 CompilerFactory 装配设计注释块 2026-09-12 10:52:00 +08:00
TermiNexus 679758c05b refactor(tpv): 删无符号判定的旧字符串兜底,真源唯一(S4 去字符串化收尾)
删 _type_name_unsigned_legacy / _annot_str_unsigned_scan / _annot_str_unsigned_legacy / _last_dot_tail / _is_simple_ident;_type_name_unsigned 与 _annot_str_unsigned 变为真源唯一(spec_sign_of_annot)。

依据:真源已含顶层 | 分段、去括号/空白、IDENT[INNER] 取元素、去模块前缀、精确名白名单、CTypedef 链展开,旧路径能力是它的真子集(S2 已把旧路径修到与真源一致),差异面就是假阳性区 —— strstr 子串扫描正是 A4/A5 根因(BYTEPTR 含 BYTE、任何含 uint32 的 mangled 名都被判无符号)。现语义:真源 UNKNOWN=不表态 => 返回 0;若日后发现漏判,补白名单/补 CTypedef,不恢复子串扫描。
2026-09-12 10:26:51 +08:00
TermiNexus cc17a8f8db refactor(tpv): 去掉函数内懒导入,改用顶层 import
Viper 的 import 是编译期的,懒导入在编译型语义下毫无作用,模块环不需要也不应该用它规避。

TypeSpec 顶层 import GenApi(GenApi→GenTypes→TypeSpec 成环但编译期无碍);CallGenLib 的既有懒导入(VpiReader)一并改顶层(VpiReader 不反向依赖 Call 层,无环)。
2026-09-12 10:23:08 +08:00
TermiNexus 7fbf69ae63 fix(tpv): InternSpec 表满改为 fatal(不再返回 None 静默丢类型信息)
表容量超限原实现只是 return None,但注释声称 fail-fast:None 会沿调用链传到 entry.Spec,条目静默失去类型信息(_entry_spec_unsigned 恒返回 0 ⇒ zext/sext 方向取错),属静默数据损坏。

改为 GenApi.fatal_error(InternalCompilerError);TypeSpec 与 GenApi 互为依赖(GenApi→GenTypes→TypeSpec),故函数内懒 import 破环(同 CallGenLib.vp:220 既有模式)。仅当 fatal_error 无 gen 通道时退化为不驻留返回完整实例,守住不返回 None 的底线。SPEC_INTERN_MAX 仍为 8192(当前未触发)。
2026-09-12 10:21:27 +08:00
TermiNexus 1039969f69 refactor(tpv): CPtr 标记语义定为注解基名精确比较(第 5 处收敛)
CallGenMixin 闭包 vs 函数指针分派:原 strstr(注解名, CPtr) 子串匹配改为 SpecTypeNameBaseIs(fp_annot, CPtr)。

语义:CPtr 标记 = 注解基名精确等于 CPtr(裸 t.CPtr / from t import CPtr)。不能改用 Spec 判定——t.Callable[[A], R] 解析结果同样是 Ptr(i8)(TypeGenResolve.vp:2525-2531),与 t.CPtr 完全同型,类型层无区分度,故标记只能由注解名承载,不新增 Spec 字段。副作用:含 CPtr 子串的类名(如 CPtrHolder)不再被误判为闭包指针。已知局限(与改造前一致):Foo | t.CPtr 的 SourceName 只留 Foo,该形态判为非标记。
2026-09-12 10:12:01 +08:00
TermiNexus ae63f4f6ab refactor(tpv): 容器判断改基名精确比较(SpecTypeNameBaseIs)
TypeSpec 新增 SpecTypeNameBaseIs(name, base):基名 = 截断第一个 '[' 起的泛型实参后,取最后一个 '.' 之后(去模块名 / LLVM 注册名的模块 sha1 前缀),再精确比较;零分配,复用 _spec_name_eq。

4 处容器判断改写:StmtGenCifMixin:1133/1142/1498(读注解名)、CallGenDispatch:1685(读 LLVM 注册名)。原 strstr 子串匹配会误命中 MyDictHelper / playlist;副作用是 GSList 等用户类不再被误判为内置 list,改由 OOP __iter__/__next__ 路径处理。第 5 处 CallGenMixin:973 的 CPtr 属标记判断(SourceName 会丢掉联合里的标记),需先定标记语义,本次不动。
2026-09-12 10:10:14 +08:00
TermiNexus 19fca19ca7 refactor(tpv): get_llvm_type_bits 双副本收敛到 TSpec.SpecTypeBits
TypeSpec 新增 SpecTypeBits 作为「LLVMType → 整数位宽」块的唯一实现(对齐 SpecTypePtrLevel 的收拢先例);ExprGenBase/TypeGenErr 两份同码副本改为委托,TypeGenErr 补 TypeSpec 导入(无环)。

保留方法名与 self. 调用点:@t.NoVTable 静态绑定下 Mixin 内部大量 self.get_llvm_type_bits 依赖其存在,只换实现体不动签名。副本分叉会导致同一类型两处算出不同位宽的隐蔽不一致。
2026-09-11 23:14:51 +08:00
TermiNexus 41c5908c5e refactor(tpv): Y 类兜底改 fail-fast(vpi 声明返回/形参 + vpi 类型文本)
新增 GenApi.fatal_error:VpiReader/Converter 等模块级函数无 self.Gen,统一经此报错通道;GenApi._g_gen 为空时回退 Generator._g_gen(Phase 1 vpi 生成早于 BuildPipeline 注入 GenApi._g_gen)。

VpiReader._inject_function_declare/_inject_method_declare 共 4 处:有注解却解析不出类型 → fatal;无注解的 i32 返回/i8* 形参合法默认保留。Converter._PyiTypeStr 未知节点类型不再静默写 int。
2026-09-11 22:46:39 +08:00
TermiNexus db8220801c refactor(tpv): Z 类兜底改 fail-fast(返回类型/模块级变量/枚举成员)
- ClassGenVTable 方法返回:有注解但解析失败即 fatal;State/CExport/CExtern 标记归一为 Void
- TranslatorGenMixin 模块级变量:有注解但解析失败即 fatal(保留无注解合法默认)
- ClassGenEnum._resolve_enum_member_type 4 处:Attribute 缺失名/Name 缺失 id/两处解析失败均改 fatal
  (无注解与 t.State 标记的 i32 默认保留)
2026-09-11 22:17:45 +08:00
TermiNexus cc3359faea refactor(tpv): 形参类型注解解析失败改 fail-fast(不再静默用 i32)
- FuncGenDef 前向声明形参 / 定义签名形参:resolved is None 即 fatal
- FuncGenBase create_function 形参:同上(暴露调用方未传 trans 上下文)
- ClassGenVTable _translate_method 形参:同上(签名/调用点/实现体三方一致)
- TypeGenCDef 整数 CDefine 值缓冲分配失败改 fatal(对齐同文件浮点/字符串分支)
- FuncGenMixin._type_byte_size 类型缺失改 fatal(case _ 保留 8,注释说明无布局查询能力)
2026-09-11 22:14:10 +08:00
TermiNexus 22c71388d4 docs(tpv): 更新 TypeSpec 注释(IsPtrElement 已删,仅容器种类待后续) 2026-09-11 22:09:32 +08:00
TermiNexus a4bc5dc0ce refactor(tpv): 删除 IsPtrElement 字段,指针元素语义承载于 Spec.ElemBits
- TypeSpec 抽 _spec_clone(消除 SpecWithSign/Source/ElemBits 三处重复逐字段拷贝)
- 新增 SpecWithElemBits:覆盖元素槽位宽(独立副本不驻留,ElemBits 不在驻留键内)
- set_var_ptr_element 改写 Spec.ElemBits=64;Spec 不可承载(非指针/缺失)即 fail-fast
- 新增 var_is_ptr_element(PtrLevel>=1 且 Elem.Bits==8 且 ElemBits==64,排除真实 i64* 与纯 str)
- ExprGenName 4 处下标读写切读该入口;GenTypes 删 VarEntry.IsPtrElement
2026-09-11 22:09:18 +08:00
TermiNexus e55ec5fde0 docs(tpv): 更新 TypeSpec 设计注释至 S4 现状(TypeStr/AnnotClassName 已删) 2026-09-11 22:06:20 +08:00
TermiNexus 9e461d21cb refactor(tpv): 删除 AnnotClassName 字段,注解类名统一承载于 Spec.SourceName
- GenTypes 删 VarEntry.AnnotClassName / FieldEntry.AnnotClassName
- set_var_annot_class_name / add_field 改为写入 Spec(Spec 为 None 时建 TS_UNKNOWN 承载,
  不表态具体类型,等价原 Spec 缺失);删 1b 的 AnnotClassName 专属深拷贝
- var_annot_class_name / get_field_annot_class_name 去旧字段回退分支
- 剩余 7 处直读(StmtGenCifMixin list/dict 5 处、CallGenMixin CPtr 2 处)改走封装
2026-09-11 22:06:00 +08:00
TermiNexus 95133d8483 refactor(tpv): 变量/字段注解类名读取统一走 Spec.SourceName 封装
- VarGenMixin 新增 var_annot_class_name(SourceName 优先,回退 AnnotClassName)
- 变量侧类名恢复与诊断读取切读:ExprGenAttr(5) / ExprGenOps(6) / ExprGenName(1)
  / CallGenDispatch(3) / CallGenCall / CallGenProp / CallGenVt / ExprGenCls / AssignGenMixin / MatchGenMixin
- ExprGenAttr 字段侧直读改走 get_field_annot_class_name
2026-09-11 22:02:28 +08:00
TermiNexus 8aad248c25 fix(tpv): SpecWithSign 不复制 SourceName,避免驻留表来源名污染
InternSpec 键不含 SourceName,若带来源名的副本入表,后续同键查找会把该来源名
污染给无关条目(键相同必须语义相同 ⇒ 表内实例 SourceName 恒为 None)
2026-09-11 21:54:29 +08:00
TermiNexus 029a423f55 feat(tpv): Spec 增 SourceName 承接注解来源名(类名块基础设施)
- TypeSpec 新增 SourceName 字段:注解书写形态名(裸名/模块限定名/t.XXX attr/mangled),
  与 BaseName(LLVM 注册名)命名域不同;不参与驻留键,避免条目暴增与线性查找变慢
- 新增 SpecWithSource(独立副本、不驻留、幂等)/ SpecSourceName 读取入口 / _spec_set_source_name
- set_var_annot_class_name 与 add_field 注解侧填 SourceName
- get_field_annot_class_name 切读 SourceName 优先(3 个调用点自动生效)
2026-09-11 21:54:10 +08:00
TermiNexus 7028399366 refactor(tpv): 无符号判定切读 Spec(条目级 TS_UINT 优先)
- 新增 _entry_spec_unsigned:Spec.Kind==TS_UINT 为注解明确无符号的强信号
- _node_type_unsigned 的 Name/Subscript 分支、Attribute 字段分支切读 Spec 优先
- 只切无符号方向(TS_INT 含未表态,不可反推 SIGNED,仍走字符串真源)
2026-09-11 21:51:11 +08:00
TermiNexus 7fedf8daad feat(tpv): Spec 注解侧填符号性(SpecWithSign + 变量/字段接入)
- TypeSpec 新增 SpecWithSign:整数 TS_INT↔TS_UINT 切换 + 驻留(SpecToText 键区分符号)
- set_var_annot_class_name 接入:注解类名提取符号性,补 Spec 无符号性
- add_field 接入:字段注解类名提取符号性,补 Spec 无符号性
2026-09-11 21:48:23 +08:00
TermiNexus 0a108e6289 refactor(tpv): 删除 FuncEntry.TypeStr 旧路径,函数类型文本改由 Spec 派生
- 删 FuncEntry.TypeStr 字段、_build_func_type_str 生产者、_check_func_spec_text 闸门
- update_global_func_typestr 改名 update_global_func_spec,仅填 Spec(去 pool 参数)
- ClassGenVTable vtable 合并 func_valid 分支,统一 SpecToLLVMText 派生
- 同步 FuncGenDef/ClassGenVTable/GenApi/VpiReader 调用点
2026-09-11 21:33:58 +08:00
TermiNexus a173ba0296 refactor(tpv): ClassGenVTable vtable 类型文本切到 SpecToLLVMText
- 删除 _format_func_type_str(512 缓冲 + >100 参截断的 TypePrint 重复实现)
- _generate_vtable 当前类方法分支改为 SpecToLLVMText 派生,回退全局表 TypeStr
- Spec/TypeStr 皆失败时 fail-fast
2026-09-11 21:21:01 +08:00
TermiNexus 0e973d5f23 feat(tpv): SpecToLLVMText 格式化器与函数签名一致性闸门,并修驻留键静默截断
- SpecToLLVMText/_spec_llvm_text/_spec_llvm_append 逐分支对齐 llvmlite.TypePrint
- CallGenLeafKit.update_global_func_typestr 增 _check_func_spec_text 闸门,不一致即 fatal
- _spec_text_append 溢出返回哨兵;_spec_to_text 各结构分支检查哨兵
- SpecBaseText 前缀先算长度;InternSpec 键装不下返回完整实例不写截断键
2026-09-11 21:13:28 +08:00
TermiNexus ff7caa57e8 feat(tpv): S3 条目双写接线,三个唯一创建点填 TypeSpec
- GenTypes: VarEntry / FieldEntry / FuncEntry 各加 Spec 字段(FuncEntry 合并为单个整签名 Spec,替代设计 D4 的 RetSpec+ParamSpecs,理由见注释)
- VarGenMixin: add_var 新增 _spec_of_var_alloca,用 alloca.Ty 的 pointee 构造 Spec(is_ptr_type 判定)
- StructGenMixin: add_field 统一在两条写入分支之后填 Spec(PermPool 驻留故无需 DeepCopyType)
- CallGenLeafKit: _register_global_func / add_func_to_table 初始化为 None;update_global_func_typestr 新增 _build_func_spec 填整签名 Spec(用 function_get_param_head 两遍遍历,变参取 func.IsVarArg,避免 SpecFromLLVMType 的 Func 分支丢变参)
- 注: 当前尚无读取方;驻留表溢出会导致该条目 Spec=None(已记录,S4 改 fatal)
2026-09-11 20:43:17 +08:00
TermiNexus f0deff931d refactor(tpv): S3 无符号判定切读真源,指针层级收拢到 TypeSpec
- ExprGenOps: _type_name_unsigned / _annot_str_unsigned 改为真源优先(UNSIGNED->1 / SIGNED->0),仅真源不表态 UNKNOWN 时按需回退旧子串路径(热路径避免双份开销)
- ExprGenOps: 删除 S2 的双写一致性闸门 _spec_sign_check(切读后读即取真源,比对恒等、无意义)
- TypeSpec: 新增 SpecTypePtrLevel -- 指针层级推导的唯一实现,原 get_ptr_level 记录的 match codegen 缺陷规避写法随实现移入
- ExprGenBase: get_ptr_level 删去同码副本,改为委托 TSpec.SpecTypePtrLevel(行为等价)
- TypeGenCType: spec_sign_of_annot 注释更新为无符号判定的读入口
- 注: 值级切读(读 Spec.PtrLevel)仍需条目双写(VarEntry.Spec / FieldEntry.Spec 等)接线,S2 路线 C 未做,留待下一步
2026-09-11 20:40:26 +08:00
TermiNexus b7100e9dc1 feat(tpv): S2 落无符号判定双写闸门,并修 i8 拓宽丢符号与 WCHARPTR 旧判
- TypeSpec: 新增 SpecFromLLVMType(LLVM 类型 -> TypeSpec 结构转换器,当前无调用点);SpecSignFromTypeName 拆出零分配切片版 Span
- TypeGenCType: 新增 lookup_ctypedef_span 与独立符号性真源 spec_sign_of_annot(精确名白名单 + CTypedef 链逐层展开)
- ExprGenOps: 旧判定改名 *_legacy,外层加双写闸门 _spec_sign_check(新真源明确表态且不一致即 fatal;S2 不切读)
- ExprGenOps: 修旧符号性两个洞 -- 子串表补 WORD、裸别名沿 CTypedef 链展开(闸门实测暴露 WCHARPTR 旧判 signed、新真源 unsigned)
- ExprGenOps: should_zext_widen 的 i8 分支补「源注解明确有符号则 sext」例外,修 b8470c1 引入的 TypeCastTest 回归(t.CInt32T(v) 丢符号);新增 _node_type_signed
- TypeGenBase: 补 import lib.core.Gen.Type.TypeSpec(此前遗漏)并接入驻留表 ensure_spec_arena(PermPool 惰性创建)
- 修两条链接错误: TypeSpec.vp 残留 stdlib.atoi 改 string.atoi;模块别名与模块内类名同名会被误解析为该类静态方法(隐式补 self 实参),四处导入别名统一改 TSpec
2026-09-11 20:32:55 +08:00
TermiNexus b1090abe70 feat(tpv): 新增 TypeSpec 统一类型描述符(S1,未接线)
类型信息目前散落在 AnnotClassName/IsPtrElement/TypeStr 等字符串与布尔里,
导致「无符号/指针层级/数组维度/函数签名」到处重复解析且结论不一致
(Debugger/_Type.md 二次盘点 A/C 类)。

新增 App/lib/core/Gen/Type/TypeSpec.vp 作为统一描述符:
- TypeSpec:Kind/Bits/IsSigned/PtrLevel/Elem/ArrayCount/BaseName/Ret/Params/
  ParamCount/IsVarArg/Storage,以及元素语义三元组 ElemBits/IsElemPtr/
  DerefBase(替代单一 IsPtrElement 布尔——同一 Ptr(i8) 下存在两种相反语义)
- TypeSpecArena 驻留表(对象持有,非全局表)+ InternSpec 去重(键=规范文本)
- SpecToText/TextToSpec 规范文本编解码(驻留键/日志/vpi 通道)
- 构造器 NewTypeSpec/NewTypeStruct/NewTypePtr/NewTypeArray/NewTypeFunc
- 访问器 SpecIsPtr/SpecPointedTo/SpecIsUnsigned/SpecBitWidth
- SpecSelfCheck 自检(不参与编译流程)
- 实例与数组一律 PermPool 分配(跨 mark 持久,跨模块可直接传指针)

GenTypes 仅加 import 使该模块参与编译(否则新文件不会被编译、无法验证);
未改动任何既有调用点,产物 IR 应与上一版一致。
2026-09-11 20:02:52 +08:00
TermiNexus b8470c1cae fix(tpv): 拓宽方向统一为单一真源,并修 TypeStr 变参与 should_zext_widen 缺参
三项独立缺陷(均来自类型系统盘点 _Type.md D7):

1) should_zext_widen 签名缺 trans 形参,函数体内的 trans 退化为隐式全局(恒
   None),而 translate_binop 两处调用早已在传第 5 实参(被静默丢弃)⇒ 注释所
   述「源无符号 → zext」补救从未生效:Name/Subscript 形态的无符号源(uint16/
   uint32 变量、数组元素)在比较/二元路径恒被 sext 扩展(ide.vp/timer.vp 根因)。
   修:should_zext_widen 补 trans 形参并明确为拓宽方向的单一真源;
   translate_compare 两处补传 trans;ExprGenBase._zext_or_sext_val 改为委托
   should_zext_widen(wide_val=None 使「宽侧常量」启发式自然失效)。

2) _translate_t_type_cast 原以「目标类型名符号性」决策整数拓宽,且 target_name
   未知时默认 zext,与 coerce_to_type / should_zext_widen 的默认 sext 极性相反。
   修:统一由源的符号性决定(与 coerce 同源),并删除已无使用点的 target_name
   形参(1 处签名 + 7 处调用点)。

3) TypeStr 生成(FuncEntry.TypeStr 的唯一生产者)与其现算现用副本
   (ClassGenVTable._format_func_type_str)都未打印变参后缀 ...,而 FuncGenDef
   对 *args 会设 IsVarArg=1,且继承方法强制走 TypeStr 回退 ⇒ vtable 中的函数
   指针类型与真实签名不符。修:按 llvmlite IR 打印规则补齐(有参 ", ...",
   无参 "...")。
2026-09-11 19:57:18 +08:00
TermiNexus c71642dca0 清理一部分异 2026-09-11 18:55:33 +08:00
TermiNexus cfb92e5da5 初步清理 t. 类型异味 2026-09-11 18:36:32 +08:00
TermiNexus 4ce55e3ea3 fix(tpv): 泛型注解预特化下沉到 AnnAssign Handle 路径,修 .get() 误分派崩溃
translate_annassign 解析注解前未先注册泛型特化,`list[ast.AST | t.CPtr]
| t.CPtr` 会退化为裸 list*,随后 body_list.get(i) 被分派到默认
list[int].get(返回 i32)→ inttoptr 截断指针 → 读垃圾 AST 节点,
StmtGenMixin._translate_stmt_impl 崩溃(寄存器指纹与既有潜伏崩溃一致,
IR 实证见 Debugger/ViperOS3.md 第十九节)。

抽出 _prespecialize_generic_annotation 供 pre_scan_allocas 与
translate_annassign 共用(该补救原先只存在于预扫描路径),使两处解析一致。

同批(P1-A):赋值/返回路径把目标类型作为 target_ty 传入 translate_value,
使 `x: uint64 = 12345` 与 `u64(12345)` 物化出同一条 i64 常量、省去 coerce
的 zext。站点:translate_annassign(global/nonlocal/模块级/局部四处)、
translate_assign(单 Name 目标)、translate_return。target_ty 仅被
Constant 节点消费,不影响调用/二元/属性路径。
2026-09-11 18:10:19 +08:00
TermiNexus d3690f9143 fix(tpv): 依赖注入期默认参数按定义模块 SHA1 登记,消除跨模块同名函数串味
N17:VpiReader 注入函数/方法 declare 时显式传入 src_sha1(被依赖模块自身
sha1),add_func_to_table 透传至 _register_global_defaults;调用点默认值查找
改用 find_func_entry_in_table_owned 按 "{owner_sha1}." 前缀限定。修复
usb.uhci.init 误命中 mm.init 的 5 形参/2 默认值而凭空补 2 个 i32 0 的 ABI
错位(IR 实证:call @...init(i16 %4, i32 0, i32 0) → (i16 %4))。

同轮并入的其他编译器侧改动:
- N11 Converter.vp:无 -> 但带 @c.CReturn 时合成 tuple 返回注解,与 define 对齐
- N12 VpiReader.vp:union 指针(T | t.CPtr)stub 解析与定义侧收敛到 i8*
- N14 ExprGenFstr.vp:f-string 32 位整型不再写 l 修饰符
- N2 撤回 ExprGenOps.vp:删除在 _node_type_unsigned 热点查/填 vpi 缓存的
  _call_ret_annot_unsigned(有副作用,触发 fat32_diskio.vp match 路径崩溃)
2026-09-11 17:39:21 +08:00
TermiNexus e66d9e3adb fix(tpv): 修 N 系列 codegen 缺陷(常量位宽/无符号判定/实参扩宽/结构体取址)
- N1 ExprGenConst/CallGenDispatch: CONST_INT 按 target_ty 位宽物化, 修 uint64(0xFFFFFFFFFFFFFFFF) 被降 i32 再 zext

- N2 ExprGenOps: 新增 _call_ret_annot_unsigned 查被调方 vpi 返回注解, 修 uint32 返回值 >> 生成 ashr

- N3 TypeGenResolve: 联合注解提取跳过存储类限定宏, 修模块级全局 uint32 比较生成 sge/slt

- N5 CallGenLib: 常量实参按形参扩宽 + _coerce_args_to_def_params 支持 3 段方法调用

- N17 CallGenLeafKit/CallGenMixin: 默认值表按定义模块 sha1 限定 + 收紧交叉校验守卫

- N18 ExprGenBase/CallGenLib: rvalue 结构体实参落栈取址
2026-09-11 15:50:09 +08:00
TermiNexus 751fbccb29 feat(tpv): vtable 对象全局 storage 也按字段默认值折叠
补 f5ee178 未覆盖的 vtable 分支:抽出 _build_stg_struct_body_text,_build_stg_struct_init_text 改为其包装;vtable 分支以 start_idx=1 复用默认值折叠,字段默认值全零时回退原零值循环(元素个数语义不变)。
2026-09-11 15:29:43 +08:00
TermiNexus f5ee178407 feat(tpv): 模块级对象全局按字段默认值生成 storage 初始化器
- 新增 _default_val_const_text:字段默认值 AST -> 常量文本(仅整型/字符/负号字面量可折叠,其余零值兜底)

- 新增 _build_stg_struct_init_text:按 StructEntry 字段默认值生成完整 struct 常量初始化器(LLVM 要求列全字段);无非零默认值返回 None

- storage 分支新增 elif:无 vtable 对象全局走默认值初始化器(vtable 路径不变)

- 效果:fat32.default_fs 的 .stg 由 zeroinitializer 变为含 sector_size=512/root_cluster=2/free_clusters=0xFFFFFFFF/backup_sector=6/winsect=-1 的完整常量;drive_lock 等零值等价场景保持 zeroinitializer
2026-09-11 14:52:29 +08:00
TermiNexus e20a6f3d11 fix(tpv): 修 VKernel 暴露的 codegen 缺陷(浮点 CDefine/__sizeof__ packed/CONST_STR/alloca/无符号/ABI)
- 浮点 t.CDefine 初值序列化与源恢复(gfx PI 编成 0.0)

- __sizeof__ 按 packed 计算(cpu _tss memset 越界写 4B)

- 数组初始化器支持 CONST_STR 单字符;模块级别名不再产出 global i32 0 假全局

- 临时缓冲/构造临时量 alloca 提升至入口块(B6 未覆盖路径)

- 无符号判定补 CSizeT/小写别名/结构体字段 + udiv/urem + range 位宽与符号 + zext 扩展

- 跨模块 declare 形参按 vpi 定义侧收敛(修 _vpi_method_param_annot 拒绝 cls_name=None 导致 coerce 恒失效)
2026-09-11 14:26:54 +08:00
TermiNexus bd46b47b22 docs(readme): 按事实重写 README, 反映自举已完成与真实源码结构
- 自举状态: 重写中 -> 已完成自主编译, TransPyC 明确为自举种子
- 目录结构: 全文 .py 名称纠正为真实 .vp 布局 (Phase/ Stub/ core/Gen/ 树, Gen 52 文件)
- 工程配置: project.json -> project.vpj; 产物路径 output/ -> .tpv_build/output/
- CLI 表补齐 --asan/--phase2command/--nowarn-register/--no-llvmpeephole/--detection/--show-args/--build-dir/--version
- 内存模型: 补常驻池 512 MiB 与 CompilerFactory 单例, 说明自举后 _mbuddy 需逐模块注入
- 规模数据改为实测值 (App 76 文件 64656 行 / Gen 52 文件 / Test 39 用例)
- 修复失效链接 wiki/01-overview.md
2026-09-10 22:33:23 +08:00
TermiNexus a5260f4441 fix(tpv): ASan 运行库改由 project.vpj 声明; read_all 容量语义修正与超限源文件 fail-fast
- --asan: 移除 BuildPipeline 中硬编码的 clang64 libclang_rt 注入, 改由工程在 linker.static_libs / dynamic_libs / flags 中自行声明; 新增 Config.asan_runtime_declared(), main.vp 在未声明时 fail-fast 并列出所需运行库, 不再给含混的 undefined reference
- read_all: 容量参数含终止符(最多读 cap-1 字节), 故删除 12 处恒不可达的 else 截断兜底; 并在读取前按 f.size() fail-fast 拒绝超限源文件(main.vp / BuildPipeline / Phase1Main / Phase1Scan / Phase2), 避免静默截断后报出莫名其妙的语法错误
- project.vpj(根 / Test / NegativeTest / Sha1Test): linker.cmd 由裸 clang++ 改为绝对路径并补 -fuse-ld=lld
2026-09-10 22:33:18 +08:00
TermiNexus 76f03b5987 fix(tpv): 修模块级浮点全局零初始化、跨模块 CDefine 重导出、默认参数快照、typedef 诊断悬垂指针
- TranslatorGenMixin/AssignGenMixin: 模块级全局补 CONST_FLOAT 初始化分支。
  此前无此分支 → init_kind 保持 0 → t.CDouble 全局一律退化成 zeroinitializer
  (torch 的 LCG_A/LCG_M/_rng_state 全变 0 → _next_rand() 得 0.0/0.0 = NaN
  → Kaiming 初始化写入全 NaN 权重 → 推理 softmax 分母 NaN → 十个概率全为 0)。
  浮点值不走 AST 的 float_val(会被 pool 操作覆写),改为按变量名从当前源文件
  重读恢复;_recover_cdefine_float 泛化为 _recover_float_literal。
- ExprGenCdef: 跨模块 CDefine 重导出链支持绝对导入(from pkg.sub import *),
  此前只认相对 from .X → vqt6 包(__init__.vp 用 from vqt6._types import *)
  里的 CDefine 全查不到,报 "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'";
  相对导入基准改用"当前模块所属包"而非模块名本身,修正子模块 from .X 拼接
  (对齐 CallGenBase._lookup_reexport_from_vpi 的包名口径)。
- CallGenLeafKit: 默认参数常量快照支持一元字面量(USub/UAdd/Invert 且操作数为
  整型 Constant)。此前只认 Constant/Name → ex3: t.CInt = -1 这类默认值快照为空,
  跨模块重建时报 "跨模块默认参数无法重建(快照缺失)"。其余形态仍保持 kinds=0,
  重建时 fail-fast,不静默兜底。
- TypeGenCType/TypeGenResolve/TypeGenBase: builtins typedef 诊断串改为自有缓冲
  副本(stdlib.malloc,clear_ctypedefs 释放)。调用方常传临时段缓冲,直接存指针
  会悬垂,_build_ann_fail_msg 的 snprintf 读已释放内存(ASan heap-use-after-free)。
2026-09-10 14:22:02 +08:00
TermiNexus adfd202218 feat(tpv): 支持 --no-llvmpeephole 关闭 llvmlite 本地窥孔(T2/T3)便于 IR 对比; 移除 project.json(改用 project.VPj) 2026-09-09 21:10:20 +08:00
TermiNexus 1d76280d0e perf(tpv): find_func_in_table 两遍扫描合并为单遍 2026-09-09 10:38:34 +08:00
TermiNexus bb9bb76653 fix(tpv-asm): c.Asm clobber 独立上限 ASM_MAX_CLOBBERS=32,修复 >16 项 clobber 截断致寄存器冲突崩溃 2026-09-09 10:38:34 +08:00
TermiNexus 7611d10072 fix(stubgen): CONST_STR 引号改用字符串字面量,修复 char 隐式转指针 2026-09-09 08:57:29 +08:00
TermiNexus 3e4f3b88b0 fix(tpv-class): lookup_field_by_class 识别 16hex 类键前缀为 sha1,修复 GSList[Param] 模块解析误报 2026-09-09 08:57:29 +08:00
TermiNexus 82558bd86b fix(tpv-expr): list 内置内联按元素原型类 load,支持链式 get(0).method() 分派 2026-09-08 21:30:56 +08:00
TermiNexus 709c3570fb fix(tpv-class): REnum 自引用字段注册后回填别名类型,修复 match 绑定变量退化为整数 2026-09-08 21:30:56 +08:00
TermiNexus ab814f55e6 fix(tpv-call): 裸名调用同模块函数优先(owner_sha1),修复跨模块同名 stub 抢占致 _advance 误绑 2026-09-08 20:47:54 +08:00
TermiNexus a260b9316d style(stubgen): Converter.vp 去除 .vpi 类/函数定义后多余空行 2026-09-07 22:35:21 +08:00
TermiNexus 1146719de1 fix(generic): 泛型显式实例化 Box[t.CInt](v) 实参提取 Attribute 直读字段
TypeGenResolve._get_type_name_from_annotation 的 Attribute 分支改用 at.attr/at.value 直读:ast.attribute_get_attr 等跨模块访问器在 Phase A 阶段 text.ll 未生成时为 stub 无体返回 None → tn_raw 为空 → Box[t.CInt](v) 泛型类型实参提取失败 → 构造调用 func 名 '?' → RHS None(GenericTest4 main.vp:22 根因)。与 Name 分支 L53-55 同源;同时移除 CallGenCtor 残留 [EXTRACT-DBG] 诊断日志。
2026-09-07 22:11:05 +08:00
TermiNexus 4496738a24 fix(asm,irbuf): c.Asm 输出类型收集时缓存 + 大模块 IR 缓冲 1MB→8MB
1) c.Asm 输出操作数类型改为收集时直接从 alloca 缓存(单输出 scalar、多输出按序数组 out_ptys),不再依赖从 pool AsmOperand 槽二次读回 Value.Ty.Pointee。修 atom/__atomic_test_and_set 与 vrandom rdrand*_step:此前槽读回不稳定 → ret 静默 Void → 发射带名 void asm(llc 'instructions returning void cannot have a name')或报输出操作数类型缺失。2) 超大模块 IR 截断:ast/parser.vp 的 text.ll >1MB,Phase1(PHASE1_IR_SIZE)/Phase2(PHASE_A_IR_SIZE、PBP_IR_SIZE) 写缓冲与 StubMergerBuild 读缓冲(STUB_READ_BUF_SIZE) 均 1MB 单次截断 → 合并 IR 尾部丢字(opt 'end of file in global variable name');统一提到 8MB。
2026-09-07 22:06:26 +08:00
TermiNexus bcedb15b23 fix(generic,rowptr): SlotsBugTest 编译修复——T.__sizeof__ 特化与 rowptr[sha1.Class] 注解解析
1) CallGenDispatch __sizeof__:指针包装判断(X|t.CPtr 联合与 rowptr[X])移到 dot-strip 之前。类型实参规范化名如 rowptr[7565e081d88517ac.AST] 内嵌 '.',strrchr('.') 会把括号内 .AST 剥成 'AST]',rowptr[ 前缀判断失配 → 落回把接收者 Name(T) 当实例翻译 → NameError 'T'(list[rowptr[AST]] 特化 list.__init__ 根因)。2) TypeGenResolve:a) 注解 Name 分支支持 '{16hex}.Class'(_norm_type_arg_name 产物,模块段即 SHA1)经 find_struct_by_module 直解;b) _parse_type_string 下标内层以数字开头(sha1 前缀类名)且单实参无嵌套/无逗号时手工拆 base+slice 递归解析——完整 lexer 会把 7565e081d88517ac 当数值误扫 → slice 变 Constant 占位 → 特化方法 append 参数 'item' 类型解析失败。3) CallGenMixin:泛型类模板缺失分支补内建下标 cast:(rowptr[list[int]])(x)/(t.CPtr[T])(x)/(t.CArray[i8,N])(x) 按 resolve_annotation_type 出类型后单参 _translate_t_type_cast,替代误报 '泛型类 rowptr 未找到模板'(ast/base.vp L392 根因)。
2026-09-07 19:36:57 +08:00
TermiNexus 41ed7176e9 fix(codegen): __sizeof__ 缺 rowptr[X] 指针尺寸分支,泛型特化 T.__sizeof__() 报未定义名称 'T'
list[rowptr[AST]] 特化翻译 list.__init__ 的 T.__sizeof__() 时,T 解析为新一代指针包装 rowptr[AST],尺寸表全部 miss 后落回把接收者 Name(T) 当实例翻译 → NameError: 未定义名称 'T'(SlotsBugTest ast 包 rowptr[list[rowptr[AST]]] 泛型特化根因)。在旧语法联合 X|t.CPtr → 8 分支旁补 rowptr[ 前缀 → 8(指针大小),与联合类型口径对齐。
2026-09-07 15:31:18 +08:00
TermiNexus ed373a3ef2 基线重置,修复了一些 bug 2026-09-07 14:46:48 +08:00
TermiNexus 6b368b46a7 feat(tpv): App 与 TPVAppBK 新代版本对齐(rowptr 配套实现全套)
把 TransPyV/App 覆盖为 TPVAppBK 内容(39 文件):6 参注册式 _BuildReachableSha1Set 全套(StubMergerBuild/Store/Collect/__init__/IncludesScanner/Phase1Scan 等)、CallGen 与 TypeGenResolve 等新代实现。配套根 includes 为 rowptr 工作区(另有 includes_rowptr_bk 备份)。
2026-09-05 14:13:03 +08:00
TermiNexus c35246f426 fix(phase1): 旧 5 参 def 前补全量 includes 预注册(1a-pre),修复'无法构建 SHA1 映射'
178ea40 重构把全量 includes 扫描/注册(PopulateSha1MapStore + 逐文件 import 依赖 PopulateIncludesDeps)从 Phase1Main 移除,依赖注册式 def 在 BFS 中自行登记。改用旧 5 参只读 def(调用点同步改回 5 参)后 store 无预注册 → _BuildIncludesSha1Map 返回 0 → '无法构建 SHA1 映射' 终止。

按 ab0f37e 1a-pre 模式补回:scan_includes 全量扫描 → PopulateSha1MapStore 登记 store → 逐文件 declare_only=2 解析 import 依赖(含 _inject_auto_container_visibility)→ PopulateIncludesDeps(文件级 mark/reset 防 OOM)。_BuildReachableResult 增加 reachable_set/count 过滤,result 只保留可达 includes 子集(1a/1b 只翻译可达文件)。
2026-09-05 12:56:10 +08:00
TermiNexus 50d53d3ce6 fix(link): 补 CallGenLib._promote_clib_call_args 定义;BuildPipeline PYI_BUF_SIZE 改 t.CDefine 宏
CallGenMixin.translate_call 调用 self.Gen.CallGenLib._promote_clib_call_args(B3 裸名 C 库函数第三参 i64 提升),但 CallGenLib 缺该方法 → ld.lld undefined symbol。按 TPVAppBK 旧语法移植完整方法。

PYI_BUF_SIZE 是跨模块共享裸全局(@PYI_BUF_SIZE),Phase1/__init__ 与 BuildPipeline 两模块各定义强符号 → duplicate symbol。BuildPipeline 侧改用 t.CDefine 宏(同 SRC_BUF_SIZE),不再产生全局。
2026-09-05 12:48:03 +08:00
TermiNexus 8dda9f7c2d feat(linker): TPV 支持 project.vpj linker.dynamic_libs 动态链接 .dll/.so(Config 解析+绝对路径解析+链接命令合并 static/dynamic,Viper str+= 拼接改用 snprintf) 2026-09-01 22:01:20 +08:00
TermiNexus 75db09e18a fix(call): 跨模块返回类型解析兜底修复——star import 候选验证失败时先验证原模块(stdlib.malloc extern def 不再误归 stdint);包模块 vpi 未命中时经 reexport 定位定义模块查返回注解(fltk.Fl_Input_value 返回 str 不再退化 i32) 2026-09-01 21:23:12 +08:00
TermiNexus 131f3a9a4d fix(phase1): 移除 [CYCBREAK] 临时诊断 printf 2026-09-01 21:07:24 +08:00
TermiNexus 178ea401b7 fix(phase1): 含 class 定义的 includes 模块强制翻译,修复增量编译跨模块类构造 find_struct_by_module 落空(fltk.Fl_Window 返回类型 fail-fast) 2026-09-01 21:06:31 +08:00
TermiNexus c7c37e1007 fix(call): star import 再导出改为多候选验证定义模块
- _lookup_reexport_from_vpi:from X import * 累积全部候选(空格分隔),不遇首个星行即返回

- CallGenMixin:按候选逐个验证 is_cexport_func/_is_state_func_from_vpi 确认定义模块(Fl_init_all→_cfl)

- 修复 fltk/__init__ 依次 from _types/_enums/_cfl import * 时定位到首个模块(_types)的误判
2026-09-01 20:39:19 +08:00
TermiNexus 60deee1d17 fix(call): _lookup_reexport_from_vpi 支持 star import 再导出定位
- from X import * 的 re-export(如 fltk/__init__ 再导出 _cfl 全部 extern FFI)此前无法定位定义模块

- fltk.Fl_init_all 被误加 SHA1 前缀走 mangled 精确解析 → extern def 无 define → fail-fast

- 命中后由 is_cexport_func/_is_state_func_from_vpi 二次校验裸名
2026-09-01 20:26:14 +08:00
TermiNexus 29667672e4 feat(linker): TPV 支持 project.vpj 静态链接 .a/.obj
- Config.vp 新增 LinkerStaticLibs(读取 linker.static_libs 数组并基于 project_dir 解析绝对路径)

- BuildPipeline link_obj_to_exe/link_objs_to_exe/run_pipeline 支持 static_libs(位于 .obj 之后、-l 之前)

- main.vp/Phase2.RunMultiFileProject 全链传递 linker_static_libs
2026-09-01 20:15:26 +08:00
TermiNexus 9b46e1da78 fix(compiler): for x in ptr/数组 实现解引用为空终止(指针/浮点元素支持 + 数组长度上限+NULL终止) 2026-08-30 08:37:39 +08:00
TermiNexus d14415a4e6 fix(compiler): asm 约束映射补全 8 位寄存器 clobber(AL/AH/BL/BH/CL/CH/DL/DH) 2026-08-30 08:12:44 +08:00
TermiNexus a0d6dced00 fix(compiler): va_arg 截断回目标宽度(%d 高位垃圾)+ 比较按无符号类型选 ULT/ULT/UGT/UGE 2026-08-30 07:54:24 +08:00
TermiNexus 4db68ff5c4 fix(compiler): 修复模块级全局初始化数据丢失(-1 与字符数组全 0) 2026-08-30 07:27:40 +08:00
TermiNexus 23e7f5b1fb fix(compiler): storage vtable 初始化改完整 struct 初始化器(LLVM 拒绝部分初始化)
llc 报 initializer has wrong # elements。__vtable__ 字段(索引 0)= i8* @vtable,其余用户字段按类型生成零值(Ptr→null/Int→0/Float→0.0/Array→zeroinitializer),列全 FieldCount 字段。
2026-08-29 18:46:18 +08:00
TermiNexus fa4c69f95d fix(compiler): storage 对象类全局初始化 vtable 首字段(@t.CVTable)
storage 方案 @g.stg 全零初始化 → @t.CVTable 模块级全局对象 vtable=0 → 用户程序入口虚调用 call [vtable+0x10] 跳 0x0 #PF(HelloWorld 启动即崩)。@g.stg 初始值改为 { i8* @Class.vtable }(首字段 vtable 地址,其余零),无 vtable 的 @t.Object 保持 zeroinitializer。
2026-08-29 18:39:28 +08:00
TermiNexus c2b9a26c62 fix(tpv): vpi 注入完整类型定义 + list elem_size + 路径修复,清理临时诊断
- VpiReader: 值 struct 全局/数组元素/函数返回类型注册完整定义,修复 llc use of undefined type;vpi 注入类后补 add_field 字段表(LARGE_INTEGER.QuadPart 恒 0 根因)
- TypeGenResolve: list[str](pool, elem_size) 语义修复(参数个数误当 elem_size,Mat4 崩溃根因);t/c 内置命名空间 Subscript 特判
- Config: _join_path 防双份路径
- GenApi: 新增 add_field 代理
- 移除 DBG-VK/PHASEADBG/GAP-DIAG/DBG-EMP 临时诊断
2026-08-29 17:27:29 +08:00
TermiNexus 1fe7878074 fix(compiler): 对象类模块级全局改静态 BSS storage,修复页 0 混叠与栈悬垂
对象类全局(裸类名注解)此前编译为 NULL 指针,实例建在栈 alloca 后悬垂,对象字段落在物理页 0(IVT+BDA 混叠),导致 VKernel devfs/fat32/sched 全部对象失效。新增 @g.stg 静态 storage(BSS),@g 编译期指向它;赋值/构造点(translate_assign/translate_annassign 全局分支)检测 storage 后绑定引用并复制对象,消除 NULL 与悬垂。
2026-08-29 17:27:21 +08:00
TermiNexus 2b2c242d14 fix(tpv): extern declare 返回类型含真实类型时不再误归 Void + 白名单兜底
vpi 注入 extern declare 时 has_state→Void 无条件触发:注解 HANDLE | t.State(FindFirstFileA/GetStdHandle)被归为 void → declare 返回 void,运行期 FindFirstFileA 句柄丢失 → 文件存在性判断失效(文件实际存在但报不存在)。修复:仅纯标记注解归 Void;含真实类型但解析 miss 时回退外部函数返回类型白名单(FindFirstFileA/GetStdHandle→i8*),GenApi 新增 infer_external_func_ret_ty 委托。
2026-08-29 12:03:12 +08:00
TermiNexus efc8f7948f fix(tpv): void 函数 return <expr> 丢弃值 + ret void(llc ret i32 vs define void)
GenApi.add_global_name 声明 -> None 但 body 为 return g.TranslatorGen.add_global_name(...)(委托返回 i32)→ translate_return 对带值 return 直接 build_ret(val) 生成 ret i32,与 define void 冲突 → llc 'value doesn't match function result type void'。修复:函数返回类型为 void 时 return <expr> 仅求值丢弃 + ret void(Python 语义)。
2026-08-29 11:12:23 +08:00
TermiNexus b7eeb504ee fix(tpv): 空类 FieldCount=0 中和重建 + 移除 [ATTR]/[DET] 调试打印
1) WIN32_FIND_DATAA 等空类(FieldCount=0)字段访问:模块限定路径补 FC==0 容忍(返回 None 交 pending,对齐裸名路径);ClassGenDef 空类不再 pending 延迟(多遍死锁),改为中和旧 entry 走完整注册重建。2) 用户要求移除 [ATTR](FuncGenBase/FuncGenDef)与 [DET](CallGenCtor)无条件调试打印。3) 拓宽 [DBG-REG]/[DBG-ADDF] 过滤器到 WIN32_FIND_DATAA 定位空类注册来源。
2026-08-29 10:44:43 +08:00
TermiNexus a99db2a29b fix(tpv): REnum 变体引用作值返回 tag 常量 + kind() 注解兜底
1) 表达式中的 REnumClass.Variant 无括号引用(llvmlite.LLVMType.Struct)此前未处理 → 与 kind() 返回的 i32 tag 比较时条件翻译失败(TranslatorGenMixin L517 根因)。仿枚举成员访问,translate_attribute 增加变体引用 → tag 常量。2) kind() 特殊处理在运行时类型退化(联合 i8*)时无法识别 REnum 类,补变量注解类名兜底。
2026-08-29 10:28:39 +08:00
TermiNexus 58d61badcb fix(tpv): 跨模块调用返回类型解析支持 import 别名(GenApi.translate_printf_call)
模块限定调用 GenApi.translate_printf_call 的 mod_ret_sha1 用 LookupSha1ByModName(别名) 查不到 → None → 误走裸名外部函数路径 fatal_if_unknown=1 fail-fast 误杀。改经 _resolve_module_sha1(LookupModNameByAlias 别名还原 → 精确/后缀)解析,与 L620-629 模块归属校验的既有模式一致。
2026-08-29 10:14:32 +08:00
TermiNexus 8d0241e0c3 fix(tpv): lookup_field_by_class 模块限定路径补 REnum 变体字段别名回退
llvmlite.LLVMType.Pointee 等模块限定 REnum 字段访问:类命中(FieldCount=4)但变体字段名(_p1/_p2 槽位别名)不是普通字段,此前变体别名回退只在裸名路径,模块限定路径 fatal 先触发永远走不到(CallGenDispatch L1005 根因)。在模块限定路径字段未找到时补 _lookup_renum_field_alias 回退,与裸名路径一致。同时移除已定位完成的 REEXPORT-DBG 临时诊断。
2026-08-29 10:03:10 +08:00
TermiNexus 9af7b2ead4 fix(tpv): from . import Y 跳过包目录名 SHA1 校验(无 __init__.vp 目录包)
from . import CallGenBase(CallGenCall.vp L12)解析后 resolved 是包目录名 lib.core.Gen.Call——无 __init__.vp 的目录包也允许这样导入,目录本身不是模块文件、无 SHA1,对它做 _check_import_sha1 恒失败误报。真实目标子模块 pkg.Y 由下方 LookupSha1ByModName/本地兄弟文件分支逐个校验,此处跳过包级校验即可。
2026-08-29 09:48:42 +08:00
TermiNexus d3bdfd5205 fix(tpv): 修复包重导出两个前置判定 bug(level 判定 + __init__ 后缀偏移)
1) 相对导入判定改 sifr.level>0:parser 把前导点存 level,module 字段不含点,原 sifr_mod[0]=='.' 恒假,走绝对分支查不到 ast.exprs,sub_sha1=None 重导出被跳过。2) __init__ 判定改 strstr:原 strcmp(rel_path+len-9) 对 ast/__init__.vp 指向 init__.vp 恒不匹配,is_pkg=0。两 bug 叠加致包重导出从未触发,ast.Attribute 未注册(AssignGenMixin L46 fail-fast 根因)。
2026-08-29 09:32:12 +08:00
TermiNexus 25363120f8 debug(tpv): 临时诊断定位 ast.Attribute 包重导出失败根因(REEXPORT-DBG) 2026-08-29 09:15:33 +08:00
TermiNexus 99ddff2d87 fix(tpv): t.CArray/t.CPtr 下标元素解析失败改为返回 None 交多遍收敛
阶段2 删 map_t_type stdint 硬编码后,UINT 等 typedef 仅经 CTypedef 动态表解析;Phase 1a(declare_only=1)无 vpi 注入,依赖 typedef 的类(zhuff_code)被跳过,导致 t.CArray[zhuff_code, N] 元素解析被误 fatal 中断编译(zhuff.vp L19 根因)。与裸名 CArray 路径及 ClassGenDef 字段 pending 机制保持一致,真实类型错误由 Phase A 收敛检查暴露。
2026-08-29 09:01:42 +08:00
TermiNexus 0a84ca8db3 debug(tpv): 临时诊断定位 SECURITY_ATTRIBUTES 空类注册来源
- register_struct/add_field 打印 [DBG-EMP](SECURITY_ATTRIBUTES 过滤)
- ClassGenDef 空类报错改为 pending + 打印(定位期间不中断编译)
- 定位后移除
2026-08-29 08:42:26 +08:00
TermiNexus 7b81e3c7cf fix(tpv): vpi 注入不再注册空类(FieldCount=0)——避免阻塞源模块真实定义
- vpi 存根不含类字段(只序列化 class 名 + def 签名),_inject_class_struct_declare
  的 field_count 恒为 0 → 注册空类
- 空类在源模块编译时被 find_struct_by_module 命中 → "结构体已注册但字段数为0"
  InternalCompilerError(win32base.SECURITY_ATTRIBUTES 根因)
- field_count==0 时不注册:依赖已编译命中真实类(L1604-1608),未编译走
  _deps_pending 多遍收敛
2026-08-29 08:20:26 +08:00
TermiNexus 6540e27844 fix(tpv): translate_attribute 读路径字段未找到时标记 _deps_pending
- fileio.vp li.QuadPart 报错:LARGE_INTEGER(win32base)未编译时只有 vpi
  空 stub,读路径字段查找失败未标记 pending → 直接 fatal 中断多遍收敛
- 读路径对齐写路径:字段未找到 → 标记 trans._deps_pending=1 下遍重译
2026-08-29 08:14:25 +08:00
TermiNexus 20ba8e0297 fix(tpv): 字段访问依赖未就绪时标记 _deps_pending 而非 fail-fast
- subprocess.vp sa.nLength 报错根因:SECURITY_ATTRIBUTES 定义侧(win32base)
  未编译时只有 vpi stub 空类(FieldCount=0),lookup_field_by_class 裸名
  回退命中空 stub → 字段未找到 → fatal_error 中断多遍收敛
- lookup_field_by_class:期望 sha1 未匹配且命中 FieldCount==0 空 stub →
  返回 None(不 fatal),交给上层 pending
- get_attribute_ptr 写路径:字段未找到 → 标记 trans._deps_pending=1 下遍
  重译(对齐读路径/AssignGenMixin L1793),真实错误由 Phase A 收敛检查暴露
2026-08-29 07:46:15 +08:00
TermiNexus 0b0cf5ba66 refactor(tpv): _parse_type_string 用 Expression 注解访问 body(源头修正后简化)
- ast.parse_expression 返回注解已改为 Expression | t.CPtr(includes/ast),
  expr_node 直接以 Expression 注解声明,去掉强转与 kind 检查
2026-08-29 07:26:12 +08:00
TermiNexus f75a4d795a fix(tpv): _parse_type_string 的 expr_node.body 修正——Expression 包装强转访问
- parse_expression 返回 Expression 节点,body 是 Expression 类字段
  (stmts.vp),ast.AST 基类无 body 字段(仅 children)
- 原注解 ast.AST 直接访问 expr_node.body 类型错误;补 kind 检查 +
  (ast.Expression) 强转后访问
2026-08-29 07:23:13 +08:00
TermiNexus 9e72393a11 fix(tpv): ExprGenName 三处 LLVMType.Int8() 模式修正为 Int(bits)+bits==8
- LLVMType 是 REnum,整数变体统一为 Int(Bits 字段),无 Int8 变体;
  Int8 仅是模块级工厂函数(返回 Int(8)),LLVMType.Int8 类成员不存在
- case llvmlite.LLVMType.Int8(): 永不匹配 → T|t.CPtr 退化 i8* 的
  8 字节步长 GEP 特判静默失效(走 i8 1 字节步长,指针截断)
- 三处(L858/1049/1087)改为 case Int(bits) + bits==8
2026-08-29 07:19:36 +08:00
TermiNexus ec4fb0bcf8 refactor(tpv): ClassGenVTable 的 self._mangle_name 统一为 self.Gen.FuncGenBase 显式调用
- 继承链 ClassGenVTable→ClassGenBase(无父类)不含 FuncGenBase,
  5 处 self._mangle_name 沿 MRO 解析不到定义(依赖编译器兜底)
- 统一为 self.Gen.FuncGenBase._mangle_name(对齐项目其他 Mixin 写法)
2026-08-29 07:16:43 +08:00
TermiNexus 98efd9b1d2 fix(tpv): __sizeof__ 模块限定类按模块归属匹配,防跨模块同名类误匹配
- w32.win32base.SECURITY_ATTRIBUTES.__sizeof__() 仅取 attr 丢失模块前缀,
  get_struct_type 裸名全局匹配误命中跨模块同名类(FieldCount=0)
- Attribute 形式保留完整模块路径(w32.win32base.SECURITY_ATTRIBUTES),
  结构体查找先按模块归属 find_struct_by_module 精确匹配,miss 再裸名
2026-08-29 07:06:11 +08:00
TermiNexus 8a69719acf fix(tpv): 裸名 typedef 强转改 resolve_typedef_or_map 统一口径(BYTEPTR 等)
- 阶段2 删除 map_t_type 的 stdint 别名后,BYTEPTR(x) 裸名 typedef 强转
  判定 map_t_type 返回 None → 误走外部函数路径 fail-fast
  (string.vp L104 TypeError 根因)
- 裸名 typedef call(BYTEPTR/HANDLE/size_t 等)改经 resolve_typedef_or_map:
  CTypedef 动态表优先 + t/c 核心 map_t_type 兜底,所有 CTypedef/t 库类型
  call 恢复强转语义
2026-08-29 06:22:30 +08:00
TermiNexus fa90586a83 refactor(tpv): 类型系统收敛——正常库分析优先,t/c 库内部特判兜底
- 阶段1a:Name 分支 from-import 别名反查(from t import CInt as XXX 可解析,
  支撑 YYY: t.CTypedef = XXX 复杂别名链)
- 阶段1b:Attribute 路径 CTypedef 动态表优先(stdint.INT 走库定义)
- 阶段2:map_t_type 删除与 stdint.vp 重复的 ~70 个硬编码别名(保留 t/c 核心
  原语 + float/double + HANDLE/LPCSTR/LPCWSTR),新增 resolve_typedef_or_map/
  get_type_size_bytes 统一口径 + GenApi 委托,改造 VpiReader/ClassGenEnum/
  TypeGenErr 旁路调用点
- 阶段3:__sizeof__/REnum 尺寸/无符号名单三张派生表加 CTypedef 动态兜底
2026-08-29 06:15:00 +08:00
TermiNexus 2610f361fd fix(tpv): 泛型特化名分裂——嵌套泛型实参递归 SHA1 规范化(_Type.md 问题 7)
- _norm_type_arg_name 顶层 '|' 分割改括号感知(list[A|B] 不再误分)
- 新增嵌套泛型实参递归:list[list[AST]] → list[list[{sha1}.AST]],
  内部按顶层 ',' 分割逐个 SHA1 规范化
- 所有 module 表示统一为 Hash Namespace({SHA1}.Class),消除跨模块
  特化名分裂(undefined symbol)
2026-08-29 05:51:49 +08:00
TermiNexus 4415f1f38b fix(tpv): 窄化截断加常量溢出诊断(_Type.md 问题 8)
- coerce_to_type 宽→窄 trunc 前检测编译期常量正溢出目标位宽无符号
  范围 → fatal_error(OverflowError),消除 buf[idx] = 0x1FF 类静默截断
- 负常量按模 2^bits 语义放行(-1 → 0xFF 合法);非常量运行时窄化保留
  trunc(值域可能安全,无法静态判定)
2026-08-29 05:48:15 +08:00
TermiNexus 4748915472 fix(tpv): 外部函数返回类型不可收敛时 fail-fast,豁免 from-import 与多层模块限定调用
- _infer_external_func_ret_ty 白名单未命中改为 fatal_error(Python 风格
  traceback),移除默认 i32 的 WARN 输出
- 裸名调用 fail-fast 前查 from_imports 可解析性:from-import 函数走
  mangled 精确解析收敛(SSAValue 场景),纯外部 C 函数才 fail-fast
- mod_ret_sha1 解析支持多层模块路径(w32.win32file.CreatePipe 场景),
  避免嵌套模块限定调用被误判为裸名外部函数
- 移除 FuncGenBase/StructGenMixin 遗留调试输出
2026-08-29 05:46:25 +08:00
TermiNexus 5551db3432 fix(tpv): try_extract_callable_ret 调用多传第 6 个参数(签名仅 5 参数)
- ab0f37e 引入:误加尾参 0(疑为 depth 参数),签名实为 (pool, annot, imported_modules, from_imports, trans)

- 删除多余参数,恢复 5 参数调用
2026-08-28 15:35:28 +08:00
TermiNexus 85e7011b6c refactor(tpv): INTPTR/UINTPTR 去硬编码,依赖 stdint.vp CTypedef 声明
- map_t_type 删除 INTPTR/UINTPTR = Ptr(i32) 硬编码(与 stdint.vp L10/L14 声明重复)

- INTPTR = t.CInt|t.CPtr、UINTPTR = UINT|t.CPtr 由 CTypedef 动态查表解析(L1377 先于 map_t_type 兜底)

- stdint.vp 成为单一事实源;语义不变(INTPTR 是 int* 历史命名,非 C 标准 intptr_t 整数)
2026-08-28 15:33:45 +08:00
TermiNexus 8445af93d2 fix(tpv): 泛型污染检测改 token 边界匹配(根治 strstr 误杀合法特化)
- _list.vp L57 elem_ptr: T | t.CPtr 在 list[AST|t.CPtr] 特化时被误报污染:strstr('e9dfb296a8674aee.AST|t.CPtr', 'T') 把 AST 内部字符 T 当参数名

- 新增 _str_contains_ident:独立标识符检测(两侧非字母/数字/_),实参内部字符不算污染

- T→T 完全自映射仍直接报错(明确污染);子串场景按边界判定
2026-08-28 15:06:30 +08:00
TermiNexus 27ba74c720 fix(tpv): REnum 别名表去指针化(class 槽位改 perm_pool 字符串 + strcmp 内容比较)
- _register_renum_field_alias: class_name strcpy 到 perm_pool,去重检查改 strcmp

- _lookup_renum_field_alias/_lookup_renum_field_alias_ty/_lookup_renum_member_ty: 指针值比较改 strcmp 内容比较

- 删除 _update_renum_alias_class_ptr 及调用点(去指针化后无需指针统一)

- 根治 'LLVMType 字段 Pointee 未找到' 反复报错(注册 AST 指针 vs 查找 entry.Name 池拷贝指针恒不等)
2026-08-28 15:00:50 +08:00
TermiNexus a8eb7fe5fe fix(tpv): 类型静默退化改编译期报错 + 统一整数拓宽 zext/sext
- FuncGenDef: 有返回注解但解析失败 → fatal_error(不再静默回退 i32;无注解函数保留 i32 默认)

- TypeGenResolve: 泛型类型实参污染(T→T 自映射 / 实参含类型参数名)→ fatal_error(不再 return None 静默退化)

- ExprGenBase: coerce_to_type 窄→宽接入 _zext_or_sext_val(i1/i8 默认 zext、无符号类型名 zext、否则 sext),修正 t.CUInt32T 等无符号值被符号扩展的隐蔽数据损坏
2026-08-28 14:57:46 +08:00
TermiNexus 24ae2dc966 refactor(tpv): 删除 Handles 目录,核心逻辑合并进 Gen
- 新建 Gen/Translator.vp:Translator 类 + Mixin + 常量 + _mbuddy 自 HandlesTranslator 迁入(删 7 个委托壳与 _g_gen)

- GenApi 下沉 7 个 global/nonlocal 管理委托(add/is_global_name、add/is_nonlocal_name、get_nonlocal_type/index、clear_scope_names)+ update_global_func_typestr

- CallGenLeafKit 并入 ExprCallHandle 类,NewExprCallHandle 改本地引用

- 删除 Handles/HandlesExpr/ExprCallTable/HandlesBase(60+ 委托零消费者/纯委托壳)与 Handles 目录整体

- 全 App 56 文件 import 改指 lib.core.Gen.*,清理死 import,委托调用改 GenApi,main/BuildPipeline _g_gen 注入删除
2026-08-28 14:29:25 +08:00
TermiNexus d99b6268de refactor(tpv): Gen 目录按前缀分组迁移子目录 + REnum.kind() 编译期转 __tag
- Gen/*.vp 按前缀分组迁入 Call/Class/Expr/Func/Stmt/Type 子目录,import 同步更新(BuildPipeline/Generator/StructGenMixin 等)

- CallGenDispatch: REnum.kind() 前置拦截 → bitcast i32* + load(读 __tag),杜绝方法查找兜底误绑 AST.kind(this 偏移语义错误)

- 留根:Assign/Async/Base/Imports/Match/Struct/Translator/Var 单文件前缀 + GenApi/GenTypes/Generator/__init__
2026-08-28 14:16:03 +08:00
TermiNexus b3a4be21d3 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/
2026-08-28 13:29:26 +08:00
TermiNexus 19171ee165 feat(tpv): vpi 预生成失败输出失败文件路径与原因(含 AST 解析错误行列) 2026-08-28 08:05:46 +08:00
TermiNexus 6daec5fa58 feat(tpv): translate_unaryop 支持单目 * / &(Deref/AddrOf) 2026-08-28 07:35:03 +08:00
TermiNexus d971bae7cd 移除了 VLogger fmt buffer 缓冲区,使用栈 f-string 2026-08-28 06:01:17 +08:00
TermiNexus 54462320d2 简化了一些表达,最后仍使用 fb 缓冲区的版本(新版本使用 栈f-string) 2026-08-28 04:50:09 +08:00
TermiNexus c542442987 fix(structgen): 裸名回退字段未找到时返回 None 而非 fatal_error,让 REnum 变体别名回退生效
- ce62546 新增的裸名回退分支在类命中但字段未找到时 fatal_error,
  截断了 ExprGenAttr L853-864 的 REnum 变体别名回退(LLVMType.Pointee
  → _p2 槽位是变体别名非普通字段)→ AssignGenMixin.vp:1002
  "类 llvmlite.LLVMType 裸名回退但字段 Pointee 未找到"
- 改为字段未找到 return None,对齐主 SHA1 匹配分支语义,让调用方
  有机会走 REnum 别名/继承链回退
2026-08-27 16:16:03 +08:00
TermiNexus ce62546e90 fix(structgen): lookup_field_by_class 模块归属未命中回退裸名同名类(唯一时),修复 ast.Attribute 归属错位误报
- 场景:ast.Attribute(ast/exprs.vp 定义,ModuleSha1=c0def010dcebadb1)经
  lookup_field_by_class 模块归属解析,模块段 "ast" 走包名回退 → ast/__init__.vp
  的 sha1 (35245c385b0ddd7b)→ find_struct_by_module 不匹配 → 误报
  "类 ast.Attribute 未找到"(AssignGenMixin.is_cdefine_annotation 的 at.attr 触发)
- 修复:模块可解析但类未注册时,统计裸名同名类数量,唯一时回退查找字段
  (Attribute 短名无跨模块同名,安全);多同名仍 fail-fast(防 sched.Thread 顶掉)
2026-08-27 16:10:48 +08:00
TermiNexus 907f7892f9 fix(callgen): 跨模块返回类型解析的 mod_ret_sha1 显式初始化 None,修复裸名调用读栈残留崩溃
- CallGenMixin.translate_call 中 mod_ret_sha1 原声明在 Attribute 分支内、
  elif mod_ret_sha1 is not None 在外层读取,裸名调用(cl.func 是 Name,
  如 LLVMModuleCore.CreateGlobalInt 内裸名 create_global_int)时该 alloca
  从未 store,读到栈残留垃圾(0x1)→ _LookupMethodRetTyFromTextLL 的
  strlen(0x1) 零页访问(llvmlite/__init__.vp 翻译时 0xC0000005)
- 前移声明并显式初始化为 None,裸名调用不再进入跨模块返回类型解析
2026-08-27 15:52:09 +08:00
TermiNexus c54e87bd6d fix(imports): from . import Y 的 Y 是包内符号(非子模块)时跳过 SHA1 校验
- translate_import_from_module 对 from . import Y 曾无条件把每个 name 拼成
  resolved.Y 当子模块校验 SHA1 → json/__parser.vp 的 from . import JsonValue
  等符号(json/__init__.vp 定义)被误报"模块 json.JsonValue 未注册 SHA1"
- 仅在完整名已注册 SHA1(真子模块)或本地文件时才校验+登记;符号映射
  由 translate_import_from_names 的 from-import / re-export 处理
- 顺带:Reexport 懒初始化改用 CompilerFactory.factory.PermPool(配合 TPC
  跨模块全局写端修复)
2026-08-27 14:27:24 +08:00
TermiNexus a603736f64 修正:直接操作操作跨模块变量(VLogger),避免间接 global 函数 2026-08-27 13:14:39 +08:00
131 changed files with 57650 additions and 39594 deletions

No files matched your search

+1
View File
@@ -10,6 +10,7 @@ __pycache__/
# 构建产物(防御性)
**/output/
**/temp/
**/output_dbg/
**/*.deps.json
**/*.doc.json
**/*.pyi
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+158 -165
View File
@@ -11,8 +11,7 @@ import w32.win32base
import ast
import llvmlite
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Gen.Translator as HandlesTranslator
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
@@ -33,10 +32,9 @@ import lib.core.Gen.GenApi as GenApi
# ============================================================
# Phase1Scan - Phase1 拆分(阶段 E):Phase1Scan
# Phase1Scan - 扫描类继承关系、泛型类标记与容器使用
#
# 直接移动模式:函数迁至此模块,原 Phase1.vp 仅保留常量/_mbuddy;
# 调用方经本模块前缀访问。全局符号经 Phase1._xxx 跨模块访问。
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
# ============================================================
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
@@ -48,26 +46,15 @@ import lib.core.Gen.GenApi as GenApi
# ============================================================
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
#
# 遍历所有可达 includes 文件,解析 AST 收集 ClassDef 的类名和父类名。
# 结果存入扁平字节数组,返回收集到的类数量。
# 遍历所有可达 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: 类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,连续内存)
# parent_names_buf: 父类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,无父类填 '\0')
# def_sha1s_buf: 定义 SHA1 扁平数组(每个 Phase1.SHA1_LEN 字节)
#
# Returns:
# 收集到的类数量,-1 失败
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否使用可达集合过滤;
# sha1_set SHA1 集合(use_reachable=0 时使用);set_count SHA1 集合数量;class_names_buf 类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节);
# parent_names_buf 父类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,无父类填 '\0');def_sha1s_buf 定义 SHA1 扁平数组(每个 Phase1.SHA1_LEN 字节)
# Returns: 收集到的类数量,-1 失败
# ============================================================
def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
result: IncludesScanner.ScanResult | t.CPtr,
def _ScanClassInheritance(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult],
reachable_set: str, reachable_count: int,
use_reachable: int,
sha1_set: str, set_count: int,
@@ -77,20 +64,22 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
generic_file_indices: t.CPtr,
generic_file_count_box: t.CPtr) -> int:
"""预扫描 includes 文件,收集类继承关系"""
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
if result is None:
return -1
# class_names_buf/parent_names_buf/def_sha1s_buf 由调用方 mb.alloc 分配(Phase1Main 预扫描前),本函数把其地址直接作为 strcpy 目的缓冲;
# 分配失败返回 NULL 时目的指针为近零地址 → strcpy 写零页(ASan access-violation),故按 fail-fast 显式报错终止(VLogger.error 内部 sys.exit(1)),不静默返回 -1。
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
class_count: int = 0
for i in range(result.Count):
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes
# 全量 parse 的 AST 若驻留到 mark1 → 1GB 主池耗尽 OOM 根因;
# class_names_buf/parent_names_buf/def_sha1s_buf 在调用前分配,
# 位于本 mark 之前,reset 不影响;generic_file_indices 同理)
scan_mark: t.CSizeT = mb.mark()
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))
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes 全量 parse 的 AST 若驻留到 mark1 会耗尽 1GB 主池 OOM);
# class_names_buf/parent_names_buf/def_sha1s_buf 与 generic_file_indices 在调用前分配、位于本 mark 之前,reset 不影响。
scan_mark: sizet = mb.mark()
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None or entry.Sha1 is None:
continue
@@ -108,10 +97,16 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
# 读取文件
file_path: str = entry.Path
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
if f.closed:
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
f.close()
continue
src_buf: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf is None:
f.close()
@@ -124,49 +119,47 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
continue
if bytes_read < Phase1.SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[Phase1.SRC_BUF_SIZE - 1] = 0
# 解析 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
lx: ast.Lexer* = 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)
tokens: ast.Token* = ast.tokenize(lx)
tree: ast.AST* = 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
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn: t.CSizeT = len(ch)
cn: sizet = len(ch)
for ci in range(cn):
child: ast.AST | t.CPtr = ch.get(ci)
child: ast.AST* = 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)
cd: ast.ClassDef* = ast.ClassDef*(child)
if cd is None or cd.name is None:
continue
# 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册
if file_has_generic == 0:
tp_scan: list[str] | t.CPtr = cd.type_params
tp_scan: list[str]* = cd.type_params
if tp_scan is not None:
file_has_generic = 1
if generic_file_count_box is not None:
gfc_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr))
gfc_ptr_scan: t.CPtr = (int | t.CPtr)(generic_file_count_box)
if gfc_ptr_scan is not None:
gfc_scan: int = gfc_ptr_scan[0]
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
gi_addr_scan: t.CUInt64T = t.CUInt64T(generic_file_indices) + gfc_scan * 4
gi_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_scan, t.CPtr))
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
gi_ptr_scan: t.CPtr = (int | t.CPtr)(gi_addr_scan)
if gi_ptr_scan is not None:
gi_ptr_scan[0] = i
gfc_ptr_scan[0] = gfc_scan + 1
@@ -176,7 +169,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
# 填充扁平数组:类名(偏移 = class_count * Phase1.CLASS_NAME_LEN)
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
nm_len: t.CSizeT = string.strlen(cd.name)
nm_len: sizet = string.strlen(cd.name)
if nm_len >= Phase1.CLASS_NAME_LEN:
string.strncpy(name_dst, cd.name, Phase1.CLASS_NAME_LEN - 1)
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
@@ -189,7 +182,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
parent_nm: str = GenApi._get_parent_class(cd, None)
# parent_nm 是 AST 节点内部指针,不需要释放
if parent_nm is not None:
pnm_len: t.CSizeT = string.strlen(parent_nm)
pnm_len: sizet = string.strlen(parent_nm)
if pnm_len >= Phase1.CLASS_NAME_LEN:
string.strncpy(parent_dst, parent_nm, Phase1.CLASS_NAME_LEN - 1)
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
@@ -210,33 +203,27 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
# ============================================================
# _BufHasGenericClassMarker - 文本扫描检测文件是否含泛型类标记
#
# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。
# 逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。
# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部
# 98 个 includes 导致 AST 驻留 mark1 前把 pool 推到 OOM
# (NewAstTest [52/68] pool 耗尽根因:1a-pre 已 parse 全部驻留,
# 预注册再全量 parse 等于翻倍)。
# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件);
# 漏报有害(泛型模板未预注册 → 悬空复发),模式保守匹配行内单行形式
# (TPV 源码泛型类均为单行 `class X[T]:`,grep 确认)。
# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。
# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部 98 个 includes 致 AST 驻留 mark1 前把 pool 推到 OOM。
# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件);漏报有害(泛型模板未预注册 → 悬空复发),故保守只匹配单行形式。
# ============================================================
def _BufHasGenericClassMarker(buf: bytes, blen: t.CSizeT) -> int:
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
"""文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0"""
if buf is None or blen <= 6:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
while pos + 5 < blen:
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' \
and buf[pos + 3] == 's' and buf[pos + 4] == 's':
# class 前是词边界(行首或非标识符字符)
prev_ok: int = 1
if pos > 0:
pc: t.CChar = buf[pos - 1]
pc: char = buf[pos - 1]
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') \
or(pc >= '0' and pc <= '9') or pc == '_':
prev_ok = 0
if prev_ok != 0:
p: t.CSizeT = pos + 5
p: sizet = pos + 5
# 跳过空白
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
@@ -260,40 +247,37 @@ def _BufHasGenericClassMarker(buf: bytes, blen: t.CSizeT) -> int:
# ============================================================
# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前)
#
# 背景:泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/
# GenericClassFiles + 模板节点)是懒分配的跨文件持久表,首次注册必须落在
# mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2),
# mark2 > mark1)存活。现有 1a-pre-generic 只注册"可达"泛型文件;FuncTest
# 这类可达集合无泛型的项目,模板要等 Phase2 Phase A-pre-inc(mark2 之后)
# 翻译全部 includes 时才注册 → reset 回收表与节点 → _find_generic_template
# 遍历悬空内存 strcmp 崩溃。
# 本函数先文本扫描(_BufHasGenericClassMarker,不 parse、不驻留)筛选出
# 含泛型类的文件(全量 parse 会把 98 个 includes 的 AST 驻留 mark1 前 →
# NewAstTest [52/68] pool 耗尽 OOM),仅对命中的文件用**永久池(perm_pool)**
# 解析 → 模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool。
# 重复注册由 _register_generic_template 内部查重跳过。
# 泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/GenericClassFiles + 模板节点)是懒分配的跨文件持久表,
# 首次注册必须落在 mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2))存活;否则表与节点被 reset 回收,
# _find_generic_template 遍历悬空内存 strcmp 崩溃。本函数先文本扫描(_BufHasGenericClassMarker,不 parse)筛选出含泛型类的文件,
# 仅对命中文件用**永久池(perm_pool)**解析(模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool);重复注册由 _register_generic_template 内部查重跳过。
# ============================================================
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
result: IncludesScanner.ScanResult | t.CPtr) -> int:
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult]) -> int:
"""预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量"""
if result is None or mb is None:
return 0
perm: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool()
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None:
return 0
p1e_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
handled_count: int = 0
for i in range(result.Count):
entry_addr_pg: t.CUInt64T = t.CUInt64T(result.Entries) + i * p1e_size
entry_pg: IncludesScanner.FileEntry | t.CPtr = (
IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_pg, t.CPtr))
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
entry_pg: rawptr[IncludesScanner.FileEntry] = (
rawptr[IncludesScanner.FileEntry])(entry_addr_pg)
if entry_pg is None or entry_pg.Sha1 is None:
continue
# 读取文件
file_path_pg: str = entry_pg.Path
f_pg: fileio.File | t.CPtr = fileio.File(file_path_pg, fileio.MODE.R)
f_pg: rawptr[fileio.File] = fileio.File(file_path_pg, fileio.MODE.R)
if f_pg.closed:
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
f_pg.close()
continue
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf_pg is None:
f_pg.close()
@@ -305,35 +289,33 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
continue
if bytes_read_pg < Phase1.SRC_BUF_SIZE:
src_buf_pg[bytes_read_pg] = 0
else:
src_buf_pg[Phase1.SRC_BUF_SIZE - 1] = 0
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0:
stdlib.free(src_buf_pg)
continue
# 泛型文件:永久池解析 → 注册模板(节点/表进 perm_pool,跨 reset 存活)
lx_pg: ast.Lexer | t.CPtr = ast.new_lexer(perm)
lx_pg: ast.Lexer* = ast.new_lexer(perm)
if lx_pg is None:
stdlib.free(src_buf_pg)
continue
ast._lexer_init(lx_pg, src_buf_pg, perm)
tokens_pg: ast.Token | t.CPtr = ast.tokenize(lx_pg)
tree_pg: ast.AST | t.CPtr = ast.parse_tokens(perm, tokens_pg)
tokens_pg: ast.Token* = ast.tokenize(lx_pg)
tree_pg: ast.AST* = ast.parse_tokens(perm, tokens_pg)
if tree_pg is None:
stdlib.free(src_buf_pg)
continue
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
ch_pg: list[ast.AST | t.CPtr] | t.CPtr = tree_pg.children
ch_pg: list[ast.AST*]* = tree_pg.children
if ch_pg is not None:
cn_pg: t.CSizeT = len(ch_pg)
cn_pg: sizet = len(ch_pg)
for ci_pg in range(cn_pg):
child_pg: ast.AST | t.CPtr = ch_pg.get(ci_pg)
child_pg: ast.AST* = ch_pg.get(ci_pg)
if child_pg is None or child_pg.kind() != ast.ASTKind.ClassDef:
continue
cd_pg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child_pg)
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
if cd_pg is None or cd_pg.name is None:
continue
tp_pg: list[str] | t.CPtr = cd_pg.type_params
tp_pg: list[str]* = cd_pg.type_params
if tp_pg is None:
continue
GenApi.set_current_file_type(file_path_pg)
@@ -341,22 +323,15 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
handled_count += 1
stdlib.free(src_buf_pg)
if handled_count > 0:
fb_gpre2: str = VLogger.fmt_buf()
if fb_gpre2 is not None:
viperlib.snprintf(fb_gpre2, 1024, "mark1 前永久池预注册 %d 个 includes 泛型类模板", handled_count)
VLogger.info(fb_gpre2, "Phase1")
VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型类模板", "Phase1")
else:
VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
return handled_count
# ============================================================
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
#
# Args:
# class_names_buf: 类名扁平数组
# def_sha1s_buf: 定义 SHA1 扁平数组
# class_count: 类数量
# class_name: 要查找的类名
#
# Returns:
# SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到
# 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:
@@ -371,14 +346,36 @@ def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
# ============================================================
# _ScanTreeForContainers - 递归扫描 AST 检测 list/dict 容器使用
#
# TPV 的 AST 中,语句块挂在 children,而表达式/注解树全部以结构体
# 字段挂载(value/slice/func/args/annotation/targets/returns/bases/
# left/right 等)。只递归 children 会漏掉 list[...] | t.CPtr(BinOp
# 包裹)、list[...](...)(Call.func)、字段注解等一切非语句块位置。
# 因此每个节点先递归其表达式承载字段,再递归 children 语句块。
# TPV 的 AST 中语句块挂在 children,而表达式/注解树全部以结构体字段挂载(value/slice/func/args/annotation/targets/returns/bases/left/right 等);
# 只递归 children 会漏掉 list[...] | t.CPtr、list[...](...)、字段注解等非语句块位置,故每个节点先递归表达式承载字段、再递归 children。
# 命中即置 need_list/need_dict;误报无害(最多多编译两个空壳模块)。
# ============================================================
def _ScanTreeForContainers(node: ast.AST | t.CPtr,
def _AnnIsContainer(ann: ast.AST*) -> int:
"""注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...])
数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _ScanTreeForContainers
把数组字面量误判为 list 容器使用(FIX 2026-08-30,联动 Translator.vp 同修)。
"""
while ann is not None:
ak_c: int = ann.kind()
if ak_c == ast.ASTKind.Name:
nm_c: ast.Name* = ast.Name*(ann)
if nm_c.id is not None:
if string.strcmp(nm_c.id, "list") == 0:
return 1
if string.strcmp(nm_c.id, "dict") == 0:
return 1
return 0
elif ak_c == ast.ASTKind.Subscript:
sub_c: ast.Subscript* = ast.Subscript*(ann)
ann = sub_c.value
else:
return 0
return 0
def _ScanTreeForContainers(node: ast.AST*,
need_list_box: t.CPtr,
need_dict_box: t.CPtr,
depth: int = 0):
@@ -386,127 +383,123 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
if depth > 128:
fb_stc: str = VLogger.fmt_buf()
if fb_stc is not None:
viperlib.snprintf(fb_stc, 256, "容器扫描递归过深(AST 环或嵌套超过 128 层)")
VLogger.error(fb_stc, "Phase1")
sys.exit(1)
VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
if node is None:
return
k_s: int = node.kind()
if k_s == ast.ASTKind.Name:
nm_s: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
nm_s: ast.Name* = ast.Name*(node)
if nm_s.id is not None:
if string.strcmp(nm_s.id, "list") == 0:
need_list_box[0] = 1
elif string.strcmp(nm_s.id, "dict") == 0:
need_dict_box[0] = 1
elif k_s == ast.ASTKind.Subscript:
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
sub_s: ast.Subscript* = ast.Subscript*(node)
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
_ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BinOp:
bop_s: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
bop_s: ast.BinOp* = ast.BinOp*(node)
_ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box, depth + 1)
_ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Call:
cl_s2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
cl_s2: ast.Call* = ast.Call*(node)
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1)
cl_args_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.args
cl_args_s: list[ast.AST*]* = cl_s2.args
if cl_args_s is not None:
na_s: t.CSizeT = len(cl_args_s)
i_a2: t.CSizeT
na_s: sizet = len(cl_args_s)
i_a2: sizet
for i_a2 in range(na_s):
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
cl_kws_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.keywords
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
if cl_kws_s is not None:
nk_s: t.CSizeT = len(cl_kws_s)
i_k2: t.CSizeT
nk_s: sizet = len(cl_kws_s)
i_k2: sizet
for i_k2 in range(nk_s):
kw_s: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(cl_kws_s.get(i_k2))
kw_s: rawptr[ast.Keyword] = rawptr[ast.Keyword](cl_kws_s.get(i_k2))
if kw_s is not None:
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Assign:
asn_s: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
asn_s: ast.Assign* = ast.Assign*(node)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
tgts_s: list[ast.AST | t.CPtr] | t.CPtr = asn_s.targets
tgts_s: list[ast.AST*]* = asn_s.targets
if tgts_s is not None:
nt_s: t.CSizeT = len(tgts_s)
i_t2: t.CSizeT
nt_s: sizet = len(tgts_s)
i_t2: sizet
for i_t2 in range(nt_s):
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.AnnAssign:
aa_s2: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
_ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.FunctionDef:
fn_s: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node)
fn_s: ast.FunctionDef* = ast.FunctionDef*(node)
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1)
fn_args_s: ast.Arguments | t.CPtr = fn_s.args
fn_args_s: ast.Arguments* = ast.Arguments*(fn_s.args)
if fn_args_s is not None:
fa_s: list[ast.AST | t.CPtr] | t.CPtr = fn_args_s.args
fa_s: list[ast.AST*]* = fn_args_s.args
if fa_s is not None:
nf_s: t.CSizeT = len(fa_s)
i_f2: t.CSizeT
nf_s: sizet = len(fa_s)
i_f2: sizet
for i_f2 in range(nf_s):
arg_s: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(fa_s.get(i_f2))
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
if arg_s is not None:
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.ClassDef:
cd_s: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(node)
bases_s: list[ast.AST | t.CPtr] | t.CPtr = cd_s.bases
cd_s: ast.ClassDef* = ast.ClassDef*(node)
bases_s: list[ast.AST*]* = cd_s.bases
if bases_s is not None:
nb_s: t.CSizeT = len(bases_s)
i_b2: t.CSizeT
nb_s: sizet = len(bases_s)
i_b2: sizet
for i_b2 in range(nb_s):
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Attribute:
at_s: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
at_s: ast.Attribute* = ast.Attribute*(node)
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Tuple:
tu_s: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
elts_s: list[ast.AST | t.CPtr] | t.CPtr = tu_s.elts
tu_s: ast.Tuple* = ast.Tuple*(node)
elts_s: list[ast.AST*]* = tu_s.elts
if elts_s is not None:
ne_s: t.CSizeT = len(elts_s)
i_e2: t.CSizeT
ne_s: sizet = len(elts_s)
i_e2: sizet
for i_e2 in range(ne_s):
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.UnaryOp:
uop_s: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Expr:
ex_s: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node)
ex_s: rawptr[ast.Expr] = rawptr[ast.Expr](node)
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Return:
rt_s: ast.Return | t.CPtr = (ast.Return | t.CPtr)(node)
rt_s: rawptr[ast.Return] = rawptr[ast.Return](node)
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Compare:
cp_s: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
cp_s: rawptr[ast.Compare] = rawptr[ast.Compare](node)
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
cps_s: list[ast.AST | t.CPtr] | t.CPtr = cp_s.comparators
cps_s: list[ast.AST*]* = cp_s.comparators
if cps_s is not None:
nc_s: t.CSizeT = len(cps_s)
i_c2: t.CSizeT
nc_s: sizet = len(cps_s)
i_c2: sizet
for i_c2 in range(nc_s):
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BoolOp:
bo_s: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
bvs_s: list[ast.AST | t.CPtr] | t.CPtr = bo_s.values
bo_s: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
bvs_s: list[ast.AST*]* = bo_s.values
if bvs_s is not None:
nv_s: t.CSizeT = len(bvs_s)
i_v2: t.CSizeT
nv_s: sizet = len(bvs_s)
i_v2: sizet
for i_v2 in range(nv_s):
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.List:
# [] 字面量
need_list_box[0] = 1
elif k_s == ast.ASTKind.Dict:
# {} 字面量
need_dict_box[0] = 1
# `[...]`/`{}` 字面量不触发 _list/_dict 导入:字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数)与数组数据初始化,并非容器使用;
# 容器的真实使用由类型位置检测覆盖(Name/Subscript 注解、Call.func 构造名)。
# elif k_s == ast.ASTKind.List:
# need_list_box[0] = 1
# elif k_s == ast.ASTKind.Dict:
# need_dict_box[0] = 1
# 递归语句块子节点
if node.children is not None:
ch_s: list[ast.AST | t.CPtr] | t.CPtr = node.children
n_s: t.CSizeT = len(ch_s)
ci_s: t.CSizeT
ch_s: list[ast.AST*]* = node.children
n_s: sizet = len(ch_s)
ci_s: sizet
for ci_s in range(n_s):
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
+60 -90
View File
@@ -10,8 +10,7 @@ import w32.win32base
import ast
import llvmlite
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Gen.Translator as HandlesTranslator
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
@@ -29,57 +28,37 @@ import lib.Phase.Phase1.Phase1Topo as Phase1Topo
import lib.Phase.Phase1 as Phase1
# ============================================================
# Phase1Topo - Phase1 拆分(阶段 E):Phase1Topo
# Phase1Topo - 按类继承与 import 依赖对源文件做拓扑排序
#
# 直接移动模式:函数迁至此模块,原 Phase1.vp 仅保留常量/_mbuddy;
# 调用方经本模块前缀访问。全局符号经 Phase1._xxx 跨模块访问。
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
# ============================================================
# ============================================================
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序(Kahn)
#
# 算法(Kahn):
# 1. 从扁平类信息数组构建"文件 SHA1 → 依赖文件 SHA1 集合"映射
# 2. 计算每个文件的入度(依赖数量)
# 3. 入度为 0 的文件先处理,处理后减少依赖它的文件入度
# 4. 重复直到所有文件处理完成
# 步骤:1) 从扁平类信息数组构建「文件 SHA1 → 依赖文件 SHA1 集合」映射;2) 计算每个文件入度(DepCount,初始化 InDegree = DepCount);
# 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 失败
# 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,
def _TopoSortFiles(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult],
reachable_set: str, reachable_count: int,
use_reachable: int,
sha1_set: str, set_count: int,
class_names_buf: bytes, parent_names_buf: bytes,
def_sha1s_buf: bytes, class_count: int,
out_order: t.CPtr, out_count: t.CPtr,
temp_dir: str, td_len_ts: t.CSizeT) -> int:
temp_dir: str, td_len_ts: sizet) -> int:
"""根据类继承关系对文件进行拓扑排序"""
if result is None or out_order is None or out_count is None:
return -1
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
@@ -111,8 +90,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
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))
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None or entry.Sha1 is None:
continue
@@ -161,8 +140,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
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))
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
cnt_ptr: t.CPtr = (int | t.CPtr)(cnt_addr)
if cnt_ptr is None:
break
cur_cnt: int = cnt_ptr[0]
@@ -185,15 +164,9 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
# 2.5 补充 import 依赖边:被 import 的模块先完整翻译。
#
# 仅按类继承建边不够:跨模块全局变量 stub(lexer.vp from-import
# tokens.vp 的 _op_head)、跨模块 vtable TypeStr(astaux 继承 base 的
# AST)、泛型模板(base.vp 用 list[...])都要求依赖模块先翻译,而它们
# 之间可能无类继承关系。deps(_includes_deps_*)由 Phase 1a-pre 填充
# (含容器按需注入的 _list/_dict)。
#
# 用 _BuildIncludesSha1Map 复制局部 sha1→模块名 数组(与
# _BuildReachableSha1Set 的 BFS 一致),避免直接使用全局 store 返回值
# 跨函数传参(bytes|t.CPtr 联合转 str 参数)的地址异常。
# 仅按类继承建边不够:跨模块全局变量 stub、跨模块 vtable TypeStr、泛型模板都要求依赖模块先翻译,而它们之间可能无类继承关系。
# deps(_includes_deps_*)由 Phase 1a-pre 填充(含容器按需注入的 _list/_dict);用 _BuildIncludesSha1Map 复制局部 sha1→模块名 数组
# (与 _BuildReachableSha1Set 的 BFS 一致),避免直接跨函数传全局 store 返回值导致地址异常。
sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
map_count_ts: int = 0
@@ -207,13 +180,13 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
if deps_imp is None:
continue
dl_imp: t.CSizeT = string.strlen(deps_imp)
dpos_imp: t.CSizeT = 0
dl_imp: sizet = string.strlen(deps_imp)
dpos_imp: sizet = 0
while dpos_imp < dl_imp:
dstart_imp: t.CSizeT = dpos_imp
dstart_imp: sizet = dpos_imp
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ':
dpos_imp += 1
dlen_imp: t.CSizeT = dpos_imp - dstart_imp
dlen_imp: sizet = dpos_imp - dstart_imp
if dpos_imp < dl_imp:
dpos_imp += 1
if dlen_imp == 0:
@@ -235,8 +208,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
if fj_imp == fi_imp:
break
# 去重 + 加边:fi_imp 依赖 fj_imp(InDegree[fi_imp]++)
cnt_addr_imp: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi_imp * 4
cnt_ptr_imp: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr_imp, t.CPtr))
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
cnt_ptr_imp: t.CPtr = (int | t.CPtr)(cnt_addr_imp)
if cnt_ptr_imp is None:
break
cur_cnt_imp: int = cnt_ptr_imp[0]
@@ -261,10 +234,10 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
# 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))
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
cnt_ptr3: t.CPtr = (int | t.CPtr)(cnt_addr3)
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
ind_ptr3: t.CPtr = (int | t.CPtr)(ind_addr3)
if cnt_ptr3 is not None and ind_ptr3 is not None:
ind_ptr3[0] = cnt_ptr3[0]
@@ -275,13 +248,13 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
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))
proc_addr4: uint64 = uint64(file_processed) + fi * 4
proc_ptr4: t.CPtr = (int | t.CPtr)(proc_addr4)
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))
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
ind_ptr4: t.CPtr = (int | t.CPtr)(ind_addr4)
if ind_ptr4 is None or ind_ptr4[0] > 0:
continue
@@ -289,13 +262,13 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
# 找到对应的 result.Entries 索引
target_sha1: str = file_sha1s_buf + fi * Phase1.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))
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
ent_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r)
if ent_r is not None and ent_r.Sha1 is not None:
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
# 写入输出索引数组
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))
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
out_int_ptr: t.CPtr = (int | t.CPtr)(out_idx_addr)
if out_int_ptr is not None:
out_int_ptr[0] = ri
out_idx += 1
@@ -307,40 +280,37 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
# 减少依赖该文件的其他文件的入度
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))
proc_addr5: uint64 = uint64(file_processed) + fj * 4
proc_ptr5: t.CPtr = (int | t.CPtr)(proc_addr5)
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))
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
cnt_ptr5: t.CPtr = (int | t.CPtr)(cnt_addr5)
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 * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.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))
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
ind_ptr5: t.CPtr = (int | t.CPtr)(ind_addr5)
if ind_ptr5 is not None:
ind_ptr5[0] = ind_ptr5[0] - 1
break
# 处理循环依赖:环内优先输出"被依赖者"(被其余剩余文件依赖数量多者先)。
# 处理循环依赖:环内优先输出被依赖者(被其余剩余文件依赖数量多者先)。
#
# 背景:容器按需注入(string 用 list 泛型 → 依赖 _list)与真实 import
# (_list import string)形成伪环,Kahn 无法完全归零。若按扫描顺序追加,
# 依赖者(astaux)会先于被依赖者(base)翻译 → 跨模块 vtable TypeStr
# 未注册 → 误杀。被依赖者先翻译,其 stub/TypeStr/模板先就绪,依赖者
# 翻译时可引用(函数/全局声明由 1a-pre 的 vpi 兜底)。
# 容器按需注入(string 用 list 泛型 → 依赖 _list)与真实 import 会形成伪环,Kahn 无法完全归零;若按扫描顺序追加,
# 依赖者会先于被依赖者翻译 → 跨模块 vtable TypeStr 未注册 → 误杀。
if out_idx < file_count:
rem_idx: t.CPtr = stdlib.malloc(4 * file_count)
rem_depcnt: t.CPtr = stdlib.malloc(4 * file_count)
rem_count: int = 0
if rem_idx is not None and rem_depcnt is not None:
for fi in range(file_count):
proc_addr7: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
proc_ptr7: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr7, t.CPtr))
proc_addr7: uint64 = uint64(file_processed) + fi * 4
proc_ptr7: t.CPtr = (int | t.CPtr)(proc_addr7)
if proc_ptr7 is not None and proc_ptr7[0] == 0:
rem_idx[rem_count] = fi
rem_depcnt[rem_count] = 0
@@ -353,8 +323,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
fi_b: int = rem_idx[i_b]
if fi_b == fi_a:
continue
cnt_addr_b: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi_b * 4
cnt_ptr_b: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr_b, t.CPtr))
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
cnt_ptr_b: t.CPtr = (int | t.CPtr)(cnt_addr_b)
if cnt_ptr_b is None:
continue
cnt_b: int = cnt_ptr_b[0]
@@ -376,18 +346,18 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
# 按序输出剩余文件
for i_a in range(rem_count):
fi_a2: int = rem_idx[i_a]
proc_addr8: t.CUInt64T = t.CUInt64T(file_processed) + fi_a2 * 4
proc_ptr8: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr8, t.CPtr))
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
proc_ptr8: t.CPtr = (int | t.CPtr)(proc_addr8)
if proc_ptr8 is None or proc_ptr8[0] != 0:
continue
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
for ri in range(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))
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
ent_r2: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r2)
if ent_r2 is not None and ent_r2.Sha1 is not None:
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
out_idx_addr2: 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))
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
out_int_ptr2: t.CPtr = (int | t.CPtr)(out_idx_addr2)
if out_int_ptr2 is not None:
out_int_ptr2[0] = ri
out_idx += 1
@@ -397,7 +367,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
stdlib.free(rem_depcnt)
# 写入输出数量
out_count_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_count, t.CPtr))
out_count_ptr: t.CPtr = (int | t.CPtr)(out_count)
if out_count_ptr is not None:
out_count_ptr[0] = out_idx
+2 -24
View File
@@ -1,37 +1,15 @@
import t, c
from stdint import *
import string
import stdlib
import memhub
import viperlib
import w32.fileio as fileio
import w32.win32file
import w32.win32base
import ast
import llvmlite
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
import lib.core.CompilerFactory as CompilerFactory
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Projectrans.Config as Config
import lib.Stub.StubGen.Converter as StubConverter
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
_mbuddy: memhub.MemBuddy*
# 源代码缓冲区大小(1MB)
SRC_BUF_SIZE: t.CDefine = 1048576
# vpi 缓冲区大小(256KB)
PYI_BUF_SIZE: t.CSizeT = 262144
PYI_BUF_SIZE: sizet = 262144
# 拓扑排序相关常量
MAX_CLASSES: t.CDefine = 512
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+256 -267
View File
@@ -1,267 +1,256 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import viperlib
import hashlib
import w32.win32file
import w32.win32base
import w32.fileio as fileio
# ============================================================
# Utils - 工程工具函数
#
# 提供 SHA1 计算和目录清理等通用工具函数。
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
# ============================================================
# nibble_to_hex_char - 半字节转十六进制字符
# ============================================================
def nibble_to_hex_char(n: int, uppercase: int) -> t.CChar:
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
if n < 10: return '0' + n
if uppercase != 0: return 'A' + (n - 10)
return 'a' + (n - 10)
# ============================================================
# bytes_to_hex - 字节序列转十六进制字符串
# ============================================================
def bytes_to_hex(pool: memhub.MemBuddy | t.CPtr,
digest: bytes,
nbytes: t.CSizeT,
uppercase: int,
use_malloc: int) -> str:
"""将 digest 前 nbytes 字节转为 2*nbytes 个十六进制字符(含 null 终止)
use_malloc=1 时用 stdlib.malloc(调用方负责 free),否则 pool.alloc。
合并自 Utils.compute_sha1 / IncludesScanner.compute_file_sha1 的重复 hex 转换。
"""
if digest is None or nbytes <= 0:
return None
hex_len: t.CSizeT = nbytes * 2 + 1
hex_buf: str = None
if use_malloc != 0:
hex_buf = stdlib.malloc(hex_len)
else:
hex_buf = pool.alloc(hex_len)
if hex_buf is None:
return None
for i in range(nbytes):
hi: int = (digest[i] >> 4) & 0xF
lo: int = digest[i] & 0xF
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
hex_buf[i * 2 + 1] = nibble_to_hex_char(lo, uppercase)
hex_buf[nbytes * 2] = '\0'
return hex_buf
# ============================================================
# compute_sha1 - 计算字符串的 SHA1,返回前 16 个十六进制字符
#
# 使用 includes/hashlib 库的 sha1 类计算摘要,转为十六进制字符串。
# 参考 Projectrans.py 的 compute_sha1(hashlib.sha1().hexdigest()[:16])。
# ============================================================
def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str:
"""计算字符串的 SHA1,返回前 16 个十六进制字符(用于文件命名)"""
if content is None or pool is None:
return None
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
ctx: hashlib.sha1 | t.CPtr = hashlib.sha1()
if ctx is None: return None
# 计算摘要
ctx.update(content)
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
if digest is None: return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
return bytes_to_hex(pool, digest, 8, 0, 0)
# ============================================================
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
# ============================================================
def is_vp_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int:
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
"""
if p is None or plen <= 3:
return 0
base: t.CSizeT = start + plen - 3
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p':
return 1
return 0
# ============================================================
# JoinPath - 拼接两个路径段为 a/b
# ============================================================
def JoinPath(a: str, b: str) -> str:
"""拼接路径段为 "a/b"(stdlib.malloc 分配,调用方负责 free),失败返回 None"""
if a is None or b is None:
return None
a_len: t.CSizeT = string.strlen(a)
b_len: t.CSizeT = string.strlen(b)
total_len: t.CSizeT = a_len + b_len + 2
buf: str = stdlib.malloc(total_len)
if buf is None: return None
viperlib.snprintf(buf, total_len, "%s/%s", a, b)
return buf
# ============================================================
# read_file_to_buf - 读取整个文件到缓冲区(null 终止)
# ============================================================
def read_file_to_buf(path: str, buf_size: t.CSizeT) -> bytes:
"""读取文件到 stdlib.malloc 缓冲区并保证 null 终止
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
"""
if path is None or buf_size <= 0: return None
sf: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R)
if sf.closed: return None
buf: bytes = stdlib.malloc(buf_size)
if buf is None:
sf.close()
return None
br: t.CInt64T = sf.read_all(buf, buf_size)
sf.close()
if br <= 0:
stdlib.free(buf)
return None
if br < buf_size:
buf[br] = 0
else:
buf[buf_size - 1] = 0
return buf
# ============================================================
# PathToModuleName - 文件路径转模块名
# ============================================================
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
"""将文件路径转换为模块名(stdlib.malloc 分配,调用方负责 free)
strip_includes_prefix=1 时跳过 includes/ 前缀(如 includes/ast/__init__.vp → ast)。
特殊处理 __init__.vp:pkg/__init__.vp → pkg(而非 pkg.__init__)。
合并自 StubMerger._PathToModuleName / IncludesScanner 内联模块名计算。
"""
if path is None:
return None
plen: t.CSizeT = string.strlen(path)
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
prefix_len: t.CSizeT = 9
path_start: t.CSizeT = 0
if strip_includes_prefix != 0 and 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
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
if is_vp_at(path, path_start, plen) != 0:
plen -= 3
if plen == 0:
return None
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
if plen > 9:
tail_off: t.CSizeT = path_start + plen - 9
sep_char: t.CChar = path[tail_off]
if (sep_char == '/' or sep_char == '\\') \
and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' \
and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' \
and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
plen = tail_off - path_start
if plen == 0:
return None
# 复制并替换 / 为 .
result: str = stdlib.malloc(plen + 1)
if result is None:
return None
for ri in range(plen):
ch: t.CChar = path[path_start + ri]
if ch == '/' or ch == '\\':
result[ri] = '.'
else:
result[ri] = ch
result[plen] = '\0'
return result
# ============================================================
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身)
#
# 用于 --clean 选项:清理 temp_dir 和 output_dir 中的旧文件。
# SHA1 分片目录结构(如 temp/1c/、temp/7a/)需要递归清理,
# 否则旧的截断文件(如 256KB text.ll)会被 Phase1 跳过逻辑复用。
# ============================================================
def CleanDir(dir_path: str) -> int:
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
if dir_path is None:
return -1
dir_len: t.CSizeT = string.strlen(dir_path)
pattern: bytes = stdlib.malloc(dir_len + 8)
if pattern is None:
return -1
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
stdlib.free(pattern)
return -1
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
stdlib.free(pattern)
stdlib.free(find_data)
return 0
deleted: int = 0
while True:
fname: str = find_data.cFileName
if fname is not None:
# 跳过 . 和 ..
if fname[0] == '.':
if fname[1] == '\0':
fname = None
elif fname[1] == '.' and fname[2] == '\0':
fname = None
if fname is not None:
full_path: bytes = JoinPath(dir_path, fname)
if full_path is not None:
# 检查是否为目录(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:
break
w32.win32file.FindClose(handle)
stdlib.free(pattern)
stdlib.free(find_data)
return deleted
import t, c
from stdint import *
import memhub
import string
import stdlib
import viperlib
import hashlib
import w32.win32file
import w32.win32base
import w32.fileio as fileio
# ============================================================
# Utils - 工程工具函数(SHA1 计算、目录清理等)
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy*
# ============================================================
# nibble_to_hex_char - 半字节转十六进制字符
# ============================================================
def nibble_to_hex_char(n: int, uppercase: int) -> char:
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
if n < 10: return '0' + n
if uppercase != 0: return 'A' + (n - 10)
return 'a' + (n - 10)
# ============================================================
# bytes_to_hex - 字节序列转十六进制字符串
# ============================================================
def bytes_to_hex(pool: memhub.MemBuddy*,
digest: bytes,
nbytes: sizet,
uppercase: int,
use_malloc: int) -> str:
"""将 digest 前 nbytes 字节转为 2*nbytes 个十六进制字符(含 null 终止)
use_malloc=1 时用 stdlib.malloc(调用方负责 free),否则 pool.alloc。
合并自 Utils.compute_sha1 / IncludesScanner.compute_file_sha1 的重复 hex 转换。
"""
if digest is None or nbytes <= 0:
return None
hex_len: sizet = nbytes * 2 + 1
hex_buf: str = None
if use_malloc != 0:
hex_buf = stdlib.malloc(hex_len)
else:
hex_buf = pool.alloc(hex_len)
if hex_buf is None:
return None
for i in range(nbytes):
hi: int = (digest[i] >> 4) & 0xF
lo: int = digest[i] & 0xF
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
hex_buf[i * 2 + 1] = nibble_to_hex_char(lo, uppercase)
hex_buf[nbytes * 2] = '\0'
return hex_buf
# ============================================================
# compute_sha1 - 计算字符串的 SHA1,返回前 16 个十六进制字符(与 Projectrans.py 一致)
# ============================================================
def compute_sha1(pool: memhub.MemBuddy*, content: str) -> str:
"""计算字符串的 SHA1,返回前 16 个十六进制字符(用于文件命名)"""
if content is None or pool is None:
return None
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
if ctx is None: return None
# 计算摘要
ctx.update(content)
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
if digest is None: return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
return bytes_to_hex(pool, digest, 8, 0, 0)
# ============================================================
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
# ============================================================
def is_vp_at(p: str, start: sizet, plen: sizet) -> int:
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
"""
if p is None or plen <= 3:
return 0
base: sizet = start + plen - 3
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p':
return 1
return 0
# ============================================================
# JoinPath - 拼接两个路径段为 a/b
# ============================================================
def JoinPath(a: str, b: str) -> str:
"""拼接路径段为 "a/b"(stdlib.malloc 分配,调用方负责 free),失败返回 None"""
if a is None or b is None:
return None
a_len: sizet = string.strlen(a)
b_len: sizet = string.strlen(b)
total_len: sizet = a_len + b_len + 2
buf: str = stdlib.malloc(total_len)
if buf is None: return None
viperlib.snprintf(buf, total_len, "%s/%s", a, b)
return buf
# ============================================================
# read_file_to_buf - 读取整个文件到缓冲区(null 终止)
# ============================================================
def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
"""读取文件到 stdlib.malloc 缓冲区并保证 null 终止
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
"""
if path is None or buf_size <= 0: return None
sf: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
if sf.closed: return None
buf: bytes = stdlib.malloc(buf_size)
if buf is None:
sf.close()
return None
br: int64 = sf.read_all(buf, buf_size)
sf.close()
if br <= 0:
stdlib.free(buf)
return None
if br < buf_size:
buf[br] = 0
return buf
# ============================================================
# PathToModuleName - 文件路径转模块名
# ============================================================
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
"""将文件路径转换为模块名(stdlib.malloc 分配,调用方负责 free)
strip_includes_prefix=1 时跳过 includes/ 前缀(如 includes/ast/__init__.vp → ast)。
特殊处理 __init__.vp:pkg/__init__.vp → pkg(而非 pkg.__init__)。
合并自 StubMerger._PathToModuleName / IncludesScanner 内联模块名计算。
"""
if path is None:
return None
plen: sizet = string.strlen(path)
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
prefix_len: sizet = 9
path_start: sizet = 0
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
psep_ch: char = path[8]
if psep_ch == '/' or psep_ch == '\\':
path_start = prefix_len
plen = plen - prefix_len
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
if is_vp_at(path, path_start, plen) != 0:
plen -= 3
if plen == 0:
return None
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
if plen > 9:
tail_off: sizet = path_start + plen - 9
sep_char: char = path[tail_off]
if (sep_char == '/' or sep_char == '\\') \
and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' \
and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' \
and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
plen = tail_off - path_start
if plen == 0:
return None
# 复制并替换 / 为 .
result: str = stdlib.malloc(plen + 1)
if result is None:
return None
for ri in range(plen):
ch: char = path[path_start + ri]
if ch == '/' or ch == '\\':
result[ri] = '.'
else:
result[ri] = ch
result[plen] = '\0'
return result
# ============================================================
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身;用于 --clean)
# ============================================================
def CleanDir(dir_path: str) -> int:
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
if dir_path is None:
return -1
dir_len: sizet = string.strlen(dir_path)
pattern: bytes = stdlib.malloc(dir_len + 8)
if pattern is None:
return -1
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
stdlib.free(pattern)
return -1
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
stdlib.free(pattern)
stdlib.free(find_data)
return 0
deleted: int = 0
while True:
fname: str = find_data.cFileName
if fname is not None:
# 跳过 . 和 ..
if fname[0] == '.':
if fname[1] == '\0':
fname = None
elif fname[1] == '.' and fname[2] == '\0':
fname = None
if fname is not None:
full_path: bytes = JoinPath(dir_path, fname)
if full_path is not None:
# 检查是否为目录(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
is_dir: uint32 = 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:
break
w32.win32file.FindClose(handle)
stdlib.free(pattern)
stdlib.free(find_data)
return deleted
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -3
View File
@@ -1,3 +1 @@
# ============================================================
# StubGen 包入口
# ============================================================
# StubGen
File diff suppressed because it is too large. Load diff
+36 -66
View File
@@ -12,7 +12,7 @@ import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
@@ -26,52 +26,39 @@ import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerCollect - StubMerger 拆分(阶段 E):StubMergerCollect
# StubMergerCollect - 切片路径构建与 stub 文件 SHA1 收集
#
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
# StubMerger._xxx 访问(main.vp 先例)。
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
# ============================================================
# ============================================================
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free)
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free;level>0 时自动调 BuildPipeline.ensure_dir 建子目录)
#
# 根据 Config.Sha1SliceLevel 将文件分散到 SHA1 前缀子目录:
# level=0 → {temp_dir}/{sha1}.{ext}
# level=1 → {temp_dir}/b7/{sha1}.{ext}
# level=2 → {temp_dir}/b7/90/{sha1}.{ext}
# 按 Config.Sha1SliceLevel 分散到 SHA1 前缀子目录:level=0 → {temp_dir}/{sha1}.{ext};level=1 → {temp_dir}/b7/{sha1}.{ext};level=2 → {temp_dir}/b7/90/{sha1}.{ext}
#
# 自动调用 BuildPipeline.ensure_dir 创建子目录(level>0 时)。
#
# Args:
# temp_dir: 基础目录
# td_len: temp_dir 长度(避免重复 strlen)
# sha1: SHA1 字符串
# ext: 文件扩展名(如 "stub.ll")
#
# Returns:
# 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
# Args: temp_dir 基础目录;td_len temp_dir 长度(避免重复 strlen);sha1 SHA1 字符串;ext 文件扩展名(如 "stub.ll")
# Returns: 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
# ============================================================
def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
"""构建切片路径(stdlib.malloc 分配,调用者负责 free)
level>0 时自动调用 ensure_dir 创建子目录。
"""
if temp_dir is None or sha1 is None or ext is None:
return None
sha1_len: t.CSizeT = string.strlen(sha1)
ext_len: t.CSizeT = string.strlen(ext)
sha1_len: sizet = string.strlen(sha1)
ext_len: sizet = string.strlen(ext)
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
sub_len: t.CSizeT = 0
sub_len: sizet = 0
if subdir_buf is not None:
sub_len = string.strlen(subdir_buf)
# 确保子目录存在: {temp_dir}/{subdir}
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
# 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。
dir_path_len: t.CSizeT = td_len + sub_len + 2
dir_path_len: sizet = td_len + sub_len + 2
dir_path: bytes = stdlib.malloc(dir_path_len)
if dir_path is not None:
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
@@ -80,7 +67,7 @@ def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
# 构建完整路径
path: str = None
path_len: t.CSizeT = 0
path_len: sizet = 0
if sub_len > 0:
path_len = td_len + sub_len + sha1_len + ext_len + 4
path = stdlib.malloc(path_len)
@@ -94,24 +81,14 @@ def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
return path
# ============================================================
# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1
# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1(从文件名提取)
#
# 根据 level 递归扫描子目录:
# level=0 → 扫描 {base_dir}/*.stub.ll,从文件名提取 SHA1
# level>0 → 扫描 {base_dir}/* 的子目录,对每个子目录递归调用
# level=0 → 扫描 {base_dir}/*.stub.ll;level>0 → 扫描 {base_dir}/* 的子目录并对每个子目录递归调用。
#
# Args:
# base_dir: 当前扫描目录
# bd_len: base_dir 长度
# level: 剩余递归层数
# out_arr: 输出数组(每个 SHA1 17 字节 = 16字符 + null)
# out_count: 当前已收集数量
# max_count: 最大数量
#
# Returns:
# 收集后的总数量
# Args: base_dir 当前扫描目录;bd_len base_dir 长度;level 剩余递归层数;out_arr 输出数组(每个 SHA1 17 字节 = 16字符 + null);out_count 当前已收集数量;max_count 最大数量
# Returns: 收集后的总数量
# ============================================================
def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
def _collect_stub_sha1s(base_dir: str, bd_len: sizet, level: int,
out_arr: str, out_count: int, max_count: int) -> int:
"""递归扫描子目录,收集 .stub.ll 文件的 SHA1"""
if base_dir is None or out_arr is None:
@@ -119,7 +96,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
if out_count >= max_count:
return out_count
find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
return out_count
string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__())
@@ -159,7 +136,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
if handle != win32base.INVALID_HANDLE_VALUE:
while True:
# 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16)
attrs: t.CUInt32T = find_data.dwFileAttributes
attrs: uint32 = find_data.dwFileAttributes
if (attrs & 16) != 0:
fname = find_data.cFileName
if fname is not None:
@@ -174,7 +151,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
# 构建子目录路径并递归扫描
sub_dir: str = Utils.JoinPath(base_dir, fname)
if sub_dir is not None:
sub_dir_len: t.CSizeT = string.strlen(sub_dir)
sub_dir_len: sizet = string.strlen(sub_dir)
out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count)
stdlib.free(sub_dir)
if win32file.FindNextFileA(handle, find_data) == 0:
@@ -189,7 +166,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
# ============================================================
def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr,
def _load_includes_sha1_set(pool: memhub.MemBuddy*,
temp_dir: str,
sha1_set: str) -> int:
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
@@ -206,8 +183,8 @@ def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr,
for i in range(StubMerger._sha1_store_count):
if count >= StubMerger.MAX_INCLUDES_SHA1:
break
idx: t.CSizeT = t.CSizeT(i) * 17
string.strcpy(sha1_set + t.CSizeT(count) * 17, StubMerger._sha1_store_arr + idx)
idx: sizet = sizet(i) * 17
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
count += 1
return count
# ============================================================
@@ -222,21 +199,14 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
return 1
return 0
# ============================================================
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出)
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(仅人类可读的调试输出,程序内部不读取,
# 机器分析用 PopulateSha1MapStore 填充的全局内存存储器)
#
# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。
# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。
#
# Args:
# mb: 内存池
# temp_dir: 临时目录(_sha1_map.txt 写入位置)
# scan_result: IncludesScanner 扫描结果
#
# Returns:
# 0 成功,非 0 失败
# Args: mb 内存池;temp_dir 临时目录(_sha1_map.txt 写入位置);scan_result IncludesScanner 扫描结果
# Returns: 0 成功,非 0 失败
# ============================================================
def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
scan_result: IncludesScanner.ScanResult | t.CPtr,
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str,
scan_result: rawptr[IncludesScanner.ScanResult],
filter_set: str,
filter_count: int) -> int:
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
@@ -247,19 +217,19 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
return 1
# 构造路径 temp_dir/_sha1_map.txt
dir_len: t.CSizeT = string.strlen(temp_dir)
dir_len: sizet = string.strlen(temp_dir)
map_path: bytes = stdlib.malloc(dir_len + 32)
if map_path is None:
return 1
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
# 打开文件写入(CREATE_ALWAYS)
f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.W)
f: rawptr[fileio.File] = fileio.File(map_path, fileio.MODE.W)
if f.closed:
return 1
# 写入每个 include 条目: {sha1}:includes/{rel_path}\n
entry_size_w: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
line_buf: bytes = stdlib.malloc(512)
if line_buf is None:
f.close()
@@ -267,8 +237,8 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
written_count: int = 0
for i in range(scan_result.Count):
ea_w: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_w
ent_w: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_w, t.CPtr))
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
ent_w: rawptr[IncludesScanner.FileEntry] = (rawptr[IncludesScanner.FileEntry])(ea_w)
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None:
continue
# 若提供过滤集合,只写入集合中的条目
@@ -276,7 +246,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0:
continue
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
line_len_w: t.CSizeT = string.strlen(line_buf)
line_len_w: sizet = string.strlen(line_buf)
f.write(line_buf, line_len_w)
written_count += 1
+62 -95
View File
@@ -13,7 +13,7 @@ import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
@@ -27,21 +27,16 @@ import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerIR - StubMerger 拆分(阶段 E):StubMergerIR
# StubMergerIR - IR 文本去重/注释与模块名-SHA1 映射工具
# ============================================================
# ============================================================
# IsDeclOnlyModule - 判断模块是否为纯声明文件
# IsDeclOnlyModule - 判断模块是否为纯声明文件(读 {temp_dir}/{sha1}.text.ll 扫描是否含 "define " 行)
#
# 读取 {temp_dir}/{sha1}.text.ll,扫描是否含 "define " 行:
# 返回 1 = 纯声明(无函数体,如 stdio.vp 全 pass),无需 .obj
# 返回 0 = 实现文件(含 define 函数体)
# 返回 -1 = text.ll 不存在/无法判断
#
# 与 Phase B+(Phase2.vp 编译缺失 includes 时)的内联判断逻辑一致,
# 供 Phase C 链接时区分"声明文件无需 .obj"与"实现文件缺失"。
# 返回 1 = 纯声明(无函数体,如全 pass 的模块,无需 .obj);0 = 实现文件(含 define 函数体);-1 = text.ll 不存在/无法判断。
# 与 Phase B+ 编译缺失 includes 时的内联判断逻辑一致,供 Phase C 链接时区分「声明文件无需 .obj」与「实现文件缺失」。
# ============================================================
def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
"""判断模块是否为纯声明文件(text.ll 无 define 行)"""
if temp_dir is None or sha1 is None:
return -1
@@ -53,7 +48,7 @@ def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
if tf_d.closed:
return -1
tbuf_d: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
tbr_d: t.CInt64T = 0
tbr_d: int64 = 0
if tbuf_d is not None:
tbr_d = tf_d.read_all(tbuf_d, StubMerger.STUB_READ_BUF_SIZE)
tf_d.close()
@@ -65,17 +60,15 @@ def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
return 1
if tbr_d < StubMerger.STUB_READ_BUF_SIZE:
tbuf_d[tbr_d] = '\0'
else:
tbuf_d[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描:找 "define " 行即为实现文件
tpos_d: t.CSizeT = 0
tpos_d: sizet = 0
while tpos_d < tbr_d:
tls_d: t.CSizeT = tpos_d
tls_d: sizet = tpos_d
while tpos_d < tbr_d:
if tbuf_d[tpos_d] == '\n':
break
tpos_d += 1
tll_d: t.CSizeT = tpos_d - tls_d
tll_d: sizet = tpos_d - tls_d
if tpos_d < tbr_d:
tpos_d += 1
if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0:
@@ -86,29 +79,24 @@ def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
# ============================================================
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
#
# 逐字节strncmp搜索 func_sig(如 @"sha1.func"(/ @printf( ),每次
# 找到匹配后检查行首是否以 "declare " 或 "define " 开头,跳过 call 语句。
# 避免 strstr 子串匹配误匹配 call ... @"func"(...) 中的 @"func"( 子串。
# 逐字节 strncmp 搜索 func_sig(如 @"sha1.func"(/ @printf(),命中后检查行首是否为 "declare "/"define ",跳过 call 语句
# (避免 strstr 子串匹配误命中 call ... @"func"(...)。
#
# Args:
# out_buf: 已有的 IR 文本缓冲区
# func_sig: 函数签名片段,如 @"sha1.func"(/ @printf(
#
# Returns:
# 1=已存在 declare/define, 0=不存在
# Args: out_buf 已有 IR 文本缓冲区;func_sig 函数签名片段,如 @"sha1.func"(/ @printf(
# Returns: 1=已存在 declare/define, 0=不存在
# ============================================================
def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
"""检查 out_buf 中是否已有函数的 declare 或 define 行"""
if out_buf is None or func_sig is None:
return 0
sig_len: t.CSizeT = string.strlen(func_sig)
out_len: t.CSizeT = string.strlen(out_buf)
sig_len: sizet = string.strlen(func_sig)
out_len: sizet = string.strlen(out_buf)
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
for pos in range(0, out_len - sig_len + 1):
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: t.CSizeT = pos
ls: sizet = pos
while ls > 0 and out_buf[ls - 1] != '\n':
ls -= 1
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
@@ -127,14 +115,14 @@ def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int:
"""在 out_buf 中搜索 declare 行(包含 func_sig),将其注释掉"""
if out_buf is None or func_sig is None:
return 0
sig_len: t.CSizeT = string.strlen(func_sig)
out_len: t.CSizeT = string.strlen(out_buf)
sig_len: sizet = string.strlen(func_sig)
out_len: sizet = string.strlen(out_buf)
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
while pos + sig_len <= out_len:
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: t.CSizeT = pos
ls: sizet = pos
while ls > 0 and out_buf[ls - 1] != '\n':
ls -= 1
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
@@ -160,11 +148,11 @@ def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int:
"""
if out_buf is None or global_name is None:
return 0
name_len: t.CSizeT = string.strlen(global_name)
out_len: t.CSizeT = string.strlen(out_buf)
name_len: sizet = string.strlen(global_name)
out_len: sizet = string.strlen(out_buf)
if name_len == 0 or out_len == 0 or name_len > out_len:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
commented: int = 0
while pos + name_len <= out_len:
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
@@ -183,11 +171,11 @@ def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int:
"""
if out_buf is None or global_name is None:
return 0
name_len: t.CSizeT = string.strlen(global_name)
out_len: t.CSizeT = string.strlen(out_buf)
name_len: sizet = string.strlen(global_name)
out_len: sizet = string.strlen(out_buf)
if name_len == 0 or out_len == 0 or name_len > out_len:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
while pos + name_len <= out_len:
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
# 确认是行首(前一个是换行或 pos==0)
@@ -211,17 +199,17 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
"""
if out_buf is None or type_name_prefix is None:
return 0
prefix_len: t.CSizeT = string.strlen(type_name_prefix)
out_len: t.CSizeT = string.strlen(out_buf)
prefix_len: sizet = string.strlen(type_name_prefix)
out_len: sizet = string.strlen(out_buf)
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
while pos + prefix_len <= out_len:
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
# 确认是行首(前一个是换行或 pos==0)
if pos == 0 or out_buf[pos - 1] == '\n':
# 检查后面是否是 " = type opaque"
p: t.CSizeT = pos + prefix_len
p: sizet = pos + prefix_len
# 跳过空格
while p < out_len and out_buf[p] == ' ':
p += 1
@@ -239,15 +227,8 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
# ============================================================
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
#
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp
# 内部 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
# deps_buf 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer
# 子模块类型;模块名含 '.' 时检查其顶层包名是否在 deps_buf 中(deps_buf = "ast stdio string" 时 "ast.lexer" → 1,"foo.bar" → 0)。
# ============================================================
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
@@ -257,12 +238,12 @@ def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
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))
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
if pkg_len == 0 or pkg_len >= 256:
return 0
# 构造顶层包名(截断到 '.' 之前)
# 直接在 mod_name 上临时截断检查,避免分配
saved_ch: t.CChar = mod_name[pkg_len]
saved_ch: char = mod_name[pkg_len]
mod_name[pkg_len] = '\0'
result: int = GenApi.is_module_imported(deps_buf, mod_name)
mod_name[pkg_len] = saved_ch
@@ -278,11 +259,11 @@ def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
"""检查 out_buf 中是否已有 type_name_prefix 的完整定义"""
if out_buf is None or type_name_prefix is None:
return 0
prefix_len: t.CSizeT = string.strlen(type_name_prefix)
out_len: t.CSizeT = string.strlen(out_buf)
prefix_len: sizet = string.strlen(type_name_prefix)
out_len: sizet = string.strlen(out_buf)
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
return 0
pos: t.CSizeT = 0
pos: sizet = 0
while pos + prefix_len <= out_len:
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
# 确认是行首(前一个是换行或 pos==0),且未被注释(行首不是 ';')
@@ -293,7 +274,7 @@ def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
is_line_start = 1
if is_line_start == 1:
# 检查后面是否是 " = type {" 或 " = type <"
p: t.CSizeT = pos + prefix_len
p: sizet = pos + prefix_len
while p < out_len and out_buf[p] == ' ':
p += 1
if p < out_len and out_buf[p] == '=':
@@ -315,7 +296,7 @@ def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
# ============================================================
def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet,
sha1_list: str,
mod_list: str) -> int:
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
@@ -331,26 +312,17 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
for i in range(StubMerger._sha1_store_count):
if count >= StubMerger.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, StubMerger._sha1_store_arr + idx)
string.strcpy(mod_list + t.CSizeT(count) * 64, StubMerger._sha1_store_mod + idx2)
idx: sizet = sizet(i) * 17
idx2: sizet = sizet(i) * 64
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
count += 1
return count
# ============================================================
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配)
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配;供依赖图分析「模块名 → SHA1」)
#
# 在 sha1_arr/mod_arr 映射中查找模块名对应的 SHA1。
# 用于依赖图分析:模块名 → SHA1。
#
# Args:
# sha1_arr: SHA1 数组(每个 17 字节)
# mod_arr: 模块名数组(每个 64 字节)
# count: 映射条目数
# mod_name: 要查找的模块名
#
# Returns:
# SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
# Args: sha1_arr SHA1 数组(每个 17 字节);mod_arr 模块名数组(每个 64 字节);count 映射条目数;mod_name 要查找的模块名
# Returns: SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
# ============================================================
def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
count: int, mod_name: str) -> str:
@@ -365,46 +337,41 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
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
idx: sizet = sizet(i) * 64
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
# (热点:本函数全表逐条 strcmp 查名)。
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
sidx: sizet = sizet(i) * 17
return sha1_arr + sidx
# 第二遍:包名回退("ast" → "ast.__init__")
nm_len: t.CSizeT = string.strlen(mod_name)
nm_len: sizet = 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
idx2: sizet = sizet(i) * 64
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
sidx2: sizet = sizet(i) * 17
stdlib.free(init_buf)
return sha1_arr + sidx2
stdlib.free(init_buf)
return None
# ============================================================
# _AddSha1ToSet - 将 SHA1 加入集合(去重)
# _AddSha1ToSet - 将 SHA1 加入集合(去重;已在集合中则 count 不变)
#
# 检查 SHA1 是否已在集合中,若不在则加入。返回新的 count。
#
# Args:
# set_buf: SHA1 集合缓冲区(每个 17 字节)
# count: 当前集合中的 SHA1 数量
# sha1: 要加入的 SHA1
#
# Returns:
# 新的 count(如果已存在则不变)
# Args: set_buf SHA1 集合缓冲区(每个 17 字节);count 当前集合中的 SHA1 数量;sha1 要加入的 SHA1
# Returns: 新的 count
# ============================================================
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
"""将 SHA1 加入集合,返回新 count"""
if set_buf is None or sha1 is None:
return count
for i in range(count):
idx: t.CSizeT = t.CSizeT(i) * 17
idx: sizet = sizet(i) * 17
if string.strcmp(set_buf + idx, sha1) == 0:
return count
if count < StubMerger.MAX_INCLUDES_SHA1:
idx: t.CSizeT = t.CSizeT(count) * 17
idx: sizet = sizet(count) * 17
string.strcpy(set_buf + idx, sha1)
return count + 1
return count
+391 -128
View File
@@ -12,7 +12,7 @@ import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
@@ -26,23 +26,152 @@ import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerStore - StubMerger 拆分(阶段 E):StubMergerStore
# StubMergerStore - 全局 SHA1 映射存储器(sha1→模块名/相对路径)读写
# ============================================================
# ============================================================
# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器
# 导入上下文记账(单一真源)
#
# 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
# 模块名通过 _PathToModuleName(includes/rel_path) 计算。
# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
# 「AST 导入语句 → imported_modules / from_imports」的记账口径必须唯一。
# 翻译期(ImportsGenMixin.translate_import)与 vpi 注入期(VpiReader 顶层
# 遍历)此前各写一份:注入期漏掉 `import X as Y → from_imports "Y:X"`(别名
# 反查模块真名的唯一入口,见 LookupModNameByAlias)以及 `import a.b.c →
# imported_modules "a"`(顶层包名放行 is_module_imported)。前者使注解里的
# `TSpec.TypeSpec*` 在 _forward_declare_ptr_from_node 4a'/4b 全落空 → 退化
# 为 Ptr(i8)(现场:GenTypes.VarEntry.Spec.PtrLevel 属性访问失败);后者使
# 包限定解析在 stub 上下文里被误判"未导入"。
#
# Args:
# scan_result: IncludesScanner 扫描结果
#
# Returns:
# 填充的条目数,-1 表示失败
# 现统一到本模块,两侧只保留各自的特有副作用(SHA1 校验 / reexport 注册 /
# add_visible_struct),字符串记账一律走下面这组函数。ImportCtx 只承载两个
# 上下文串(Viper 无多返回值,用 pool 分配的小 struct 传出)。
# ============================================================
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int:
class ImportCtx:
"""导入上下文容器:两个空格分隔串(ImportedModules / FromImports)"""
ImportedModules: str
FromImports: str
# ============================================================
# AppendImportedModule - 追加模块名到空格分隔列表(纯记账,无索引副作用)
# ============================================================
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
"""追加模块名到空格分隔列表(pool 分配),返回新列表"""
if name is None:
return cur
if cur is None:
nlen: sizet = string.strlen(name)
buf: str = pool.alloc(nlen + 1)
if buf is not None:
string.strcpy(buf, name)
return buf
old_len: sizet = string.strlen(cur)
name_len: sizet = string.strlen(name)
buf2: str = pool.alloc(old_len + 1 + name_len + 1)
if buf2 is not None:
string.strcpy(buf2, cur)
buf2[old_len] = ' '
string.strcpy(buf2 + old_len + 1, name)
return buf2
# ============================================================
# AppendFromImport - 追加 from-import 映射("local:mod[:orig]" 空格分隔)
#
# [D7·截断纪律] 旧实现固定 256 缓冲 + snprintf:三者合计超长即被静默截断,
# 该条目在查找侧仍被当作合法条目解析出错误的 local/mod/orig ⇒ 跨模块类名/
# 别名还原取到错名字(污染 Spec.SourceName)。此处按精确长度分配,无上限。
# ============================================================
def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
local: str, mod: str, orig: str) -> str:
"""追加 from-import 映射(pool 分配),返回新列表"""
if local is None or mod is None:
return cur
llen: sizet = string.strlen(local)
mlen: sizet = string.strlen(mod)
use_orig: int = 0
olen: sizet = 0
if orig is not None and string.strcmp(orig, local) != 0:
use_orig = 1
olen = string.strlen(orig)
# local ':' mod [':' orig] '\0'
need: sizet = llen + 1 + mlen + 1
if use_orig != 0:
need += 1 + olen
entry: str = pool.alloc(need)
if entry is None:
return cur
string.strcpy(entry, local)
entry[llen] = ':'
string.strcpy(entry + llen + 1, mod)
if use_orig != 0:
entry[llen + 1 + mlen] = ':'
string.strcpy(entry + llen + 1 + mlen + 1, orig)
if cur is None:
return entry
old_len: sizet = string.strlen(cur)
entry_len: sizet = string.strlen(entry)
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
if buf is not None:
string.strcpy(buf, cur)
buf[old_len] = ' '
string.strcpy(buf + old_len + 1, entry)
return buf
# ============================================================
# AllocImportCtx - 用两个既有上下文串构造记账容器
# ============================================================
def AllocImportCtx(pool: memhub.MemBuddy*,
imported_modules: str, from_imports: str) -> ImportCtx*:
"""构造导入上下文容器(pool 分配);两串可为 None"""
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
if ctx is None:
return None
ctx.ImportedModules = imported_modules
ctx.FromImports = from_imports
return ctx
# ============================================================
# NotePlainImport - 记账 `import X` / `import X as Y`(就地更新 ctx)
#
# 有别名:追加别名 `Y`,并写 from-import 映射 "Y:X"——这就是 `LookupModNameByAlias`
# 与 `_forward_declare_ptr_from_node` 4a' 反查模块真名的唯一来源,绝不可省。
# 无别名:`import a.b.c` 时把顶层包名 `a` 也追加(使 a.b.c 作为属性访问 base
# 时 is_module_imported("a") 放行)。
# ============================================================
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*,
name: str, asname: str) -> int:
"""记账单条 import(就地更新 ctx),返回 0"""
if ctx is None or name is None:
return 1
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
if asname is not None:
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname)
ctx.FromImports = AppendFromImport(pool, ctx.FromImports, asname, name, None)
else:
dot_ptr: str = string.strstr(name, ".")
if dot_ptr is not None:
pkg_len: sizet = sizet(uint64(dot_ptr) - uint64(name))
if pkg_len > 0 and pkg_len < 256:
pkg_buf: str = pool.alloc(pkg_len + 1)
if pkg_buf is not None:
string.strncpy(pkg_buf, name, pkg_len)
pkg_buf[pkg_len] = '\0'
ctx.ImportedModules = AppendImportedModule(
pool, ctx.ImportedModules, pkg_buf)
return 0
# ============================================================
# PopulateSha1MapStore - 遍历 ScanResult.Entries 构建 sha1→module_name 映射,填充全局存储器
#
# 模块名通过 _PathToModuleName(includes/rel_path) 计算;内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
# Args: scan_result IncludesScanner 扫描结果
# Returns: 填充的条目数,-1 表示失败
# ============================================================
def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int:
"""从扫描结果填充全局 SHA1 映射存储器
遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
@@ -72,17 +201,17 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
StubMerger._sha1_store_count = 0
p_bad: int = 0
entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
for i in range(scan_result.Count):
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
VLogger.error("includes SHA1 存储超上限,终止", "StubMerger")
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))
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
ent: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea)
if ent is None or ent.Sha1 is None or ent.RelPath is None:
p_bad += 1
continue
# 构建完整路径 includes/{RelPath}(_PathToModuleName 需要)
rp_len: t.CSizeT = string.strlen(ent.RelPath)
rp_len: sizet = string.strlen(ent.RelPath)
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
if full_path_buf is None:
p_bad += 1
@@ -91,10 +220,10 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
stdlib.free(full_path_buf)
if mod_name is not None:
idx: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17
idx: sizet = sizet(StubMerger._sha1_store_count) * 17
string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1)
idx2: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
idx2: sizet = sizet(StubMerger._sha1_store_count) * 64
mn_len: sizet = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
else:
@@ -102,16 +231,16 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
stdlib.free(mod_name)
# 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用)
idx3: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
if rp_len < StubMerger.MAX_REL_PATH_LEN:
string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
else:
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
# 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用)
idx_inc: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
if ent.IncDir is not None:
icd_len: t.CSizeT = string.strlen(ent.IncDir)
icd_len: sizet = string.strlen(ent.IncDir)
if icd_len < StubMerger.MAX_INC_DIR_LEN:
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
else:
@@ -119,30 +248,15 @@ 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:
fb_pbad: str = VLogger.fmt_buf()
if fb_pbad is not None:
viperlib.snprintf(fb_pbad, 1024, "includes SHA1 存储有 %d 个条目失败", p_bad)
VLogger.error(fb_pbad, "StubMerger")
VLogger.error(f"includes SHA1 存储有 {p_bad} 个条目失败", "StubMerger")
return StubMerger._sha1_store_count
# ============================================================
# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器
# AppendToSha1MapStore - 追加 App 模块条目到全局 SHA1 映射存储器(Phase1 先用 PopulateSha1MapStore 填 includes,
# Phase2 扫描 App 源文件后调用本函数追加,否则编译 App 自身时跨模块 CDefine 查找命中不到 App 模块)
#
# Phase1 调用 PopulateSha1MapStore 填充 includes 文件,
# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目,
# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块
# (如 StubMerger.StubMerger.MAX_INCLUDES 在编译 Phase2 时通过
# _build_source_path_from_sha1 查找 StubMerger 的源路径)。
#
# 若存储器未初始化(PopulateSha1MapStore 未调用),自动初始化。
# 若 SHA1 已存在(去重),跳过。
#
# Args:
# sha1: SHA1 字符串(16 字符 + null)
# rel_path: 相对路径(如 "lib/core/StubMerger.vp",
# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
#
# Returns:
# 0 成功(含去重跳过),1 失败
# 存储器未初始化时自动初始化;SHA1 已存在(去重)则跳过。
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对路径(如 "lib/core/StubMerger.vp",不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
# Returns: 0 成功(含去重跳过),1 失败
# ============================================================
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
"""追加条目到全局 SHA1 映射存储器(Phase2 用)"""
@@ -172,24 +286,21 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
stdlib.free(full_path_buf)
if mod_name is None:
return 1
# 去重:SHA1 与模块名都相同才跳过。同一 SHA1 可能被 includes 扫描先以
# 全路径模块名注册(如 App 源文件在 includes 目录下 → "Apps.Scene3D.gfx"),
# App 追加的短模块名("gfx")必须保留——否则 import gfx 的精确查找
# LookupSha1ByModName("gfx") 失败 → 后缀匹配劫持到 VKernel 同名 gfx
# (VKernel/Kernel/drivers/video/vesafb/gfx.vp,类 Scene3D 归属错乱根因)。
# 去重:SHA1 与模块名都相同才跳过——同一 SHA1 可能先以全路径模块名注册(App 源文件在 includes 目录下),
# App 追加的短模块名必须保留,否则 import 该模块的精确查找失败、后缀匹配会劫持到其它同名模块。
for i in range(StubMerger._sha1_store_count):
sidx_d: t.CSizeT = t.CSizeT(i) * 17
sidx_d: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
midx_d: t.CSizeT = t.CSizeT(i) * 64
midx_d: sizet = sizet(i) * 64
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
stdlib.free(mod_name)
return 0
# 写入 SHA1
idx_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
# 写入模块名
idx_m: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
mn_len: sizet = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
else:
@@ -197,19 +308,85 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
stdlib.free(mod_name)
# 写入相对路径
idx_r: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
rp_len: t.CSizeT = string.strlen(rel_path)
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
rp_len: sizet = string.strlen(rel_path)
if rp_len < StubMerger.MAX_REL_PATH_LEN:
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
else:
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
# App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数)
idx_inc_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
StubMerger._sha1_store_count += 1
return 0
# ============================================================
# AddIncludeToSha1MapStore - 单条登记 includes 条目到全局 SHA1 映射存储器
#
# 依赖图按需扫描(_BuildReachableSha1Set)解析到可达 includes 文件后逐条登记,未引用的 includes 不进 store;
# App 源文件仍用 AppendToSha1MapStore(inc_dir 留空)。
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对 includes 根目录的路径(如 "ast/parser.vp");inc_dir 所属 includes 根目录;mod_name 模块名(如 "ast.parser")
# Returns: 0 成功(含去重跳过),1 失败
# ============================================================
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
"""单条登记 includes 条目到全局 SHA1 映射存储器(依赖图按需扫描用)"""
if sha1 is None or rel_path is None or mod_name is None:
return 1
# 存储器未初始化:自动初始化
if StubMerger._sha1_store_arr is None:
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
return 1
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
StubMerger._sha1_store_count = 0
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
return 1
# 去重:SHA1 与模块名都相同才跳过(对齐 AppendToSha1MapStore)
for i in range(StubMerger._sha1_store_count):
sidx_d: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
midx_d: sizet = sizet(i) * 64
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
return 0
# 写 SHA1
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
# 写模块名
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
mn_len: sizet = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
else:
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
# 写相对路径
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
rp_len: sizet = string.strlen(rel_path)
if rp_len < StubMerger.MAX_REL_PATH_LEN:
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
else:
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
# 写所属 includes 根目录(非空 = includes 条目;App 条目留空)
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
if inc_dir is not None:
icd_len: sizet = string.strlen(inc_dir)
if icd_len < StubMerger.MAX_INC_DIR_LEN:
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
else:
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1)
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
else:
StubMerger._sha1_store_incdir[idx_inc] = '\0'
StubMerger._sha1_store_count += 1
return 0
# ============================================================
# GetSha1StoreCount - 获取全局存储器条目数
# ============================================================
def GetSha1StoreCount() -> int:
@@ -227,15 +404,88 @@ def GetSha1StoreCount() -> int:
def LookupSha1ByModName(mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
# ============================================================
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1
# 泛型类归属表(泛型类名 → 定义模块 SHA1)——仅 REPL 会话登记/查询
#
# 为什么需要:REPL 的 includes 会话初始化只注入 `.vpi` 的类(泛型类被跳过),
# 不跑批处理的 _PreRegister*GenericTemplates ⇒ 会话内泛型模板表为空 ⇒ 注解里的
# `list[rawptr[AST]]` 等泛型下标解析(`TypeGenResolve._resolve_generic_subscript_ty`)
# 恒 miss。会话侧的最后兜底要按**定义侧特化命名** "%{模板模块 sha1}.{mangled}"
# 造 opaque 具名类型(实证:`%"b56d2f995104690d.list[rawptr[AST]]"`),因此需要
# "泛型类定义在哪个模块"这一事实——由注入期扫 `.vpi` 的 ClassDef 登记(见
# VpiReader._repl_stub_collect_module)。
#
# 归属模块 sha1 无法从类名反推(模块名取自文件路径,class list ← _list.vp;用户
# 泛型类更是名字与模块名无关),故必须显式登记,不做"按名字猜"。
# 批处理不登记(表恒空 ⇒ 查询恒 None ⇒ 批处理路径一字不改)。
# 容量:includes 现有 6 个泛型类定义(list/dict 系 + Vector/GSList/ndarray),取 32 槽。
# ============================================================
GENERIC_OWNER_MAX: t.CDefine = 32
_generic_owner_names: bytes = None
_generic_owner_sha1s: bytes = None
_generic_owner_count: int = 0
def ResetGenericOwners() -> None:
"""清空泛型类归属表(REPL 会话初始化开始时调用一次)"""
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
if _generic_owner_names is not None:
stdlib.free(_generic_owner_names)
_generic_owner_names = None
if _generic_owner_sha1s is not None:
stdlib.free(_generic_owner_sha1s)
_generic_owner_sha1s = None
_generic_owner_count = 0
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
"""登记"泛型类名 → 定义模块 SHA1";1=已登记(含重复)/ 0=失败(参数/满/同名异 sha1)
同名异 sha1(跨模块同名泛型类)保留首个,与泛型模板表按类名去重的口径一致。
"""
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
if cls_name is None or mod_sha1 is None:
return 0
if cls_name[0] == '\0' or mod_sha1[0] == '\0':
return 0
if _generic_owner_names is None:
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
if _generic_owner_names is None or _generic_owner_sha1s is None:
return 0
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
_generic_owner_count = 0
i_go: int
for i_go in range(_generic_owner_count):
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0:
return 1
return 0
if _generic_owner_count >= GENERIC_OWNER_MAX:
return 0
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
_generic_owner_count += 1
return 1
def LookupGenericOwnerSha1(cls_name: str) -> str:
"""按泛型类名取定义模块 SHA1(返回内部指针,无需释放;未登记 None)"""
if cls_name is None or _generic_owner_names is None:
return None
i_lg: int
for i_lg in range(_generic_owner_count):
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0:
return _generic_owner_sha1s + sizet(i_lg) * 17
return None
# ============================================================
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1(用于 from .X import Y:impf.module 是 ".X",而映射表键是 "pkg.X")
#
# 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
# 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
# 包含三种匹配方式:精确、后缀、反向后缀。
#
# Returns:
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# Returns: SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
"""后缀匹配查找模块 SHA1"""
@@ -245,55 +495,52 @@ def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
return None
if StubMerger._sha1_store_count <= 0:
return None
suffix_len_sf: t.CSizeT = string.strlen(mod_suffix)
suffix_len_sf: sizet = string.strlen(mod_suffix)
if suffix_len_sf == 0:
return None
i_sf: int
for i_sf in range(StubMerger._sha1_store_count):
idx_sf: t.CSizeT = t.CSizeT(i_sf) * 64
idx_sf: sizet = sizet(i_sf) * 64
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
mod_len_sf: t.CSizeT = string.strlen(mod_name_sf)
mod_len_sf: sizet = string.strlen(mod_name_sf)
if mod_len_sf == 0:
continue
# 精确匹配
if mod_len_sf == suffix_len_sf:
if string.strcmp(mod_name_sf, mod_suffix) == 0:
sidx_sf: t.CSizeT = t.CSizeT(i_sf) * 17
sidx_sf: sizet = sizet(i_sf) * 17
return StubMerger._sha1_store_arr + sidx_sf
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
if mod_len_sf > suffix_len_sf:
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
sidx2_sf: t.CSizeT = t.CSizeT(i_sf) * 17
sidx2_sf: sizet = sizet(i_sf) * 17
return StubMerger._sha1_store_arr + sidx2_sf
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
if suffix_len_sf > mod_len_sf:
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0:
sidx_rev_sf: t.CSizeT = t.CSizeT(i_sf) * 17
sidx_rev_sf: sizet = sizet(i_sf) * 17
return StubMerger._sha1_store_arr + sidx_rev_sf
return None
# ============================================================
# LookupSha1ByAlias - 从 from_imports 别名映射解析模块 SHA1
#
# from_imports 条目格式: "local:module" 或 "local:module:orig"(空格分隔),
# 由 ImportsGenMixin.add_from_import 写入(import X as Y → "Y:X")。
# 命中 local==alias 时,对 module 执行 LookupSha1ByModName 返回 SHA1。
#
# Returns:
# SHA1 字符串指针(指向全局存储器内部),None 未找到
# from_imports 条目格式 "local:module" 或 "local:module:orig"(空格分隔,由 ImportsGenMixin.add_from_import 写入);
# 命中 local==alias 时对 module 执行 LookupSha1ByModName。
# Returns: SHA1 字符串指针(指向全局存储器内部),None 未找到
# ============================================================
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
"""从 from_imports 别名映射解析模块 SHA1(改名别名 import X as Y 场景)"""
if from_imports is None or alias is None:
return None
alias_len: t.CSizeT = string.strlen(alias)
alias_len: sizet = string.strlen(alias)
if alias_len == 0:
return None
total_len: t.CSizeT = string.strlen(from_imports)
ci: t.CSizeT = 0
total_len: sizet = string.strlen(from_imports)
ci: sizet = 0
while ci < total_len:
# 跳过前导空格
while ci < total_len and from_imports[ci] == ' ':
@@ -301,14 +548,14 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
if ci >= total_len:
break
# 条目起点 ci,找 ':'(local_name 结束)
colon: t.CSizeT = ci
colon: sizet = ci
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
colon += 1
local_len: t.CSizeT = colon - ci
local_len: sizet = colon - ci
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
# 比较 local_name == alias
match: int = 1
li: t.CSizeT = 0
li: sizet = 0
while li < alias_len:
if from_imports[ci + li] != alias[li]:
match = 0
@@ -316,8 +563,8 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
li += 1
if match == 1:
# 提取 module(colon+1 到下一个 ':' 或空格)
m_start: t.CSizeT = colon + 1
m_end: t.CSizeT = m_start
m_start: sizet = colon + 1
m_end: sizet = m_start
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
m_end += 1
if m_end > m_start:
@@ -336,22 +583,19 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
# ============================================================
# LookupModNameByAlias - 从 from_imports 别名映射解析模块真名
#
# import X as Y 时 ImportsGenMixin.add_from_import 写入 "Y:X"(Y=别名,
# X=模块真名)。本函数返回别名 Y 对应的模块真名 X(供限定名解析
# "Y.Class" → 按 X 模块归属匹配,如 "types.fileinfo" → "fat32_types")。
#
# Returns:
# 模块名字符串(stdlib 分配,调用方负责 free),None 未找到
# import X as Y 时 ImportsGenMixin.add_from_import 写入 "Y:X"(Y=别名,X=模块真名);
# 本函数返回别名 Y 对应的模块真名 X(供限定名解析 "Y.Class" → 按 X 模块归属匹配)。
# Returns: 模块名字符串(stdlib 分配,调用方负责 free),None 未找到
# ============================================================
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
"""从 from_imports 别名映射解析模块真名(import X as Y → Y:X)"""
if from_imports is None or alias is None:
return None
alias_len: t.CSizeT = string.strlen(alias)
alias_len: sizet = string.strlen(alias)
if alias_len == 0:
return None
total_len: t.CSizeT = string.strlen(from_imports)
ci: t.CSizeT = 0
total_len: sizet = string.strlen(from_imports)
ci: sizet = 0
while ci < total_len:
# 跳过前导空格
while ci < total_len and from_imports[ci] == ' ':
@@ -359,14 +603,14 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
if ci >= total_len:
break
# 条目起点 ci,找 ':'(local_name 结束)
colon: t.CSizeT = ci
colon: sizet = ci
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
colon += 1
local_len: t.CSizeT = colon - ci
local_len: sizet = colon - ci
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
# 比较 local_name == alias
match: int = 1
li: t.CSizeT = 0
li: sizet = 0
while li < alias_len:
if from_imports[ci + li] != alias[li]:
match = 0
@@ -374,8 +618,8 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
li += 1
if match == 1:
# 提取 module(colon+1 到下一个 ':' 或空格)
m_start: t.CSizeT = colon + 1
m_end: t.CSizeT = m_start
m_start: sizet = colon + 1
m_end: sizet = m_start
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
m_end += 1
if m_end > m_start:
@@ -392,15 +636,9 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
# ============================================================
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
#
# Phase B+ 前用 declare_only 翻译 includes 文件,提取 _imported_modules
# 后调用此函数存入内存。_BuildReachableSha1Set 从内存读取,不读 deps.txt。
#
# Args:
# sha1: includes 文件的 SHA1(16字符)
# deps_str: 依赖模块名字符串(空格分隔,如 "t c stdint string")
#
# Returns:
# 0 成功,非 0 失败
# Phase B+ 前用 declare_only 翻译 includes 文件、提取 _imported_modules 后调用本函数存入内存;_BuildReachableSha1Set 从内存读取,不读 deps.txt。
# Args: sha1 includes 文件的 SHA1(16字符);deps_str 依赖模块名字符串(空格分隔,如 "t c stdint string")
# Returns: 0 成功,非 0 失败
# ============================================================
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
"""将 includes 文件的依赖存入内存"""
@@ -419,16 +657,16 @@ def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
return 1
# 去重:若 SHA1 已存在则跳过
for i in range(StubMerger._includes_deps_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
sidx: sizet = sizet(i) * 17
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
return 0
# 写入 SHA1
idx: t.CSizeT = t.CSizeT(StubMerger._includes_deps_count) * 17
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
# 写入依赖字符串
idx2: t.CSizeT = t.CSizeT(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
if deps_str is not None:
dl: t.CSizeT = string.strlen(deps_str)
dl: sizet = string.strlen(deps_str)
if dl < StubMerger.MAX_DEPS_STR_LEN:
string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
else:
@@ -453,9 +691,9 @@ def LookupIncludesDeps(sha1: str) -> str:
if StubMerger._includes_deps_count <= 0:
return None
for i in range(StubMerger._includes_deps_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
sidx: sizet = sizet(i) * 17
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
idx2: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_DEPS_STR_LEN
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
return StubMerger._includes_deps_str + idx2
return None
def GetSha1StoreArrPtr() -> bytes:
@@ -477,24 +715,23 @@ def GetSha1StoreIncdirArrPtr() -> bytes:
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*StubMerger.MAX_REL_PATH_LEN]
# 内存归全局存储器所有,调用者不要 free
# ============================================================
def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int:
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
"""获取全局存储器数组指针
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针,
函数将存储器数组指针写入这些位置。
返回条目数,-1 失败
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 槽的地址(t.CPtr*),
函数把存储器数组指针写入这些槽。返回条目数,-1 失败
"""
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
return -1
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None:
return -1
sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr)) # 存疑
sp: rawptr[t.CPtr] = out_sha1_arr
if sp is not None:
sp[0] = StubMerger._sha1_store_arr
mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr))
mp: rawptr[t.CPtr] = out_mod_arr
if mp is not None:
mp[0] = StubMerger._sha1_store_mod
rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr))
rp: rawptr[t.CPtr] = out_rel_arr
if rp is not None:
rp[0] = StubMerger._sha1_store_rel
return StubMerger._sha1_store_count
@@ -502,14 +739,9 @@ def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CP
# ============================================================
# LookupSha1RelPath - 通过 SHA1 查找相对路径
#
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free)。
# 用于错误提示:将 sha1 转换为人类可读的文件路径。
#
# Args:
# sha1: SHA1 字符串(16 字符)
#
# Returns:
# 相对路径字符串指针(只读),None=未找到
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free),用于把 sha1 转成人类可读的文件路径。
# Args: sha1 SHA1 字符串(16 字符)
# Returns: 相对路径字符串指针(只读),None=未找到
# ============================================================
def LookupSha1RelPath(sha1: str) -> str:
"""通过 SHA1 查找相对路径,返回只读指针,None=未找到"""
@@ -518,8 +750,39 @@ def LookupSha1RelPath(sha1: str) -> str:
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None:
return None
for i in range(StubMerger._sha1_store_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
sidx: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
ridx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_REL_PATH_LEN
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
return StubMerger._sha1_store_rel + ridx
return None
# ============================================================
# LookupModNameBySha1 - 通过 SHA1 反查模块名
#
# 用途:判定"本 stub 是否某包的 __init__"(模块名以 ".__init__" 结尾)——该事实
# **不依赖 sha1→rel_path 字段**:rel_path 反查在部分运行形态下拿不到条目 ⇒
# VpiReader 原来的 `strstr(rel_path, "__init__.vp")` 判定恒 false ⇒ 包重导出类的
# 别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类**限定名在泛型实参里保留包前缀**,
# 与构造函数侧(真定义模块前缀)分裂成两个类型(现场 ClassGenGeneric.vp:92 的
# CastError;幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物 opaque)。
# 模块名与 rel_path 是同一次登记写入的(PopulateSha1MapStore /
# AppendToSha1MapStore),但模块名可用即足以作出判定与取包前缀。
#
# 同一 SHA1 可能有多条(App 源文件按全路径模块名与短模块名各登记一次):返回
# **第一条命中**;各条的包前缀一致,调用方只取"去 .__init__ 后的前缀"。
# Args: sha1 SHA1 字符串(16 字符)
# Returns: 模块名字符串指针(只读,不要 free),None=未找到
# ============================================================
def LookupModNameBySha1(sha1: str) -> str:
"""通过 SHA1 查找模块名,返回只读指针,None=未找到"""
if sha1 is None or sha1[0] == '\0':
return None
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
return None
for i in range(StubMerger._sha1_store_count):
sidx_mb: sizet = sizet(i) * 17
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
midx_mb: sizet = sizet(i) * 64
return StubMerger._sha1_store_mod + midx_mb
return None
+28 -47
View File
@@ -12,7 +12,7 @@ import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
@@ -26,44 +26,31 @@ import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerTopo - StubMerger 拆分(阶段 E):StubMergerTopo
# StubMergerTopo - 对可达 includes 做逆拓扑排序(被依赖者先编译)
#
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
# StubMerger._xxx 访问(main.vp 先例)。
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
# ============================================================
# ============================================================
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
#
# 背景:跨模块 vtable 生成要求被依赖模块先完整翻译(如 astaux.vp 继承
# base.vp 的 AST,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表)。
# Phase B+ 原先按 store 扫描顺序编译,依赖顺序不保证 → fail-fast 暴露。
# 跨模块 vtable 生成要求被依赖模块先完整翻译(子类 vtable 槽位需父类方法的类型已注册到全局表)。
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中);入度为 0(不依赖任何可达模块)者先输出,
# 输出后对依赖它的模块减入度,入度归 0 者入队。reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
#
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中)。
# indegree[m] = m 在可达集合内的直接依赖数;
# 入度为 0(不依赖任何可达模块)者先输出,输出后对依赖它的模块
# 减入度,入度归 0 者入队。结果即"被依赖者先编译"。
# reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
#
# Args:
# store_count / store_sha1_arr / store_mod_arr: store 元数据
# reachable_set / reachable_count: 可达集合(_BuildReachableSha1Set 输出)
# sorted_idx: 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
#
# Returns:
# 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
# Args: store_count / store_sha1_arr / store_mod_arr store 元数据;reachable_set / reachable_count 可达集合(_BuildReachableSha1Set 输出);sorted_idx 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
# Returns: 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
# ============================================================
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
store_mod_arr: bytes, reachable_set: bytes,
reachable_count: int,
sorted_idx: t.CSizeT | t.CPtr) -> int:
sorted_idx: sizet*) -> int:
"""对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx"""
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)))
indegree: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
queue: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
rel_idx: sizet* = (sizet*)(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)
@@ -87,28 +74,23 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
rel_count += 1
else:
for i_t in range(store_count):
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0:
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + sizet(i_t) * 17, reachable_set, reachable_count) != 0:
rel_idx[rel_count] = i_t
rel_count += 1
# 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行)
fb_rel: str = VLogger.fmt_buf()
if fb_rel is not None:
viperlib.snprintf(fb_rel, 1024, "诊断 topo rel_count=%d", rel_count)
VLogger.debug(fb_rel, "StubMergerTopo")
# 计算入度:m 在可达集合内的直接依赖数
i_r: int
for i_r in range(rel_count):
si: int = rel_idx[i_r]
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si) * 17)
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
if deps_si is None:
continue
dl_si: t.CSizeT = string.strlen(deps_si)
dpos: t.CSizeT = 0
dl_si: sizet = string.strlen(deps_si)
dpos: sizet = 0
while dpos < dl_si:
dstart: t.CSizeT = dpos
dstart: sizet = dpos
while dpos < dl_si and deps_si[dpos] != ' ':
dpos += 1
dlen: t.CSizeT = dpos - dstart
dlen: sizet = dpos - dstart
if dpos < dl_si:
dpos += 1
if dlen == 0:
@@ -133,32 +115,31 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
queue[q_tail] = si2
q_tail += 1
out_count: int = 0
# Kahn 主循环必须用 while(不能用 for range):循环体内会动态入队
# (queue[q_tail]=si3; q_tail+=1),for range 上界只求值一次,
# 动态入队的模块永远不会被处理且兜底(indegree>0)也捞不回 → 拓扑漏项
# Kahn 主循环必须用 while 而非 for range:循环体内会动态入队(for range 上界只求值一次,
# 动态入队的模块永远不会被处理,且 indegree>0 兜底也捞不回 → 拓扑漏项)
q_head: int = 0
while q_head < q_tail:
m: int = queue[q_head]
sorted_idx[out_count] = m
out_count += 1
# 对依赖 m 的可达模块减入度
mod_m: str = store_mod_arr + t.CSizeT(m) * 64
mod_m_len: t.CSizeT = string.strlen(mod_m)
mod_m: str = store_mod_arr + sizet(m) * 64
mod_m_len: sizet = string.strlen(mod_m)
for i_r in range(rel_count):
si3: int = rel_idx[i_r]
if indegree[si3] == 0:
continue
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si3) * 17)
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si3) * 17)
if deps_si3 is None:
continue
dl_si3: t.CSizeT = string.strlen(deps_si3)
dpos3: t.CSizeT = 0
dl_si3: sizet = string.strlen(deps_si3)
dpos3: sizet = 0
found_dep: int = 0
while dpos3 < dl_si3 and found_dep == 0:
dstart3: t.CSizeT = dpos3
dstart3: sizet = dpos3
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ':
dpos3 += 1
dlen3: t.CSizeT = dpos3 - dstart3
dlen3: sizet = dpos3 - dstart3
if dpos3 < dl_si3:
dpos3 += 1
if dlen3 == 0:
+10 -17
View File
@@ -12,7 +12,7 @@ import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
@@ -20,26 +20,21 @@ import lib.Projectrans.Config as Config
# ============================================================
# StubMerger - stub 声明合并模块
# StubMerger - stub 声明合并模块(Phase2 核心)
#
# 从 temp 目录加载所有 .stub.ll 文件,将声明内容合并到主 IR。
# 这是 Phase2 的核心功能:翻译主文件时,将所有依赖的 stub
# 声明合并到主 IR 前面,使链接器能解析跨模块符号引用。
#
# 合并策略:
# 1. 扫描 temp 目录,收集所有 {sha1}.stub.ll 文件
# 2. 读取每个 stub 文件内容
# 3. 过滤去重(避免重复声明)
# 4. 将声明插入到主 IR 的 target triple 之后
# 从 temp 目录加载所有 .stub.ll,把依赖模块的声明合并到主 IR 的 target triple 之后,使链接器能解析跨模块符号引用。
# 合并策略:扫描 temp 目录收集 {sha1}.stub.ll → 读取内容 → 过滤去重(避免重复声明)→ 插入主 IR。
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
_mbuddy: memhub.MemBuddy*
# 最大 includes SHA1 数(用于过滤)
MAX_INCLUDES_SHA1: t.CDefine = 1024
# stub 读取缓冲区大小
STUB_READ_BUF_SIZE: t.CDefine = 1048576
# 1MB 不够:ast/parser.vp 的 text.ll >1MB,BuildCombinedIR 读取本地 text.ll
# 单次截断 → 合并 IR 尾部丢字(opt: "end of file in global variable name")。
STUB_READ_BUF_SIZE: t.CDefine = 8388608
# 最大 includes 条目数
MAX_INCLUDES: t.CDefine = 1024
# 可达性分析 worklist/processed 缓冲区大小(8192 不够:递归扫描 source_dir
@@ -61,10 +56,8 @@ _includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个
_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个)
_includes_deps_count: int # 条目数
# BuildCombinedIR 全目录扫描缓存:Phase B 对每个源文件调用一次 BuildCombinedIR,
# 每次 _collect_stub_sha1s 递归扫描 temp_dir 全部切片子目录(O(N×S) 目录枚举,
# 90 文件 × 全目录是 Phase B 慢的根因之一)。按 temp_dir 缓存扫描结果,
# 首次扫描后复用,temp_dir 变化时自动重建。
# BuildCombinedIR 全目录扫描缓存:Phase B 每个源文件调用一次 BuildCombinedIR,而 _collect_stub_sha1s 会递归枚举
# temp_dir 全部切片子目录(O(N×S) 目录枚举很慢);按 temp_dir 缓存扫描结果,首次扫描后复用,temp_dir 变化时自动重建。
_stub_scan_cache_dir: str # 缓存对应的 temp_dir(None=未初始化)
_stub_scan_cache_arr: str # 扫描结果数组(每 17 字节一个 SHA1,MAX_INCLUDES_SHA1 个)
_stub_scan_cache_count: int # 收集数量
+13 -13
View File
@@ -1,13 +1,13 @@
import t, c
from stdint import *
import memhub
import lib.core.VLogger as VLogger
# ============================================================
# lib 包入口 - 声明全局 _mbuddy 内存池指针
# ============================================================
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
_mbuddy: memhub.MemBuddy | t.CPtr
import t, c
from stdint import *
import memhub
import lib.core.VLogger as VLogger
# ============================================================
# lib 包入口 - 声明全局 _mbuddy 内存池指针
# ============================================================
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
_mbuddy: memhub.MemBuddy*
+34 -68
View File
@@ -1,68 +1,34 @@
import t, c
import memhub
# ============================================================
# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式)
#
# TPC 的 Translator 在 __init__ 集中创建编译器各子对象(Handler/
# SymbolTable/Exportable 等),是编译器的"装配器/工厂";TPV 的 main
# 目前手动创建 mb/gen 并逐模块注入(_mbuddy/set_global_gen),装配
# 逻辑散落。本工厂集中持有编译器级共享内存对象:
#
# - Pool :主内存池(MemBuddy,瞬时数据,受 mark/reset_to 回收)
# - PermPool :永久内存池(MemBuddy,跨 mark 持久数据专用,从不 reset)
#
# 各模块经模块级委托 CompilerFactory.get_perm_pool() 获取永久池,
# 避免把编译器专属单例塞进通用内存库 memhub(memhub 保持通用)。
# includes 层(llvmlite/__types.vp 的 DeepCopyType)不依赖本工厂:
# 深拷贝的分配源由调用方以 pool 参数传入(传 PermPool 即持久)。
# 类名与模块名同名(均为 CompilerFactory):合法场景,TPC 已支持
# 模块别名优先解析——`X.func()` 中 X 是 import 别名(IsModuleAlias)
# 时按模块函数处理,`X.X(args)` 的类构造按 FuncAttr==类名 走 struct
# 路径,二者互不冲突(勿再改类名规避,见用户明确要求)。
# ============================================================
@t.NoVTable
class CompilerFactory:
"""编译器结构体工厂(持有主池/永久池等共享对象)"""
Pool: memhub.MemBuddy | t.CPtr # 主内存池(瞬时数据,mark/reset 回收)
PermPool: memhub.MemBuddy | t.CPtr # 永久内存池(持久数据,从不 reset)
def __init__(self, pool: memhub.MemBuddy | t.CPtr,
perm_pool: memhub.MemBuddy | t.CPtr):
if self is None:
return
self.Pool = pool
self.PermPool = perm_pool
# ============================================================
# 全局工厂单例
# ============================================================
_g_factory: CompilerFactory | t.CPtr = None
def set_factory(factory: CompilerFactory | t.CPtr) -> None:
"""注册全局工厂单例(main 创建后注册)"""
global _g_factory
_g_factory = factory
def get_factory() -> CompilerFactory | t.CPtr:
"""获取全局工厂单例(未注册返回 None)"""
return _g_factory
def get_pool() -> memhub.MemBuddy | t.CPtr:
"""获取主内存池(瞬时数据分配源)"""
if _g_factory is not None:
return _g_factory.Pool
return None
def get_perm_pool() -> memhub.MemBuddy | t.CPtr:
"""获取永久内存池(跨 mark 持久数据分配源)"""
if _g_factory is not None:
return _g_factory.PermPool
return None
import t, c
import memhub
# ============================================================
# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式)
# ============================================================
novirtual class CompilerFactory:
"""编译器结构体工厂(持有主池/永久池等共享对象)"""
Pool: memhub.MemBuddy* # 主内存池(瞬时数据,mark/reset 回收)
PermPool: memhub.MemBuddy* # 永久内存池(持久数据,从不 reset)
# ---- REnum 变体注册表(编译期单例持有,与 perm_pool 的 struct entry 同寿命)----
RenumClassNames: sizet*
RenumClassVariantCounts: sizet*
RenumVariantNames: sizet*
RenumVariantTags: sizet*
RenumClassCount: int
# ---- REnum 变体字段别名表(同上,必须同寿命;_bind_variant_fields 查字段真实类型)----
RenumAliasClass: sizet*
RenumAliasField: sizet*
RenumAliasIndex: sizet*
RenumAliasTy: sizet*
RenumAliasVariant: sizet*
RenumAliasCount: int
def __init__(self, pool: memhub.MemBuddy*, perm_pool: memhub.MemBuddy*):
if self is None: return
self.Pool = pool
self.PermPool = perm_pool
# 全局工厂单例
factory: CompilerFactory* = None
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+10 -21
View File
@@ -4,44 +4,33 @@ import t, c
from stdint import *
import memhub
import llvmlite
import lib.core.Handles.HandlesExprCall as HandlesExprCall
# ============================================================
# BaseGenMixin - 生成器基座(状态容器)
#
# 对象化重构 P1(方案见 wiki/14-oo-refactor.md):
# 将 Translator/Handles 的共享状态集中于此。后续 Mixin
# (TypeGen/StructGen/ExprGen/CallGen/FuncGen/ClassGen/StmtGen/
# ImportsGen)单继承本类,逐步把 Handles* 模块的函数迁移为
# Generator 的方法,消除 (pool, builder, mod, node, ...) 六参连传。
#
# 注意:@t.NoVTable(静态绑定,骨架无多态需求;若未来需要
# 运行时多态再改 @t.CVTable,字段布局由编译器统一管理)。
# ============================================================
@t.NoVTable
class BaseGenMixin:
novirtual class BaseGenMixin:
"""生成器状态容器(基座)"""
# 内存/IR 状态
Gen: Generator.Generator | t.CPtr
Pool: memhub.MemBuddy | t.CPtr # 内存池
Module: llvmlite.LLVMModule | t.CPtr # 当前 LLVM 模块
Builder: llvmlite.IRBuilder | t.CPtr # 当前 IRBuilder
Gen: Generator.Generator*
Pool: memhub.MemBuddy* # 内存池
Module: llvmlite.LLVMModule* # 当前 LLVM 模块
Builder: llvmlite.IRBuilder* # 当前 IRBuilder
# 符号表/函数表
SymTab: GenTypes.SymbolTable | t.CPtr
Funcs: GenTypes.FuncEntry | t.CPtr
SymTab: rawptr[GenTypes.SymbolTable]
Funcs: GenTypes.FuncEntry*
FuncCount: int
# 导入/模块信息
ImportedModules: str
FromImports: str
ModuleSha1: str
# 当前翻译上下文
CurFunc: llvmlite.Function | t.CPtr
CurBuilder: llvmlite.IRBuilder | t.CPtr
CurFunc: llvmlite.Function*
CurBuilder: llvmlite.IRBuilder*
CurClassName: str
def InitBaseGen(self, pool: memhub.MemBuddy | t.CPtr) -> int:
def InitBaseGen(self, pool: memhub.MemBuddy*) -> int:
"""初始化生成器状态(默认值;子类构造时调用)"""
if self is None:
return 1
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+296
View File
@@ -0,0 +1,296 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenApi as GenApi
from . import CallGenBase
# ============================================================
# CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈
# ============================================================
novirtual class CallGenFd(CallGenBase.CallGenBase):
"""FakeDuck / requires 字段注入 / provider 栈"""
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cls_name: str,
method_name: str,
cl: ast.Call*,
trans: HT.Translator*) -> llvmlite.Value*:
"""FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None"""
if pool is None or builder is None or cl is None or trans is None: return None
if method_name is None: return None
# 仅对 str 类型触发(AnnotClassName == "str",对象是 char*);
# cls_name 为 None(表达式接收者解析失败)时也尝试
if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None
fd_at: ast.Attribute* = ast.Attribute*(cl.func)
if fd_at is None or fd_at.value is None or fd_at.attr is None: return None
if string.strcmp(fd_at.attr, method_name) != 0: return None
# 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等)
fd_func: llvmlite.Function* = None
fd_cls: str = None
fd_key: str = pool.alloc(160)
if fd_key is not None:
for fd_ci in range(2):
fd_cn: str = "_Type"
if fd_ci == 1:
fd_cn = "_str"
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
fd_func = self.find_func_in_module(mod, fd_key)
if fd_func is not None:
fd_cls = fd_cn
break
if fd_func is None or fd_cls is None: return None
# 扩展类结构体必须已注册(GEP/临时影子需要类型)
fd_sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
if fd_sty is None: return None
# 扩展类字段数:有字段(_str)时 self 用影子结构体指针;
# 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876)
fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty)
fd_has_fields: int = 0
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0:
fd_has_fields = 1
# self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时)
fd_self: llvmlite.Value* = None
if fd_at.value.kind() == ast.ASTKind.Name:
fd_onm: ast.Name* = ast.Name*(fd_at.value)
if fd_onm.id is not None:
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
fd_mn: str = pool.alloc(256)
if fd_mn is not None:
viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
fd_meta: llvmlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
if fd_meta is not None and fd_has_fields != 0:
fd_self = fd_meta
if fd_self is None:
if fd_has_fields != 0:
# 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化
# 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
if fd_self is None: return None
fd_obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
if fd_obj_val is not None:
fd_dfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__data__")
if fd_dfe is not None and fd_dfe.Ty is not None:
fd_dp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
if fd_dp is not None:
llvmlite.build_store(builder, fd_obj_val, fd_dp)
fd_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
if fd_bfe is not None and fd_bfe.Ty is not None:
fd_bp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, fd_bfe.Ty, "null")
if fd_bp is not None and fd_null is not None:
llvmlite.build_store(builder, fd_null, fd_bp)
# FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)
self._inject_requires_fields(trans, pool, builder, mod, fd_self, fd_cls)
else:
# 无字段扩展类(_Type):对象值 bitcast 到扩展类指针
fd_obj_v: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
if fd_obj_v is None: return None
fd_self = llvmlite.build_bitcast(builder, fd_obj_v, llvmlite.Ptr(pool, fd_sty))
if fd_self is None: return None
# 构建参数链表: self → 其余参数
llvmlite.value_set_next(fd_self, None)
fd_head: llvmlite.Value* = fd_self
fd_tail: llvmlite.Value* = fd_self
fd_args: list[ast.AST*]* = cl.args
fd_can: int = 0
if fd_args is not None:
fd_can = len(fd_args)
for fd_ai in range(fd_can):
fd_arg: ast.AST* = fd_args.get(fd_ai)
if fd_arg is not None:
fd_av: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
# char 常量参数 → 单字符字符串(char 隐式转 str,
# 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化,
# 否则运行时 strlen/strncmp 解引用 44 崩溃)
if fd_arg.kind() == ast.ASTKind.Constant:
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
if fd_ac.const_kind == ast.CONST_CHAR:
ch_buf: str = pool.alloc(2)
if ch_buf is not None:
ch_buf[0] = char(fd_ac.int_val)
ch_buf[1] = '\0'
fd_av = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, ch_buf, trans)
if fd_av is not None:
llvmlite.value_set_next(fd_av, None)
llvmlite.value_set_next(fd_tail, fd_av)
fd_tail = fd_av
# 返回类型与调用名(来自 find_func_in_module 命中的函数定义)
fd_ret_ty: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(fd_func)
if fd_ret_ty is None:
fd_ret_ty = llvmlite.Int32(pool)
fd_fname: str = llvmlite.function_get_name(fd_func)
if fd_fname is None: return None
# 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标路径而非 list 内联 __getitem__(printf %s 崩溃)。从定义模块 text.ll 读取精确返回类型;仅当函数名确为 "{16hex}.{func}"(判定走唯一真源 GenApi.sha1_prefix_len)才提取模块 sha1
if GenApi.sha1_prefix_len(fd_fname) != 0:
fd_sha1: str = pool.alloc(17)
if fd_sha1 is not None:
string.strncpy(fd_sha1, fd_fname, 16)
fd_sha1[16] = '\0'
fd_exact: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
if fd_exact is not None:
fd_ret_ty = fd_exact
fd_total: int = 1 + fd_can
return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
# ============================================================
# _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段
#
# 对齐 TPC HandlesExprCall.py L4021-4079:
# 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用
# _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时
# bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖
# 用户已手动设置的值)。_find_provider 未注册(未 import
# _withcontent)时跳过注入,不影响非 with 场景。
# ============================================================
# ============================================================
def _inject_requires_fields(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
meta_ptr: llvmlite.Value*,
class_name: str) -> int:
"""在 with 上下文注入 requires 字段,返回注入字段数"""
if trans is None or pool is None or builder is None or mod is None:
return 0
if meta_ptr is None or class_name is None:
return 0
# _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent
# 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050
# (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with
# 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...)
# 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。
fd_fn_probe: llvmlite.Function* = self.find_func_in_module(mod, "_find_provider")
if fd_fn_probe is None: return 0
fd_fname: str = llvmlite.function_get_name(fd_fn_probe)
if fd_fname is None: return 0
cnt: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_REQUIRES, class_name)
if cnt <= 0: return 0
sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
if sty is None: return 0
i8_ty_j: llvmlite.LLVMType* = llvmlite.Int8(pool)
if i8_ty_j is None: return 0
i8_ptr_ty_j: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_j)
if i8_ptr_ty_j is None: return 0
cur_func: llvmlite.Function* = trans._cur_func
if cur_func is None: return 0
injected: int = 0
for ji in range(cnt):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
if fname is None: continue
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
if fe is None or fe.Ty is None: continue
# 参数:字段名字符串(i8*)
fd_fstr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
if fd_fstr is None: continue
llvmlite.value_set_next(fd_fstr, None)
self._ensure_method_declare(pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
fd_ptr: llvmlite.Value* = llvmlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
if fd_ptr is None: continue
# 条件注入:ptr != NULL 才写入(保留用户手动设置的值)
fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty_j, "null")
if fd_null is None: continue
fd_cmp: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, fd_ptr, fd_null)
if fd_cmp is None: continue
# 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中
# 冲突 → llc "Terminator found in the middle of a basic block")
vtc2: int = trans._label_counter
trans._label_counter = vtc2 + 1
fd_bb_buf2: str = pool.alloc(64)
if fd_bb_buf2 is None: continue
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
fd_skip_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, cur_func, fd_bb_buf2)
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
fd_inject_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, cur_func, fd_bb_buf2)
if fd_skip_bb is None or fd_inject_bb is None: continue
llvmlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
# inject 块:bitcast + GEP + store,跳回 skip
llvmlite.position_at_end(builder, fd_inject_bb)
fd_cast: llvmlite.Value* = llvmlite.build_bitcast(builder, fd_ptr, fe.Ty)
if fd_cast is not None:
fd_fp: llvmlite.Value* = llvmlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
if fd_fp is not None: llvmlite.build_store(builder, fd_cast, fd_fp)
llvmlite.build_br(builder, fd_skip_bb)
# skip 块:恢复 CurBlock,继续后续代码生成
llvmlite.position_at_end(builder, fd_skip_bb)
injected += 1
return injected
# ============================================================
# _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文
# provider push/pop(__provides__ 元数据,fd1 注册)
#
# 对齐 TPC HandlesWith.py L138-174:
# with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8);
# 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责
# 维护 TLS provider 栈,供 _inject_requires_fields 查找。
# ============================================================
# ============================================================
def _emit_provider_push(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cls_name: str,
ctx_val: llvmlite.Value*) -> int:
"""生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数"""
if trans is None or pool is None or builder is None or mod is None: return 0
if cls_name is None or ctx_val is None: return 0
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
if fd_pc <= 0: return 0
# _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫:
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
fd_pfn_probe: llvmlite.Function* = self.find_func_in_module(mod, "_push_provider")
if fd_pfn_probe is None: return 0
fd_pfn: str = llvmlite.function_get_name(fd_pfn_probe)
if fd_pfn is None: return 0
i8_ty_p: llvmlite.LLVMType* = llvmlite.Int8(pool)
if i8_ty_p is None: return 0
i8_ptr_ty_p: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_p)
if i8_ptr_ty_p is None: return 0
ret_i32_p: llvmlite.LLVMType* = llvmlite.Int32(pool)
if ret_i32_p is None: return 0
# ctx 值 bitcast 到 i8*(provider 栈存 void*)
fd_ctx_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
if fd_ctx_i8 is None: return 0
for pi in range(fd_pc):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
if fname is None: continue
fd_fstr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
if fd_fstr is None: continue
llvmlite.value_set_next(fd_fstr, fd_ctx_i8)
llvmlite.value_set_next(fd_ctx_i8, None)
self._ensure_method_declare(pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
llvmlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
return fd_pc
def _emit_provider_pop(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cls_name: str) -> int:
"""生成 _pop_provider() 调用(每提供字段一次),返回字段数"""
if trans is None or pool is None or builder is None or mod is None: return 0
if cls_name is None: return 0
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
if fd_pc <= 0: return 0
# _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫:
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
fd_pon_probe: llvmlite.Function* = self.find_func_in_module(mod, "_pop_provider")
if fd_pon_probe is None: return 0
fd_pon: str = llvmlite.function_get_name(fd_pon_probe)
if fd_pon is None: return 0
ret_i32_q: llvmlite.LLVMType* = llvmlite.Int32(pool)
if ret_i32_q is None: return 0
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
for _pi in range(fd_pc):
llvmlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
return fd_pc
File diff suppressed because it is too large. Load diff
+783
View File
@@ -0,0 +1,783 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdlib
import lib.core.Gen.Translator as HT
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.core.Gen.GenApi as GenApi
from . import CallGenBase
# ============================================================
# CallGenLib - CallGenMixin 拆分(阶段 C):C 库声明 / 返回类型推断 /
# ============================================================
novirtual class CallGenLib(CallGenBase.CallGenBase):
"""C 库声明 / 返回类型推断 / kwargs 调用辅助(继承 Base)"""
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
name: str) -> llvmlite.Function*:
"""为已知的 C 库函数创建 declare 声明"""
if pool is None or mod is None or name is None: return None
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
ret_ty: llvmlite.LLVMType* = None
params: int = 0 # 参数数量
if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0:
ret_ty = i64_ty
params = 1
elif string.strcmp(name, "memset") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "memcpy") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "memmove") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "memcmp") == 0:
ret_ty = i32_ty
params = 3
elif string.strcmp(name, "strcmp") == 0:
ret_ty = i32_ty
params = 2
elif string.strcmp(name, "strncmp") == 0:
ret_ty = i32_ty
params = 3
elif string.strcmp(name, "strcpy") == 0:
ret_ty = i8_ptr_ty
params = 2
elif string.strcmp(name, "strncpy") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "strcat") == 0:
ret_ty = i8_ptr_ty
params = 2
elif string.strcmp(name, "puts") == 0:
ret_ty = i32_ty
params = 1
elif string.strcmp(name, "atoi") == 0:
ret_ty = i32_ty
params = 1
elif string.strcmp(name, "atol") == 0:
ret_ty = i64_ty
params = 1
elif string.strcmp(name, "abs") == 0:
ret_ty = i32_ty
params = 1
elif string.strcmp(name, "labs") == 0:
ret_ty = i64_ty
params = 1
else:
return None
# 创建 declare
fn: llvmlite.Function* = llvmlite.create_declare(pool, mod, name, ret_ty)
if fn is None:
return None
# 添加参数
if params == 1:
if string.strcmp(name, "abs") == 0:
llvmlite.add_param(pool, fn, i32_ty, "n")
elif string.strcmp(name, "labs") == 0:
llvmlite.add_param(pool, fn, i64_ty, "n")
else:
llvmlite.add_param(pool, fn, i8_ptr_ty, "s")
elif params == 2:
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
llvmlite.add_param(pool, fn, i8_ptr_ty, "src")
elif params == 3:
if string.strcmp(name, "memset") == 0:
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
llvmlite.add_param(pool, fn, i32_ty, "c")
llvmlite.add_param(pool, fn, i64_ty, "n")
else:
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
llvmlite.add_param(pool, fn, i8_ptr_ty, "src")
llvmlite.add_param(pool, fn, i64_ty, "n")
return fn
def _promote_clib_call_args(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
call_name: str,
head: llvmlite.Value*,
count: int) -> llvmlite.Value*:
"""裸名 C 库函数实参按标准签名提升(B3)。
跨模块裸名调用(string.memset → @memset)时 _ensure_method_declare 用
实参类型建 declare:第三参字面量 32=i32 把定义 sizet(i64) 固化窄
→ declare/call @memset(i8*,i32,i32) vs 定义 @memset(i8*,i32,i64),
ABI 宽度不一致(VKernel process.vp B3)。调用点先把第三参窄整数
实参提升到 i64,declare 与 call 均按 i64 生成 → 与定义一致。
未命中白名单原样返回 head。
"""
if pool is None or builder is None or call_name is None or head is None:
return head
# 仅裸名(无 '.' 分隔 → 无 sha1 前缀);带前缀走 vpi 定义侧解析
if string.strchr(call_name, 46) is not None:
return head
# 白名单:size_t(i64) 长度参数所在下标(与 _ensure_c_lib_declare 一致;vsnprintf 第二参即 sizet 长度)
size_idx: int = -1
if string.strcmp(call_name, "memset") == 0: size_idx = 2
elif string.strcmp(call_name, "memcpy") == 0: size_idx = 2
elif string.strcmp(call_name, "memmove") == 0: size_idx = 2
elif string.strcmp(call_name, "memcmp") == 0: size_idx = 2
elif string.strcmp(call_name, "strncmp") == 0: size_idx = 2
elif string.strcmp(call_name, "strncpy") == 0: size_idx = 2
elif string.strcmp(call_name, "strncat") == 0: size_idx = 2
elif string.strcmp(call_name, "vsnprintf") == 0: size_idx = 1
if size_idx < 0 or count <= size_idx:
return head
# 定位长度参数(index size_idx)
cur: llvmlite.Value* = head
idx: int = 0
while cur is not None and idx < size_idx:
cur = cur.Next
idx += 1
if cur is None or cur.Ty is None:
return head
i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool)
promoted: llvmlite.Value* = None
match cur.Ty:
case llvmlite.LLVMType.Int(bits):
if bits >= 64:
return head
if cur.IsConst != 0:
# 常量:直接 i64 常量(位模式保留,无符号长度语义)
promoted = llvmlite.const_int(pool, 64, cur.IntVal)
else:
# SSA 值:zext 到 i64(size_t 无符号长度)
promoted = llvmlite.build_zext(builder, cur, i64_ty)
case _:
return head
if promoted is None:
return head
# 重建实参链表:index size_idx 替换为提升后的值
new_head: llvmlite.Value* = None
new_tail: llvmlite.Value* = None
cur2: llvmlite.Value* = head
i2: int = 0
while cur2 is not None:
node_v: llvmlite.Value* = cur2
if i2 == size_idx:
node_v = promoted
if new_head is None:
new_head = node_v
else:
new_tail.Next = node_v
new_tail = node_v
cur2 = cur2.Next
i2 += 1
return new_head
# _coerce_args_to_def_params:按 vpi 定义侧形参注解把跨模块调用实参 coerce 到目标类型并重建实参链,使 call/declare/define 宽度一致;支持 {sha1}.{func} 与 {sha1}.{cls}.{method} 两形态(段下标与实参下标一一对应),裸名 C 库走 _promote_clib_call_args/_ensure_c_lib_declare,未命中原样保留
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
call_name: str,
head: llvmlite.Value*,
count: int,
cargs: list[ast.AST*]* = None,
self_off: int = 0,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链
cargs/self_off/trans:实参 AST 列表与其在实参链中的偏移(模块级函数 0、
方法 1——链首元素是 self,而 vpi 段 0 即 self 占位段)。仅用于非常量
(SSA)整型扩宽时的**源符号性**判定(与 ExprGenBase._zext_or_sext_val
同源);缺省时该判定退化为有符号(sext),与 coerce_to_type 默认一致。
"""
if pool is None or builder is None or call_name is None or head is None:
return head
if self._is_sha1_mangled_call(call_name) == 0:
return head
# 拆 {sha1}.{rest}:rest 含 '.' 则为 {cls}.{method},否则为 {func}
rest: str = call_name + 17
cls_nm: str = None
meth_nm: str = rest
# 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断
dot_r: str = string.strrchr(rest, 46)
if dot_r is not None:
cls_len: sizet = uint64(dot_r) - uint64(rest)
if cls_len <= 0:
return head
cls_buf: bytes = stdlib.malloc(cls_len + 1)
if cls_buf is None:
return head
string.strncpy(cls_buf, rest, cls_len)
cls_buf[cls_len] = '\0'
cls_nm = cls_buf
meth_nm = dot_r + 1
if meth_nm is None or meth_nm[0] == '\0':
if cls_nm is not None:
stdlib.free(cls_nm)
return head
sha1_buf: bytes = stdlib.malloc(17)
if sha1_buf is None:
if cls_nm is not None:
stdlib.free(cls_nm)
return head
string.strncpy(sha1_buf, call_name, 16)
sha1_buf[16] = '\0'
# 直接 ensure 加载 + 直查缓存(不用 _vpi_method_param_annot:其 cls_name is None 时恒返回 None,模块级函数永不生效)
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir)
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
# 泛型特化类({sha}.list[str].__new__):定义侧注解写在模板类上,精确键必遗漏 ⇒ tpl_src=1 时退查模板注解并替换类型形参(VpiReader._resolve_vpi_param_annot_seg_template)
tpl_src: int = 0
if prm_annots is None:
if cls_nm is not None and string.strchr(cls_nm, 91) is not None:
tpl_src = 1
else:
stdlib.free(sha1_buf)
if cls_nm is not None:
stdlib.free(cls_nm)
return head
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
new_head: llvmlite.Value* = None
new_tail: llvmlite.Value* = None
cur: llvmlite.Value* = head
idx: int = 0
while cur is not None and idx < count:
node_v: llvmlite.Value* = cur
tgt_ty: llvmlite.LLVMType* = None
if tpl_src != 0:
tgt_ty = VpiReader._resolve_vpi_param_annot_seg_template(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty)
else:
tgt_ty = VpiReader._resolve_vpi_param_annot_seg(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty)
if tgt_ty is not None and cur.Ty is not None:
# 实参 AST(源符号性判定用):链下标 idx 减 self 偏移即 AST 下标
arg_idx: int = idx - self_off
arg_node: ast.AST* = None
if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs):
arg_node = cargs.get(arg_idx)
node_v = self._coerce_arg_value_to_ty(pool, builder, cur, tgt_ty, arg_node, trans, tpl_src)
if new_head is None:
new_head = node_v
else:
new_tail.Next = node_v
new_tail = node_v
cur = cur.Next
idx += 1
stdlib.free(sha1_buf)
if cls_nm is not None:
stdlib.free(cls_nm)
return new_head
# _coerce_arg_value_to_ty:单个实参 Value 转换到目标形参类型——整型同宽原样、窄化 trunc/按宽度重建常量、扩宽按源符号性 zext/sext;同层指针类型不一致则 bitcast 到目标类型;其余原样返回(不可转换亦不改型)
def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
val: llvmlite.Value*,
tgt_ty: llvmlite.LLVMType*,
node: ast.AST* = None,
trans: HT.Translator* = None,
tpl_src: int = 0) -> llvmlite.Value*:
"""把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回
node/trans:实参 AST 与其翻译器(仅 SSA 整型扩宽的源符号性判定用,
委托 ExprGenBase._zext_or_sext_val;缺省退化为有符号 sext)。
tpl_src:目标类型来自**泛型模板类**注解(特化名精确键遗漏 → 退查模板 +
类型形参替换,见 VpiReader._resolve_vpi_param_annot_seg_template)⇒
指针口径亦可对齐定义侧(同为具体结构体指针但基类型不同时 bitcast)。
"""
if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None:
return val
match val.Ty:
case llvmlite.LLVMType.Int(bits_a):
match tgt_ty:
case llvmlite.LLVMType.Int(bits_b):
if bits_b == bits_a:
return val
if bits_b < bits_a:
if val.IsConst != 0:
nv_c: llvmlite.Value* = llvmlite.const_int(pool, bits_b, val.IntVal)
if nv_c is not None: return nv_c
else:
nv_t: llvmlite.Value* = llvmlite.build_trunc(builder, val, tgt_ty)
if nv_t is not None: return nv_t
# 扩宽(实参窄于形参):编译期常量按十进制值重建,各自保持原类型数值,无需 AST 判符号性
if val.IsConst != 0:
nv_w: llvmlite.Value* = llvmlite.const_int(pool, bits_b, val.IntVal)
if nv_w is not None:
return nv_w
return val
# 非常量(SSA)扩宽:扩宽方向由源符号性决定(i1/i8、无符号源 → zext,否则 sext),使 declare/call/define 三者同宽
if bits_b > bits_a:
if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, node, trans) != 0:
nv_zw: llvmlite.Value* = llvmlite.build_zext(builder, val, tgt_ty)
if nv_zw is not None: return nv_zw
else:
nv_sw: llvmlite.Value* = llvmlite.build_sext(builder, val, tgt_ty)
if nv_sw is not None: return nv_sw
return val
case _:
pass
case llvmlite.LLVMType.Ptr(_):
match tgt_ty:
case llvmlite.LLVMType.Ptr(_):
# 同层指针:实参类型与定义侧形参类型不一致就 bitcast 对齐(一致则不比,
# 避免生成非法 `bitcast T* to T*`)。跨模块调用的 call/declare 都以
# 定义侧为准(declare 见 CallGenBase._ensure_method_declare),故这里
# 必须对齐:首个调用点由它建立 declare、不走 coerce_to_type,原样传具体
# 指针就会与 declare 的类型不一致(llc 报 argument type mismatch)。
# 原判据只覆盖"一侧为 i8*"与"模板注解来源",漏了两侧同为具体结构体指针
# 但基类型不同的情形(实参 MemBuddy* vs 定义侧形参 MemManager*)。
if self.Gen.AssignGen._stg_ty_equal(pool, val.Ty, tgt_ty) == 0:
nv_p: llvmlite.Value* = llvmlite.build_bitcast(builder, val, tgt_ty)
if nv_p is not None: return nv_p
case _:
pass
case _:
# 聚合值 → 指针:rvalue 结构体/数组落栈取址(FuncGenDef 形参一律 lowering 为 Ptr,跨模块时上游 coerce_to_type 不参与)
match tgt_ty:
case llvmlite.LLVMType.Ptr(_):
vk_a: int = val.Ty.kind()
if vk_a == llvmlite.LLVMType.Struct or vk_a == llvmlite.LLVMType.Array:
tmp_a: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty)
if tmp_a is not None:
llvmlite.build_store(builder, val, tmp_a)
return tmp_a
case _:
pass
return val
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy*,
func_name: str,
node: ast.AST*,
fatal_if_unknown: int) -> llvmlite.LLVMType*:
"""根据函数名推断外部 includes 函数的返回类型
支持三种函数名形式:
1. 裸名: "parse", "strlen"
2. 别名: "json_parse"(from X import parse as json_parse)
3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name)
fatal_if_unknown: 白名单全部未命中时的行为。
=1(纯裸名外部函数,无法收敛)→ fatal_error fail-fast(默认 i32
会截断指针/i64,静默生成导致运行时数据损坏,不兜底原则)。
=0(Phase A 中间遍,_deps_pending 下遍重译可收敛)→ 返回默认
i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。
"""
if pool is None or func_name is None:
return llvmlite.Int32(pool)
# 提取裸函数名:去掉 SHA1 前缀(如 "240a9a4157959a9f.parse" → "parse")
# SHA1 前缀是 16 位十六进制 + '.',检查是否有 '.' 分隔
bare_name: str = func_name
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
if dot_pos is not None:
# '.' 后的部分是裸函数名
bare_name = dot_pos + 1
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i8* 的函数(指针返回值,截断会导致错误)
if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strcpy") == 0 or string.strcmp(func_name, "strcpy") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strncpy") == 0 or string.strcmp(func_name, "strncpy") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memset") == 0 or string.strcmp(func_name, "memset") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memmove") == 0 or string.strcmp(func_name, "memmove") == 0:
return i8_ptr_ty
# JSON 解析函数返回 JsonValue*(指针)
# from json.__parser import parse as json_parse → 别名 json_parse 也需覆盖
if string.strcmp(bare_name, "parse") == 0 or string.strcmp(func_name, "parse") == 0 or string.strcmp(func_name, "json_parse") == 0:
return i8_ptr_ty
# MemBuddy 方法(当类型信息丢失时可能走外部函数路径)
# 存疑
# malloc:裸名 CExport 跨模块调用(uhci/desktop 等 mm.malloc → @malloc)
# 在模块归属解析失败(mod_ret_sha1=None)或 vpi/text.ll 未命中时回退到
# 此白名单。缺失导致返回类型被推断为 i32 → call i32 @malloc 截断 64 位
# 指针 → mm.malloc 实际返回非空但调用方判 None(uhci frame list /
# desktop bg_row_buf 根因)。calloc/realloc/alloc 均已登记,malloc 遗漏。
if string.strcmp(bare_name, "malloc") == 0 or string.strcmp(func_name, "malloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "alloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "calloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "realloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "alloc_buf") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃)
if string.strcmp(func_name, "get_logger") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "fmt_buf") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏)
if string.strcmp(func_name, "get_includes_binary_dir") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# HandlesType 模块函数(返回 ast.AST|t.CPtr 联合类型,跨模块推断为 i32 导致指针截断)
if string.strcmp(bare_name, "_parse_type_string") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i64 的函数
if string.strcmp(func_name, "strlen") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "atoll") == 0:
return llvmlite.Int64(pool)
# llvmlite 函数名长度访问器返回 sizet(i64):默认 i32 与 declare 的 i64 签名不一致,须登记
if string.strcmp(bare_name, "function_get_name_len") == 0 or \
string.strcmp(func_name, "function_get_name_len") == 0:
return llvmlite.Int64(pool)
# 返回 double 的函数
if string.strcmp(func_name, "atof") == 0:
return llvmlite.Double(pool)
# 返回 i8 的函数
if string.strcmp(func_name, "samestr") == 0:
return llvmlite.Int8(pool)
# Win32 API 返回指针(VOIDPTR/HANDLE)的函数
# stubs 翻译时未注入,需推断返回 i8* 避免指针被截断为 i32 导致解引用崩溃
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "VirtualAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "HeapAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "HeapReAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "HeapCreate") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetProcessHeap") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GlobalAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GlobalFree") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GlobalLock") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LocalAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LocalFree") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetProcAddress") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetModuleHandleA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetModuleHandleW") == 0:
return i8_ptr_ty
# GetCommandLineA/W 返回 LPSTR(i8*):缺则 64 位指针被截成 i32,解引用读到无效地址
if string.strcmp(func_name, "GetCommandLineA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetCommandLineW") == 0:
return i8_ptr_ty
# gethostbyname 返回 hostent*(i8*)
if string.strcmp(func_name, "gethostbyname") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LoadLibraryA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LoadLibraryW") == 0:
return i8_ptr_ty
# Win32 Handle 返回函数(HANDLE = void* = i8*)
if string.strcmp(func_name, "GetStdHandle") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "CreateFileA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "CreateFileW") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "FindFirstFileA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "FindFirstFileW") == 0:
return i8_ptr_ty
# Win32 API 返回 i64 (SIZE_T) 的函数
if string.strcmp(func_name, "VirtualQuery") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "HeapSize") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "HeapCompact") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "GlobalSize") == 0:
return llvmlite.Int64(pool)
# llvmlite build_* 返回类型推断:多数返回 Value*,少数终止指令返回 void(stubs 未注入时防指针截断)
if string.strncmp(func_name, "build_", 6) == 0:
# 返回 void 的 build_* 函数(终止指令和无返回值指令)
if string.strcmp(func_name, "build_ret") == 0 or string.strcmp(func_name, "build_ret_void") == 0 or \
string.strcmp(func_name, "build_store") == 0 or string.strcmp(func_name, "build_br") == 0 or \
string.strcmp(func_name, "build_cond_br") == 0 or string.strcmp(func_name, "build_unreachable") == 0 or \
string.strcmp(func_name, "build_switch") == 0:
return llvmlite.Void(pool)
# 其他 build_* 函数返回 Value*(指针)
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# llvmlite 类型工厂 / 常量构造 / 访问器函数返回指针(LLVMType*/Value*):Int*/Ptr/Func/Array/Struct/Void 等、const_*/Const*、function_get_*/param_get_*
if string.strcmp(func_name, "Int1") == 0 or string.strcmp(func_name, "Int8") == 0 or string.strcmp(func_name, "Int16") == 0 or \
string.strcmp(func_name, "Int32") == 0 or string.strcmp(func_name, "Int64") == 0 or string.strcmp(func_name, "Ptr") == 0 or \
string.strcmp(func_name, "Func") == 0 or string.strcmp(func_name, "Array") == 0 or string.strcmp(func_name, "Struct") == 0 or \
string.strcmp(func_name, "Void") == 0 or string.strcmp(func_name, "Half") == 0 or string.strcmp(func_name, "Float") == 0 or \
string.strcmp(func_name, "Double") == 0 or string.strcmp(func_name, "FP128") == 0 or string.strcmp(func_name, "Label") == 0 or \
string.strcmp(func_name, "new_param_node") == 0 or string.strcmp(func_name, "new_phi_incoming") == 0 or \
string.strcmp(func_name, "const_int32") == 0 or string.strcmp(func_name, "const_int64") == 0 or \
string.strcmp(func_name, "ConstFloat") == 0 or string.strcmp(func_name, "ConstZero") == 0 or \
string.strcmp(func_name, "ConstNull") == 0 or string.strcmp(func_name, "ConstInt") == 0 or \
string.strcmp(func_name, "function_get_name") == 0 or string.strcmp(func_name, "param_get_ty") == 0 or \
string.strcmp(func_name, "value_get_ty") == 0 or string.strcmp(func_name, "value_get_next") == 0 or \
string.strcmp(func_name, "function_get_ret_ty") == 0 or string.strcmp(func_name, "function_get_params") == 0 or \
string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 默认 i32 占位:fatal_if_unknown=1 → fail-fast 报错;=0(Phase A 中间遍)保持占位,不输出 WARN
if fatal_if_unknown != 0:
self.Gen.TypeGenErr.fatal_error(node, f"外部函数返回类型未登记且无法收敛推断(默认 i32 会截断指针/i64,需提供 stub 声明或加入白名单): {func_name}", "TypeError")
return llvmlite.Int32(pool)
def _get_external_func_param_ty(self, pool: memhub.MemBuddy*,
func_name: str,
param_idx: int) -> llvmlite.LLVMType*:
"""获取外部函数第 param_idx 个参数的期望类型,None 表示用字面量原类型
对 Win32 API 函数的所有非指针参数返回 i64,匹配 declare 中
ULONG/SIZE_T 被统一翻译为 i64 的情况。
"""
if pool is None or func_name is None:
return None
i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool)
# Win32 API 函数列表:所有非指针参数在 declare 中都是 i64
# (因为 ULONG/DWORD/SIZE_T 在 LLVM stubs 翻译时都被映射为 i64)
win32_apis: list[str]* = [
"VirtualAlloc", "VirtualFree", "VirtualProtect", "VirtualQuery",
"VirtualLock", "VirtualUnlock",
"HeapCreate", "HeapDestroy", "HeapAlloc", "HeapReAlloc",
"HeapFree", "HeapSize", "HeapValidate", "HeapCompact",
"GlobalAlloc", "GlobalFree", "GlobalLock", "GlobalUnlock", "GlobalSize",
"LocalAlloc", "LocalFree",
"GetLastError", "SetLastError",
"Sleep", "SleepEx",
"GetTickCount", "GetProcessHeap"
]
# list 迭代不支持,用 range + get 替代
api_count: sizet = len(win32_apis)
for ai in range(api_count):
api_name: str = win32_apis.get(ai)
if string.strcmp(api_name, func_name) == 0:
return i64_ty
# 内存分配函数:size 参数声明为 i64(size_t),裸名回退时不提升则 call 与 define 宽度不匹配;calloc/realloc 的 size 在 idx 1
if string.strcmp(func_name, "malloc") == 0 \
or string.strcmp(func_name, "alloc") == 0 \
or string.strcmp(func_name, "alloc_buf") == 0:
if param_idx == 0:
return i64_ty
if string.strcmp(func_name, "calloc") == 0:
if param_idx == 0 or param_idx == 1:
return i64_ty
if string.strcmp(func_name, "realloc") == 0:
if param_idx == 1:
return i64_ty
return None
def _is_sha1_mangled_call(self, call_name: str) -> int:
"""判断 call_name 是否为跨模块 Viper 函数("{16位小写hex}.func" 形式)
C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0,
不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。
判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:不再接受大写 hex)。
"""
if call_name is None:
return 0
if GenApi.sha1_prefix_len(call_name) != 0:
return 1
return 0
def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
dst_val: llvmlite.Value*,
src_val: llvmlite.Value*,
num_val: llvmlite.Value*) -> llvmlite.Value*:
"""将 memcpy 调用映射到 @llvm.memcpy 内联函数
直接接收三个 Value 参数,避免通过 Next 链表遍历(共享 alloca 对象的
Next 字段可能被其他操作污染,导致链表断裂)。
"""
if pool is None or builder is None or mod is None:
return None
if dst_val is None or src_val is None or num_val is None:
return None
# 在函数顶部声明 void_ty,避免后续遮蔽
void_ty: llvmlite.LLVMType* = llvmlite.Void(pool)
# 声明 @llvm.memcpy(如未声明)
memcpy_func: llvmlite.Function* = self.find_func_in_module(mod, "llvm.memcpy")
if memcpy_func is None:
memcpy_func = llvmlite.create_declare(pool, mod, "llvm.memcpy", void_ty)
if memcpy_func is not None:
i8_ptr_ty_d: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_ty_d: llvmlite.LLVMType* = llvmlite.Int64(pool)
i1_ty_d: llvmlite.LLVMType* = llvmlite.Int1(pool)
llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "dst")
llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "src")
llvmlite.add_param(pool, memcpy_func, i64_ty_d, "len")
llvmlite.add_param(pool, memcpy_func, i1_ty_d, "isvolatile")
# 类型转换: dst, src → i8*; num → i64
# 注意: 使用 value_get_ty 访问器获取 Ty,避免本地 Value 类型字段索引偏移
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_ty: llvmlite.LLVMType* = llvmlite.Int64(pool)
# dst → i8*
dst_coerced: llvmlite.Value* = dst_val
dst_ty: llvmlite.LLVMType* = llvmlite.value_get_ty(dst_val)
if self.Gen.ExprGenBase.is_ptr_type(dst_ty) != 0:
dst_coerced = llvmlite.build_bitcast(builder, dst_val, i8_ptr_ty)
else:
dst_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dst_ty)
if dst_bits != 0:
if dst_bits < 64:
dst_coerced = llvmlite.build_zext(builder, dst_val, i64_ty)
dst_coerced = llvmlite.build_inttoptr(builder, dst_coerced, i8_ptr_ty)
# src → i8*
src_coerced: llvmlite.Value* = src_val
src_ty: llvmlite.LLVMType* = llvmlite.value_get_ty(src_val)
if self.Gen.ExprGenBase.is_ptr_type(src_ty) != 0:
src_coerced = llvmlite.build_bitcast(builder, src_val, i8_ptr_ty)
else:
src_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(src_ty)
if src_bits != 0:
if src_bits < 64:
src_coerced = llvmlite.build_zext(builder, src_val, i64_ty)
src_coerced = llvmlite.build_inttoptr(builder, src_coerced, i8_ptr_ty)
# num → i64
num_coerced: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, num_val, i64_ty)
if dst_coerced is None or src_coerced is None or num_coerced is None:
return None
# 添加 i1 false 参数(isvolatile)— llvm.memcpy 需要 4 个参数
false_val: llvmlite.Value* = llvmlite.ConstInt(pool, llvmlite.Int1(pool), 0, "false")
# 使用 includes 中的访问器函数构建参数链表,避免本地 Value 类型字段索引偏移
# (f26cd4c78b4d554c.Value 缺少 GSListNode 的 Next 字段,直接访问 .Next 会修改 .Ty)
llvmlite.value_set_next(dst_coerced, src_coerced)
llvmlite.value_set_next(src_coerced, num_coerced)
if false_val is not None:
llvmlite.value_set_next(num_coerced, false_val)
llvmlite.value_set_next(false_val, None)
else:
llvmlite.value_set_next(num_coerced, None)
# 使用 build_call 发射 call 指令(内部用 includes Value 类型访问字段,索引正确)
# _ll_name_needs_quote 已对 llvm. 前缀豁免引号
result: llvmlite.Value* = llvmlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0)
# 返回 dst(匹配 C memcpy 语义: 返回 dest 指针)
return dst_coerced
# _translate_call_with_kwargs:带关键字参数的函数调用,按 found_func.Params 顺序构建实参链表(位置参数优先,否则按参数名匹配关键字)
# ============================================================
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
func_name: str,
found_func: llvmlite.Function*,
cargs: list[ast.AST*]*,
can: sizet,
keywords: list[ast.AST*]*,
kw_count: sizet,
trans: HT.Translator*) -> llvmlite.Value*:
"""带关键字参数的函数调用,按参数顺序构建调用链表
使用 llvmlite 访问器函数绕过 stub 类型限制:
found_func.Params/RetTy/Name 和 cur_param.Name/Ty/Next 在 stub
类型中字段不足,直接访问会被 TransPyC 静默跳过。
"""
head: llvmlite.Value* = None
tail: llvmlite.Value* = None
actual_count: sizet = 0
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
mc_dst1: llvmlite.Value* = None
mc_src1: llvmlite.Value* = None
mc_num1: llvmlite.Value* = None
# 遍历函数参数链表(使用访问器获取 Head)
param_idx: sizet = 0
cur_param: llvmlite.Param* = llvmlite.function_get_param_head(found_func)
while cur_param is not None:
arg_val: llvmlite.Value* = None
param_ty: llvmlite.LLVMType* = llvmlite.param_get_ty(cur_param)
if param_idx < can:
# 优先使用位置参数
arg: ast.AST* = cargs.get(param_idx)
if arg is not None:
# 传形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant)
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans, param_ty)
else:
# 查找匹配的关键字参数
param_name: str = llvmlite.param_get_name(cur_param)
if param_name is not None:
for ki in range(kw_count):
kw: rawptr[ast.Keyword] = rawptr[ast.Keyword](keywords.get(ki))
if kw is None or kw.arg is None or kw.value is None: continue
if self.Gen.CallGenCall._kwarg_name_matches(kw.arg, param_name) == 1:
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, kw.value, None, 0, trans, param_ty)
break
if arg_val is not None:
# 类型转换(按参数类型,使用访问器)
if param_ty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, param_ty)
arg_val.Next = None
if head is None:
head = arg_val
else:
tail.Next = arg_val
tail = arg_val
# memcpy 专用: 保存前三个参数的独立引用
if actual_count == 0:
mc_dst1 = arg_val
elif actual_count == 1:
mc_src1 = arg_val
elif actual_count == 2:
mc_num1 = arg_val
actual_count += 1
param_idx += 1
cur_param = llvmlite.param_get_next(cur_param)
# declare 与 define 统一用 found_func.Name:普通函数带 SHA1 前缀,t.CExtern/t.State/t.CExport 用裸名(用裸名会造成 call 与 define 符号名不一致)
call_name: str = func_name
found_name: str = llvmlite.function_get_name(found_func)
if found_name is not None:
call_name = found_name
# 返回类型(使用访问器)
call_ret_ty: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(found_func)
if call_ret_ty is None:
# found_func 已有 declare 却读不到返回类型(访问器异常,罕见防御):
# 白名单未命中也不 fail-fast,保持默认 i32(有声明即有类型来源)。
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name, None, 0)
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3:
return self._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1)
return llvmlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)
File diff suppressed because it is too large. Load diff
@@ -1,4 +1,5 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import ast
@@ -6,39 +7,18 @@ import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Translator as HT
import lib.core.Gen.CallGenBase as CallGenBase
from . import CallGenBase
# ============================================================
# CallGenProp - CallGenMixin 拆分(阶段 B):属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class CallGenProp(CallGenBase.CallGenBase):
novirtual class CallGenProp(CallGenBase.CallGenBase):
"""属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)"""
# ============================================================
# _ensure_method_declare - 为跨模块方法调用创建 declare 声明
#
# 当 found_func 为 None(stub 未注入或跨模块调用)时,
# 在当前模块中创建函数声明,避免 llc 报 undefined value。
# ============================================================
# ============================================================
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
#
@@ -57,26 +37,24 @@ class CallGenProp(CallGenBase.CallGenBase):
#
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
# ============================================================
def _translate_property_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
def _translate_property_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
class_name: str,
method_name: str,
call_head: llvmlite.Value | t.CPtr,
call_head: llvmlite.Value*,
call_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""翻译属性/静态方法调用,返回 call 结果 Value"""
if pool is None or builder is None or class_name is None or method_name is None:
return None
if pool is None or builder is None or class_name is None or method_name is None: return None
# 构造查找名: ClassName.method_name
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return None
if lookup_name is None: return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
# 查找函数(精确/后缀匹配)
found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, lookup_name)
found_func: llvmlite.Function* = self.find_func_in_module(mod, lookup_name)
# 确定调用名
call_name: str = lookup_name
@@ -88,50 +66,38 @@ class CallGenProp(CallGenBase.CallGenBase):
# 未找到:用类所属模块的 SHA1 构造调用名
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
if cls_sha1_pc is not None:
mlen_pc: t.CSizeT = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
mlen_pc: sizet = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
mangled_pc: str = pool.alloc(mlen_pc)
if mangled_pc is not None:
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
call_name = mangled_pc
# 确定返回类型
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
call_ret_ty: llvmlite.LLVMType | t.CPtr = i32_ty
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
call_ret_ty: llvmlite.LLVMType* = i32_ty
if found_func is not None:
frt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func)
frt: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(found_func)
if frt is not None:
call_ret_ty = frt
else:
sp_ty1: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty(pool, class_name, method_name)
sp_ty1: llvmlite.LLVMType* = self._special_method_ret_ty(pool, class_name, method_name)
if sp_ty1 is not None:
call_ret_ty = sp_ty1
else:
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
looked_up_ty: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
pool, cls_sha1_rt, class_name, method_name)
looked_up_ty: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
if looked_up_ty is not None:
call_ret_ty = looked_up_ty
else:
# 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单)
vpi_ret_ty: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi(
pool, cls_sha1_rt, class_name, method_name)
vpi_ret_ty: llvmlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
if vpi_ret_ty is not None:
call_ret_ty = vpi_ret_ty
else:
# Phase A 依赖未就绪(cls_sha1_rt None / 定义模块 text.ll 未生成):
# 标记 pending 下遍重译(对齐 _translate_method_call L4783-4790),
# 绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。
# 保持默认 i32(build_call(Void) 返回 None 会破坏表达式翻译)。
if cls_sha1_rt is None \
or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0:
if trans is not None:
trans._deps_pending = 1
# [DEPDBG] 临时诊断:属性方法依赖未就绪即 pending 的根因(用后移除)
dbg_ms8: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?"
stdio.printf("[DEPDBG] pending(prop_ret): mod=%s cls=%s method=%s sha1=%s\n",
dbg_ms8, class_name, method_name,
cls_sha1_rt if cls_sha1_rt is not None else "none")
# 依赖未就绪:保持默认 i32(build_call(Void) 返回 None
# 会破坏表达式翻译;pending 文件本遍不写盘,下遍重译)
call_ret_ty = llvmlite.Int32(pool)
else:
call_ret_ty = self._fail_method_ret_ty(method_name)
@@ -140,18 +106,17 @@ class CallGenProp(CallGenBase.CallGenBase):
# 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong),
# 不转换则 call(i32) 与 declare(i64) 不匹配,llc 报参数类型错误。
# 目标函数优先用已找到的 found_func,否则按 mangled 调用名查模块 declare。
tgt_func_pc: llvmlite.Function | t.CPtr = found_func
tgt_func_pc: llvmlite.Function* = found_func
if tgt_func_pc is None:
tgt_func_pc = self.find_func_in_module(mod, call_name)
if tgt_func_pc is not None:
prev_pc: llvmlite.Value | t.CPtr = None
cur_arg_pc: llvmlite.Value | t.CPtr = call_head
cur_param_pc: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(tgt_func_pc)
prev_pc: llvmlite.Value* = None
cur_arg_pc: llvmlite.Value* = call_head
cur_param_pc: llvmlite.Param* = llvmlite.function_get_param_head(tgt_func_pc)
while cur_arg_pc is not None and cur_param_pc is not None:
param_ty_pc: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_pc)
param_ty_pc: llvmlite.LLVMType* = llvmlite.param_get_ty(cur_param_pc)
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
coerced_pc: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, cur_arg_pc, param_ty_pc)
coerced_pc: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
if coerced_pc is not None and coerced_pc != cur_arg_pc:
# 保持链表完整性:新 Value 继承原节点的 Next,前驱指向新节点
llvmlite.value_set_next(coerced_pc, cur_arg_pc.Next)
@@ -176,22 +141,38 @@ class CallGenProp(CallGenBase.CallGenBase):
# ============================================================
# ============================================================
def _resolve_obj_class_name(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
obj_node: ast.AST | t.CPtr) -> str:
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*,
trans: HT.Translator*,
obj_node: ast.AST*) -> str:
"""解析对象表达式的类名(短名),失败返回 None"""
if pool is None or trans is None or obj_node is None:
return None
if obj_node.kind() == ast.ASTKind.Name:
obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(obj_node)
obj_nm: ast.Name* = ast.Name*(obj_node)
if obj_nm.id is not None:
var_entry: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, obj_nm.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
annot_val: str = var_entry.AnnotClassName
dot_ptr: str = string.strrchr(annot_val, '.')
if dot_ptr is not None:
return dot_ptr + 1
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, obj_nm.id)
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
if annot_val is not None:
# [D32] 取「模块限定名」末段时只按**括号外**的 '.' 切分。
# 此前用 string.strrchr 在整个串上找最后一个 '.':泛型特化名
# "list[e94ef9e139461915.AST|t.CPtr]" 的 '.' 落在 '[' 内 ⇒
# 返回 "AST|t.CPtr]" 这种垃圾短名 ⇒ 后续按它查表必落空,
# 进而可能被兜底成任意特化(.get() 误分派)。
n_av: sizet = string.strlen(annot_val)
last_dot_av: sizet = n_av
depth_av: int = 0
ai_av: sizet = 0
while ai_av < n_av:
ch_av: int = annot_val[ai_av]
if ch_av == '[':
depth_av += 1
elif ch_av == ']':
depth_av -= 1
elif ch_av == '.' and depth_av == 0:
last_dot_av = ai_av
ai_av += 1
if last_dot_av < n_av:
return annot_val + last_dot_av + 1
return annot_val
return None
# ============================================================
@@ -202,13 +183,13 @@ class CallGenProp(CallGenBase.CallGenBase):
# ============================================================
# ============================================================
def _try_property_getter_attr(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
at_node: ast.Attribute | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr,
trans: HT.Translator | t.CPtr,
fallback_class_name: str = None) -> llvmlite.Value | t.CPtr:
def _try_property_getter_attr(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
at_node: ast.Attribute*,
obj_ptr: llvmlite.Value*,
trans: HT.Translator*,
fallback_class_name: str = None) -> llvmlite.Value*:
"""尝试 property getter 属性访问,返回调用结果 Value;非 getter 返回 None
fallback_class_name: 对象表达式非 Name 时从 LLVM 类型解析出的类名兜底。
@@ -225,8 +206,7 @@ class CallGenProp(CallGenBase.CallGenBase):
return None
# 单参数调用: obj → getter
llvmlite.value_set_next(obj_ptr, None)
return self._translate_property_call(
pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
# ============================================================
# _try_property_setter — 尝试将 obj.attr = v 翻译为 property setter 调用
#
@@ -235,36 +215,29 @@ class CallGenProp(CallGenBase.CallGenBase):
# ============================================================
# ============================================================
def _try_property_setter(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
target_attr: ast.Attribute | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
def _try_property_setter(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
target_attr: ast.Attribute*,
rhs_val: llvmlite.Value*,
trans: HT.Translator*) -> int:
"""尝试 property setter 赋值,返回 1=已处理(生成调用)"""
if pool is None or builder is None or mod is None or trans is None:
return 0
if target_attr is None or target_attr.attr is None or rhs_val is None:
return 0
if pool is None or builder is None or mod is None or trans is None: return 0
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
if class_name is None:
return 0
if class_name is None: return 0
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
if meta & GenTypes.META_PROP_SETTER == 0:
return 0
if meta & GenTypes.META_PROP_SETTER == 0: return 0
# 翻译对象值(self 参数)
obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None:
return 0
obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None: return 0
# 构建参数链表: obj → rhs
llvmlite.value_set_next(obj_val, None)
llvmlite.value_set_next(rhs_val, None)
llvmlite.value_set_next(obj_val, rhs_val)
# setter 函数名带 $set 后缀
setter_name_buf: str = pool.alloc(128)
if setter_name_buf is None:
return 0
if setter_name_buf is None: return 0
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
self._translate_property_call(pool, builder, mod, class_name,
setter_name_buf, obj_val, 2, trans)
@@ -277,32 +250,25 @@ class CallGenProp(CallGenBase.CallGenBase):
# ============================================================
# ============================================================
def _try_property_deleter(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
target_attr: ast.Attribute | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
def _try_property_deleter(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
target_attr: ast.Attribute*,
trans: HT.Translator*) -> int:
"""尝试 property deleter 删除,返回 1=已处理(生成调用)"""
if pool is None or builder is None or mod is None or trans is None:
return 0
if target_attr is None or target_attr.attr is None:
return 0
if pool is None or builder is None or mod is None or trans is None: return 0
if target_attr is None or target_attr.attr is None: return 0
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
if class_name is None:
return 0
if class_name is None: return 0
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
if meta & GenTypes.META_PROP_DELETER == 0:
return 0
if meta & GenTypes.META_PROP_DELETER == 0: return 0
# 翻译对象值(self 参数)
obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None:
return 0
obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None: return 0
llvmlite.value_set_next(obj_val, None)
# deleter 函数名带 $del 后缀
del_name_buf: str = pool.alloc(128)
if del_name_buf is None:
return 0
if del_name_buf is None: return 0
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
self._translate_property_call(pool, builder, mod, class_name,
del_name_buf, obj_val, 1, trans)
@@ -315,58 +281,48 @@ class CallGenProp(CallGenBase.CallGenBase):
# ============================================================
# ============================================================
def _translate_static_class_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
def _translate_static_class_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
class_name: str,
method_name: str,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
cl: ast.Call*,
trans: HT.Translator*) -> llvmlite.Value*:
"""翻译类名静态/类方法调用,返回 call 结果 Value;非静态/类方法返回 None"""
if pool is None or builder is None or mod is None or trans is None:
return None
if class_name is None or method_name is None or cl is None:
return None
if pool is None or builder is None or mod is None or trans is None: return None
if class_name is None or method_name is None or cl is None: return None
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
is_static: int = 0
is_class: int = 0
if meta & GenTypes.META_STATIC != 0:
is_static = 1
if meta & GenTypes.META_CLASS != 0:
is_class = 1
if is_static == 0 and is_class == 0:
return None
if meta & GenTypes.META_STATIC != 0: is_static = 1
if meta & GenTypes.META_CLASS != 0: is_class = 1
if is_static == 0 and is_class == 0: return None
# 构建参数链表
head: llvmlite.Value | t.CPtr = None
head: llvmlite.Value* = None
total_count: int = 0
# @classmethod: 插入栈上分配的类实例指针作为 cls 参数
if is_class != 0:
struct_ty_cc: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name)
if struct_ty_cc is None:
return None
cls_ptr: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty_cc)
if cls_ptr is None:
return None
struct_ty_cc: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
if struct_ty_cc is None: return None
cls_ptr: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
if cls_ptr is None: return None
llvmlite.value_set_next(cls_ptr, None)
head = cls_ptr
total_count = 1
# 翻译调用参数
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
can: t.CSizeT = 0
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
if cargs is not None:
can = len(cargs)
tail: llvmlite.Value | t.CPtr = head
tail: llvmlite.Value* = head
for ai in range(can):
arg_node: ast.AST | t.CPtr = cargs.get(ai)
if arg_node is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg_node, None, 0, trans)
if arg_val is None:
continue
arg_node: ast.AST* = cargs.get(ai)
if arg_node is None: continue
arg_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
if arg_val is None: continue
llvmlite.value_set_next(arg_val, None)
if head is None:
head = arg_val
@@ -381,8 +337,7 @@ class CallGenProp(CallGenBase.CallGenBase):
# 若此处 return None,调用方回退通用路径把方法名当裸函数解析,生成
# call i32 @new() → 链接 undefined reference(CPythonTest 根因)。
# @classmethod 恒有 cls_ptr(head 非 None),不受影响。
return self._translate_property_call(pool, builder, mod, class_name,
method_name, head, total_count, trans)
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans)
# ============================================================
# _is_list_builtin_method - 判断是否是 list 泛型内置容器方法
#
@@ -400,11 +355,9 @@ class CallGenProp(CallGenBase.CallGenBase):
def _is_list_builtin_method(self, class_name: str,
method_name: str) -> int:
"""判断 (class_name, method_name) 是否是 list 泛型内置方法"""
if class_name is None or method_name is None:
return 0
if class_name is None or method_name is None: return 0
# 类名必须以 "list" 开头(list / list[AST | CPtr] 特化名)
if string.strncmp(class_name, "list", 4) != 0:
return 0
if string.strncmp(class_name, "list", 4) != 0: return 0
# 方法名必须是 list 内置方法集合
if string.strcmp(method_name, "append") == 0: return 1
if string.strcmp(method_name, "get") == 0: return 1
@@ -415,4 +368,78 @@ class CallGenProp(CallGenBase.CallGenBase):
if string.strcmp(method_name, "__len__") == 0: return 1
if string.strcmp(method_name, "__getitem__") == 0: return 1
if string.strcmp(method_name, "__setitem__") == 0: return 1
return 0
return 0
# ============================================================
# _strip_module_sha1_prefix - 去跨模块特化名的 sha1 前缀
#
# 接收者实际结构体名常带 "{16位小写hex}." 前缀(如
# "e94ef9e139461915.list[ast.ASTptr]");判定基名 | 特化名前必须先剥离。
# 前缀长度判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛)。
# ============================================================
def _strip_module_sha1_prefix(self, name: str) -> str:
"""去除 "{16hex}." 前缀(无前缀则原样返回;返回原串内部子串)"""
if name is None:
return None
pfx: int = GenApi.sha1_prefix_len(name)
return name + pfx if pfx != 0 else name
# _resolve_generic_receiver_name:解析接收者实际泛型特化名(IR 真值)——接收者实际 LLVM 结构体类型名比可能退化的注解/注册名可靠,故泛型判定以此为准:去 sha1 前缀取 '[' 前基名,基名是已注册泛型模板时含类型实参 ⇒ 返回实际特化名,无类型实参 ⇒ fail-fast(D25);基名非泛型模板或实际类型不可得 ⇒ 返回 None(调用方各自回退)。与 CallGenCall._translate_method_call 的 D25 分派修复共用本函数(唯一口径)
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*,
struct_ty: llvmlite.LLVMType*,
method_name: str,
cl: ast.Call*) -> str:
"""返回实际泛型特化名(去 sha1)或 None;裸泛型基类 fail-fast"""
if struct_ty is None:
return None
ty_nm: str = llvmlite.get_struct_name(struct_ty)
if ty_nm is None or ty_nm[0] == '\0':
return None
short_nm: str = self._strip_module_sha1_prefix(ty_nm)
lb: str = string.strchr(short_nm, '[')
bare_len: sizet = 0
if lb is not None:
bare_len = sizet(uint64(lb) - uint64(short_nm))
else:
bare_len = string.strlen(short_nm)
if bare_len == 0:
return None
base_nm: str = pool.alloc(bare_len + 1)
if base_nm is None:
return None
string.strncpy(base_nm, short_nm, bare_len)
base_nm[bare_len] = '\0'
# 基名非已注册泛型模板(普通类接收者)⇒ 非泛型,交由调用方原口径处理
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None:
return None
if lb is not None:
return short_nm
# 裸泛型基类(无类型实参):接收者元素类型未知 → fail-fast(D25 口径)
self.Gen.TypeGenErr.fatal_error(
cl,
f"方法 '{method_name}' 的接收者实际结构体类型是裸泛型基类 "
f"'{short_nm}*'(丢失类型实参),元素类型未知;"
f"禁止静默按默认元素类型处理(元素按 i32 处理会截断指针),"
f"请让该变量的注解/类型带上实参(如 list[ast.AST*])",
"TypeError")
return None
# _is_list_builtin_receiver:基于接收者实际类型判定 list 内建方法,判据与 _resolve_generic_receiver_name 同源——优先采用接收者实际 LLVM 结构体类型名(去 sha1 判基名),不可得时回退注解名/注册名判定。返回 1=list 内置方法 / 0=非
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*,
recv_val: llvmlite.Value*,
fallback_name: str,
method_name: str,
cl: ast.Call*) -> int:
"""基于接收者实际类型判定 list 内建方法(实际类型不可得回退注解名)"""
sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
if sty is not None:
ty_nm: str = llvmlite.get_struct_name(sty)
if ty_nm is not None and ty_nm[0] != '\0':
# 泛型模板接收者:裸基类 fail-fast;含实参用实际特化名
act_nm: str = self._resolve_generic_receiver_name(pool, sty, method_name, cl)
if act_nm is not None:
return self._is_list_builtin_method(act_nm, method_name)
# 非泛型模板接收者:用去 sha1 后的实际短名判定(不复用退化注解名)
return self._is_list_builtin_method(self._strip_module_sha1_prefix(ty_nm), method_name)
# 实际类型不可得 → 回退注解名/注册名判定(信息缺失下的既有行为)
return self._is_list_builtin_method(fallback_name, method_name)
@@ -4,31 +4,15 @@ import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Translator as HT
import lib.core.Gen.CallGenBase as CallGenBase
from . import CallGenBase
# ============================================================
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class CallGenVa(CallGenBase.CallGenBase):
novirtual class CallGenVa(CallGenBase.CallGenBase):
"""va_arg 变长参数(start/arg/end / 变长调用提升)"""
# ============================================================
@@ -38,38 +22,37 @@ class CallGenVa(CallGenBase.CallGenBase):
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
# ============================================================
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
arg_node: ast.AST | t.CPtr,
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
arg_node: ast.AST*,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""获取 va_list 变量的 alloca 指针(不 load)
va_arg/va_start/va_end 需要的是 va_list 对象的地址(alloca 指针),
而非 va_list 的值。若 arg_node 是 Name,查符号表获取 alloca;
否则用 translate_value 翻译(调用者负责确保传入的是指针)。
"""
if arg_node is None:
return None
if arg_node is None: return None
# Name 节点:直接查符号表获取 alloca 指针(不 load)
if arg_node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arg_node)
nm: ast.Name* = ast.Name*(arg_node)
if nm.id is not None:
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is not None:
return alloca
# 回退:translate_value(可能返回 load 后的值,仅适用于已是指针的情况)
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
def _resolve_va_arg_type(self, pool: memhub.MemBuddy | t.CPtr,
type_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*,
type_node: ast.AST*,
trans: HT.Translator*) -> llvmlite.LLVMType*:
"""解析 va_arg/arg 的类型参数为 LLVMType
支持的类型表达式:
t.CInt / t.CInt32T → i32
t.CInt64T → i64
int / int32 → i32
int64 → i64
t.CDouble → double
t.CPtr / str → i8*
str / bytes → i8*
@@ -78,22 +61,21 @@ class CallGenVa(CallGenBase.CallGenBase):
if type_node is None:
return llvmlite.Int32(pool)
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
return resolved
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg 类型解析失败", "TypeError")
return llvmlite.Int32(pool)
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""va_arg(ap, type) / va_arg(ap) → %N = va_arg <ap_ty> <ap>, <type>
生成 LLVM va_arg 指令,从 va_list 读取下一个变长参数。
@@ -104,36 +86,43 @@ class CallGenVa(CallGenBase.CallGenBase):
# 第一个参数:va_list(求值为 i8* 地址值,对齐 TPC _HandleVaArgLlvm)
# 当前函数 *args:translate_value 经 vararg 名映射直接返回 va_list 缓冲区地址;
# 转发场景(sprintf → vsnprintf 的 ap 参数):translate_value load 参数得到地址值。
ap_node: ast.AST | t.CPtr = cargs.get(0)
ap_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, ap_node, None, 0, trans)
ap_node: ast.AST* = cargs.get(0)
ap_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
if ap_val is None:
return llvmlite.const_int32(pool, 0)
ap_ptr: llvmlite.Value | t.CPtr = ap_val
ap_ptr: llvmlite.Value* = ap_val
# 第二个参数:目标类型(可选,默认 i32)
arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
if can >= 2:
type_node: ast.AST | t.CPtr = cargs.get(1)
type_node: ast.AST* = cargs.get(1)
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73)
read_ty: llvmlite.LLVMType | t.CPtr = arg_ty
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73),
# 再 trunc 回目标宽度——否则返回 8 字节含高位垃圾,%d/%i 语义错误
# (viperlib vsnprintf %d 非 long 分支 `int64(va_arg(ap, int))`
# 编译成直接 va_arg i64,高 32 位未定义 → 负数当大正数 A6 根因)。
read_ty: llvmlite.LLVMType* = arg_ty
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
if abits_va != 0 and abits_va < 64:
read_ty = llvmlite.Int64(pool)
va_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, ap_ptr, read_ty)
if va_raw is None:
return llvmlite.const_int32(pool, 0)
# 8 字节槽 → 目标宽度(低 bits 位);调用方 int64(...) 会再 sext/zext
return llvmlite.build_trunc(builder, va_raw, arg_ty)
# 生成 va_arg 指令
return llvmlite.build_va_arg(builder, ap_ptr, read_ty)
def _translate_arg_sugar(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
def _translate_arg_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cl: ast.Call*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""arg(type) → %N = va_arg <va_list_ty> <trans._va_list_ptr>, <type>
使用当前函数的隐式 va_list(trans._va_list_ptr),
@@ -143,27 +132,32 @@ class CallGenVa(CallGenBase.CallGenBase):
return llvmlite.const_int32(pool, 0)
# 第一个参数:目标类型(可选,默认 i32)
arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
if can >= 1 and cargs is not None:
type_node: ast.AST | t.CPtr = cargs.get(0)
type_node: ast.AST* = cargs.get(0)
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致)
read_ty: llvmlite.LLVMType | t.CPtr = arg_ty
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致),
# 再 trunc 回目标宽度(%d/%i 高位垃圾 A6 修复,见 _translate_va_arg_sugar)
read_ty: llvmlite.LLVMType* = arg_ty
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
if abits_arg != 0 and abits_arg < 64:
read_ty = llvmlite.Int64(pool)
arg_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
if arg_raw is None:
return llvmlite.const_int32(pool, 0)
return llvmlite.build_trunc(builder, arg_raw, arg_ty)
# 生成 va_arg 指令(使用 trans._va_list_ptr 作为 va_list 地址)
return llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
def _translate_va_start_sugar(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""va_start(ap, last_arg) → call void @llvm.va_start(i8* %ap)
初始化 va_list。第二个参数 last_arg 在 LLVM intrinsic 中不使用(C 宏兼容)。
@@ -172,30 +166,29 @@ class CallGenVa(CallGenBase.CallGenBase):
return llvmlite.const_int32(pool, 0)
# 第一个参数:va_list 指针(获取 alloca,不 load)
ap_node: ast.AST | t.CPtr = cargs.get(0)
ap_ptr: llvmlite.Value | t.CPtr = self._va_arg_get_alloca_ptr(
pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
ap_node: ast.AST* = cargs.get(0)
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
if ap_ptr is None:
return llvmlite.const_int32(pool, 0)
# bitcast 到 i8*(@llvm.va_start 接受 i8*)
i8_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_vs)
ap_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
i8_ty_vs: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_vs: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_vs)
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
if ap_i8 is None:
return llvmlite.const_int32(pool, 0)
void_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
void_ty_vs: llvmlite.LLVMType* = llvmlite.Void(pool)
llvmlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
return llvmlite.const_int32(pool, 0)
def _translate_va_end_sugar(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""va_end(ap) → call void @llvm.va_end(i8* %ap)
清理 va_list。在大多数平台上是 no-op,但调用它是良好的 C ABI 习惯。
@@ -204,28 +197,27 @@ class CallGenVa(CallGenBase.CallGenBase):
return llvmlite.const_int32(pool, 0)
# 第一个参数:va_list 指针(获取 alloca,不 load)
ap_node: ast.AST | t.CPtr = cargs.get(0)
ap_ptr: llvmlite.Value | t.CPtr = self._va_arg_get_alloca_ptr(
pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
ap_node: ast.AST* = cargs.get(0)
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
if ap_ptr is None:
return llvmlite.const_int32(pool, 0)
# bitcast 到 i8*(@llvm.va_end 接受 i8*)
i8_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_ve)
ap_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
i8_ty_ve: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_ve: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_ve)
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
if ap_i8 is None:
return llvmlite.const_int32(pool, 0)
void_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
void_ty_ve: llvmlite.LLVMType* = llvmlite.Void(pool)
llvmlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
return llvmlite.const_int32(pool, 0)
def _get_func_fixed_param_count(self, func: llvmlite.Function | t.CPtr) -> int:
def _get_func_fixed_param_count(self, func: llvmlite.Function*) -> int:
"""获取函数的固定参数数量(遍历 Params 链表)"""
if func is None:
return 0
count: int = 0
param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
param: llvmlite.Param* = llvmlite.function_get_param_head(func)
while param is not None:
count += 1
param = llvmlite.param_get_next(param)
@@ -248,8 +240,8 @@ class CallGenVa(CallGenBase.CallGenBase):
return 2
return -1
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy | t.CPtr,
name: str, idx: int) -> llvmlite.LLVMType | t.CPtr:
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*,
name: str, idx: int) -> llvmlite.LLVMType*:
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
@@ -257,8 +249,8 @@ class CallGenVa(CallGenBase.CallGenBase):
"""
if pool is None or name is None or idx < 0:
return None
i8_ptr_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ptr_kv: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_kv: llvmlite.LLVMType* = llvmlite.Int64(pool)
if string.strcmp(name, "snprintf") == 0:
if idx == 0:
return i8_ptr_kv
@@ -274,15 +266,15 @@ class CallGenVa(CallGenBase.CallGenBase):
return i8_ptr_kv
return None
return None
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
call_name: str,
head: llvmlite.Value | t.CPtr,
can: t.CSizeT,
arg_nodes: list[ast.AST | t.CPtr] | t.CPtr = None,
head: llvmlite.Value*,
can: sizet,
arg_nodes: list[ast.AST*]* = None,
self_offset: int = 0,
trans: HT.Translator | t.CPtr = None) -> int:
trans: HT.Translator* = None) -> int:
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
返回: is_variadic (1=变长调用, 0=普通调用)
@@ -295,18 +287,12 @@ class CallGenVa(CallGenBase.CallGenBase):
if mod is None or call_name is None or head is None:
return 0
# 查找函数对象
func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name)
func: llvmlite.Function* = self.find_func_in_module(mod, call_name)
fixed_count: int = -1
if func is not None and func.IsVarArg == 1:
fixed_count = self._get_func_fixed_param_count(func)
else:
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):
# 1. func 为 None——跨模块 stub 未注入(CallGenMixin 注释:stubs
# 在翻译后才注入,翻译时跨模块函数 found_func 恒 None);
# 2. func 存在但 IsVarArg=0——该 declare 由 _ensure_method_declare
# 按实参类型临时创建(无 ... 标记),不能代表真实签名。
# 否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 →
# va_arg(i64) 高 4 字节读残留栈垃圾(clean 日志 0x7ff700000052 根因)。
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):func 为 None(跨模块 stub 未注入,翻译时 found_func 恒 None);func 存在但 IsVarArg=0(该 declare 由 _ensure_method_declare 按实参类型临时创建,无 ... 标记,不代表真实签名)。否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 → va_arg(i64) 高 4 字节读残留栈垃圾
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
if fixed_count < 0:
return 0
@@ -316,8 +302,8 @@ class CallGenVa(CallGenBase.CallGenBase):
return 1
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
cur: llvmlite.Value | t.CPtr = head
prev: llvmlite.Value | t.CPtr = None
cur: llvmlite.Value* = head
prev: llvmlite.Value* = None
idx: int = 0
while cur is not None and idx < fixed_count:
prev = cur
@@ -326,23 +312,23 @@ class CallGenVa(CallGenBase.CallGenBase):
# cur 指向第 fixed_count+1 个节点(第一个变长参数)
# 对变长参数进行 C ABI 提升
i32_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
double_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
i32_ty_promote: llvmlite.LLVMType* = llvmlite.Int32(pool)
double_ty_promote: llvmlite.LLVMType* = llvmlite.Double(pool)
v_idx: int = fixed_count
while cur is not None:
next_node: llvmlite.Value | t.CPtr = cur.Next
promoted: llvmlite.Value | t.CPtr = cur
next_node: llvmlite.Value* = cur.Next
promoted: llvmlite.Value* = cur
# C ABI integer promotion: i8/i16 → i32
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
if arg_bits != 0 and arg_bits < 32:
# 无符号整型(t.CUInt8T/CUInt16T 等)→ zext;有符号/未知 → sext。
# 无符号整型(uint8/CUInt16T 等)→ zext;有符号/未知 → sext。
# 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得
# 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。
is_u_va: int = 0
if arg_nodes is not None and trans is not None:
ast_idx_va: int = v_idx - self_offset
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
an_va: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va)
an_va: ast.AST* = arg_nodes.get(ast_idx_va)
if an_va is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0:
is_u_va = 1
@@ -375,15 +361,15 @@ class CallGenVa(CallGenBase.CallGenBase):
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
# L798-811 的 found_func.IsVarArg 提升)。
i64_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i64_ty_promote: llvmlite.LLVMType* = llvmlite.Int64(pool)
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
if pb_va != 0 and pb_va < 64:
promoted_va2: llvmlite.Value | t.CPtr = None
promoted_va2: llvmlite.Value* = None
is_u_va2: int = 0
if arg_nodes is not None and trans is not None:
ast_idx_va2: int = v_idx - self_offset
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
an_va2: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va2)
an_va2: ast.AST* = arg_nodes.get(ast_idx_va2)
if an_va2 is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0:
is_u_va2 = 1
+177
View File
@@ -0,0 +1,177 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenTypes as GenTypes
from . import CallGenBase
# ============================================================
# CallGenVt - CallGenMixin 拆分(阶段 B):vtable 槽位写入
# ============================================================
novirtual class CallGenVt(CallGenBase.CallGenBase):
"""vtable 槽位写入"""
# _write_vtable_slot / _write_vtable_attr:把模块级函数指针写入对象 vtable 的方法槽位(devfs 驱动注册模式,对齐 TPC HandlesAssign.py L905-949 写时复制)——vtable 方法不是数据字段,get_attribute_ptr 查不到;对象 vtable 仍指向类默认 vtable 时先复制一份(internal 全局 + llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store i8*。多次写入互不覆盖
# ============================================================
def _write_vtable_slot(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
obj_ptr: llvmlite.Value*,
class_name: str,
method_index: int,
func_val: llvmlite.Value*,
trans: HT.Translator*) -> int:
"""生成 vtable 槽位写入(写时复制),成功返回 1"""
if pool is None or builder is None or mod is None or trans is None: return 0
if obj_ptr is None or class_name is None or func_val is None: return 0
if method_index < 0 or trans._cur_func is None: return 0
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
if i8_ty is None: return 0
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None: return 0
i8_ptr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty)
if i8_ptr_ptr_ty is None: return 0
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
if method_count <= 0: return 0
# 构造 vtable 类型 { i8*, i8*, ... }
# vtable 类型的字段链表是 llvmlite.ParamNode(new_param_node / paramnode_set_next /
# Struct 形参均为 rawptr[ParamNode])——原先误注解为 Function*。
vt_first: llvmlite.ParamNode* = None
vt_prev: llvmlite.ParamNode* = None
for vi in range(method_count):
pn: llvmlite.ParamNode* = llvmlite.new_param_node(pool, i8_ptr_ty)
if pn is None: continue
if vt_first is None:
vt_first = pn
if vt_prev is not None:
llvmlite.paramnode_set_next(vt_prev, pn)
vt_prev = pn
vtable_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, vt_first, method_count, None)
if vtable_ty is None:
return 0
# 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段)
vtable_slot: llvmlite.Value* = llvmlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
if vtable_slot is None: return 0
vtable_ptr: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
if vtable_ptr is None: return 0
# 类默认 vtable 地址(比较用)
class_vt: llvmlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
if class_vt is None: return 0
cmp_vt: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, vtable_ptr, class_vt)
if cmp_vt is None: return 0
# 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如
# devfs open/close/read/write 连续赋值)同名 label 冲突)
vtc3: int = trans._label_counter
trans._label_counter = vtc3 + 1
vt_bb_buf3: str = pool.alloc(64)
if vt_bb_buf3 is None: return 0
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
vt_copy_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, trans._cur_func, vt_bb_buf3)
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
vt_skip_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, trans._cur_func, vt_bb_buf3)
if vt_copy_bb is None or vt_skip_bb is None: return 0
llvmlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
# copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本
llvmlite.position_at_end(builder, vt_copy_bb)
vtc: int = trans._label_counter
trans._label_counter = vtc + 1
vcopy_buf: str = pool.alloc(256)
if vcopy_buf is not None:
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
vcopy_gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, vcopy_buf, vtable_ty)
if vcopy_gv is not None:
llvmlite.module_add_global(mod, vcopy_gv)
vcopy_gv.Linkage = None
vcopy_gv.Initializer = "zeroinitializer"
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
# 输出时加的,引用侧不自动加)
vcopy_ref_nm: str = pool.alloc(272)
if vcopy_ref_nm is None:
vcopy_ref_nm = vcopy_buf
else:
viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
vcopy_ref: llvmlite.Value* = llvmlite.SSAValue(pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
if vcopy_ref is not None:
vcopy_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
if vcopy_i8 is not None:
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
# → llc "expected value token"(devfs memcpy 根因)
vtsz_buf: str = pool.alloc(32)
if vtsz_buf is not None:
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
vt_size: llvmlite.Value* = llvmlite.ConstInt(pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf)
if vt_size is not None:
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
llvmlite.build_store(builder, vcopy_i8, vtable_slot)
llvmlite.build_br(builder, vt_skip_bb)
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
llvmlite.position_at_end(builder, vt_skip_bb)
vt_ptr2: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
if vt_ptr2 is None: return 0
vt_typed: llvmlite.Value* = llvmlite.build_bitcast(builder, vt_ptr2, llvmlite.Ptr(pool, vtable_ty))
if vt_typed is None: return 0
vt_slotp: llvmlite.Value* = llvmlite.build_gep_struct(builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
if vt_slotp is None: return 0
fptr_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, func_val, i8_ptr_ty)
if fptr_i8 is not None:
llvmlite.build_store(builder, fptr_i8, vt_slotp)
return 1
def _write_vtable_attr(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
rhs_val: llvmlite.Value*,
trans: HT.Translator*) -> int:
"""属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)"""
if node is None or builder is None or pool is None or mod is None: return 0
if rhs_val is None or trans is None: return 0
at: ast.Attribute* = ast.Attribute*(node)
if at is None or at.value is None or at.attr is None: return 0
# 解析类名(AnnotClassName;去模块前缀)
class_nm_v: str = None
if at.value.kind() == ast.ASTKind.Name:
nm_v: ast.Name* = ast.Name*(at.value)
if nm_v.id is not None:
ve_v: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_v.id)
if ve_v is not None:
class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
elif at.value.kind() == ast.ASTKind.Attribute:
# 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenAttr,经 self.Gen 调用
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
if class_nm_v is None: return 0
fd_dot_v: str = string.strrchr(class_nm_v, '.')
if fd_dot_v is not None:
class_nm_v = fd_dot_v + 1
if self.Gen.StructGen.get_struct_type(class_nm_v) is None: return 0
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
if vm_idx_v < 0: return 0
# 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca)
obj_ptr_v: llvmlite.Value* = None
if at.value.kind() == ast.ASTKind.Name:
obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
else:
obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr_v is None or obj_ptr_v.Ty is None: return 0
# X|t.CPtr 变量(Ptr(Ptr))load 解引用取对象指针
match obj_ptr_v.Ty:
case llvmlite.LLVMType.Ptr(inner_v):
match inner_v:
case llvmlite.LLVMType.Ptr(_):
obj_ptr_v = llvmlite.build_load(builder, inner_v, obj_ptr_v)
case _:
pass
case _:
pass
if obj_ptr_v is None:
return 0
return self._write_vtable_slot(pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-404
View File
@@ -1,404 +0,0 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.CallGenBase as CallGenBase
# ============================================================
# CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class CallGenFd(CallGenBase.CallGenBase):
"""FakeDuck / requires 字段注入 / provider 栈"""
# ============================================================
# _translate_fakeduck_call - FakeDuck: str 变量方法调用 → 扩展类 _Type/_str
#
# 对齐 TPC HandlesExprCall.py L2841-2880:
# ClassName 常规解析失败且对象是 char*(i8*)时,遍历扩展类查找
# {CN}.{method} 函数。对象是 Name 变量且有影子({var}__meta__,fd2
# 创建)时 self=影子;否则(字符串字面量/匿名 str)在入口创建临时
# 影子,写 __data__=obj 值、__mbuddy__=NULL 后作 self,避免
# bitcast i8* → _str* 读取垃圾指针导致 ACCESS_VIOLATION。
# ============================================================
# ============================================================
def _translate_fakeduck_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cls_name: str,
method_name: str,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None"""
if pool is None or builder is None or cl is None or trans is None:
return None
if method_name is None:
return None
# 仅对 str 类型触发(AnnotClassName == "str",对象是 char*);
# cls_name 为 None(表达式接收者解析失败)时也尝试
if cls_name is not None and string.strcmp(cls_name, "str") != 0:
return None
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute:
return None
fd_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if fd_at is None or fd_at.value is None or fd_at.attr is None:
return None
if string.strcmp(fd_at.attr, method_name) != 0:
return None
# 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等)
fd_func: llvmlite.Function | t.CPtr = None
fd_cls: str = None
fd_key: str = pool.alloc(160)
if fd_key is not None:
for fd_ci in range(2):
fd_cn: str = "_Type"
if fd_ci == 1:
fd_cn = "_str"
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
fd_func = self.find_func_in_module(mod, fd_key)
if fd_func is not None:
fd_cls = fd_cn
break
if fd_func is None or fd_cls is None:
return None
# 扩展类结构体必须已注册(GEP/临时影子需要类型)
fd_sty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(fd_cls)
if fd_sty is None:
return None
# 扩展类字段数:有字段(_str)时 self 用影子结构体指针;
# 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876)
fd_entry_fd: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_type(fd_sty)
fd_has_fields: int = 0
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0:
fd_has_fields = 1
# self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时)
fd_self: llvmlite.Value | t.CPtr = None
if fd_at.value.kind() == ast.ASTKind.Name:
fd_onm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(fd_at.value)
if fd_onm.id is not None:
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
fd_mn: str = pool.alloc(256)
if fd_mn is not None:
viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
fd_meta: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
trans.SymTab, fd_mn)
if fd_meta is not None and fd_has_fields != 0:
fd_self = fd_meta
if fd_self is None:
if fd_has_fields != 0:
# 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化
# 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
if fd_self is None:
return None
fd_obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fd_at.value, None, 0, trans)
if fd_obj_val is not None:
fd_dfe: GenTypes.FieldEntry | t.CPtr = \
self.Gen.StructGen.lookup_field(fd_sty, "__data__")
if fd_dfe is not None and fd_dfe.Ty is not None:
fd_dp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
if fd_dp is not None:
llvmlite.build_store(builder, fd_obj_val, fd_dp)
fd_bfe: GenTypes.FieldEntry | t.CPtr = \
self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
if fd_bfe is not None and fd_bfe.Ty is not None:
fd_bp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull(
pool, fd_bfe.Ty, "null")
if fd_bp is not None and fd_null is not None:
llvmlite.build_store(builder, fd_null, fd_bp)
# FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)
self._inject_requires_fields(
trans, pool, builder, mod, fd_self, fd_cls)
else:
# 无字段扩展类(_Type):对象值 bitcast 到扩展类指针
fd_obj_v: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fd_at.value, None, 0, trans)
if fd_obj_v is None:
return None
fd_self = llvmlite.build_bitcast(
builder, fd_obj_v, llvmlite.Ptr(pool, fd_sty))
if fd_self is None:
return None
# 构建参数链表: self → 其余参数
llvmlite.value_set_next(fd_self, None)
fd_head: llvmlite.Value | t.CPtr = fd_self
fd_tail: llvmlite.Value | t.CPtr = fd_self
fd_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args
fd_can: int = 0
if fd_args is not None:
fd_can = len(fd_args)
for fd_ai in range(fd_can):
fd_arg: ast.AST | t.CPtr = fd_args.get(fd_ai)
if fd_arg is not None:
fd_av: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fd_arg, None, 0, trans)
# char 常量参数 → 单字符字符串(char 隐式转 str,
# 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化,
# 否则运行时 strlen/strncmp 解引用 44 崩溃)
if fd_arg.kind() == ast.ASTKind.Constant:
fd_ac: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(fd_arg)
if fd_ac.const_kind == ast.CONST_CHAR:
ch_buf: str = pool.alloc(2)
if ch_buf is not None:
ch_buf[0] = t.CChar(fd_ac.int_val)
ch_buf[1] = '\0'
fd_av = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, ch_buf, trans)
if fd_av is not None:
llvmlite.value_set_next(fd_av, None)
llvmlite.value_set_next(fd_tail, fd_av)
fd_tail = fd_av
# 返回类型与调用名(来自 find_func_in_module 命中的函数定义)
fd_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(fd_func)
if fd_ret_ty is None:
fd_ret_ty = llvmlite.Int32(pool)
fd_fname: str = llvmlite.function_get_name(fd_func)
if fd_fname is None:
return None
# 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化
# 为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标
# 路径而非 list 内联 __getitem__(printf %s 解引用字符值崩溃)。
# 从定义模块 text.ll 读取精确返回类型(对齐普通方法调用 _translate_method_call)。
fd_sha1: str = pool.alloc(17)
if fd_sha1 is not None:
string.strncpy(fd_sha1, fd_fname, 16)
fd_sha1[16] = '\0'
fd_exact: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
pool, fd_sha1, fd_cls, method_name)
if fd_exact is not None:
fd_ret_ty = fd_exact
fd_total: int = 1 + fd_can
return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
# ============================================================
# _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段
#
# 对齐 TPC HandlesExprCall.py L4021-4079:
# 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用
# _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时
# bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖
# 用户已手动设置的值)。_find_provider 未注册(未 import
# _withcontent)时跳过注入,不影响非 with 场景。
# ============================================================
# ============================================================
def _inject_requires_fields(self, trans: HT.Translator | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
meta_ptr: llvmlite.Value | t.CPtr,
class_name: str) -> int:
"""在 with 上下文注入 requires 字段,返回注入字段数"""
if trans is None or pool is None or builder is None or mod is None:
return 0
if meta_ptr is None or class_name is None:
return 0
# _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent
# 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050
# (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with
# 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...)
# 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。
fd_fn_probe: llvmlite.Function | t.CPtr = self.find_func_in_module(
mod, "_find_provider")
if fd_fn_probe is None:
return 0
fd_fname: str = llvmlite.function_get_name(fd_fn_probe)
if fd_fname is None:
return 0
cnt: int = self.Gen.ClassGenBase.class_meta_count(
GenTypes.CLASS_META_REQUIRES, class_name)
if cnt <= 0:
return 0
sty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name)
if sty is None:
return 0
i8_ty_j: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if i8_ty_j is None:
return 0
i8_ptr_ty_j: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_j)
if i8_ptr_ty_j is None:
return 0
cur_func: llvmlite.Function | t.CPtr = trans._cur_func
if cur_func is None:
return 0
injected: int = 0
for ji in range(cnt):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(
GenTypes.CLASS_META_REQUIRES, class_name, ji)
if fname is None:
continue
fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field(
sty, fname)
if fe is None or fe.Ty is None:
continue
# 参数:字段名字符串(i8*)
fd_fstr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, fname, trans)
if fd_fstr is None:
continue
llvmlite.value_set_next(fd_fstr, None)
self._ensure_method_declare(
pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
fd_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
if fd_ptr is None:
continue
# 条件注入:ptr != NULL 才写入(保留用户手动设置的值)
fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull(
pool, i8_ptr_ty_j, "null")
if fd_null is None:
continue
fd_cmp: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_NE, fd_ptr, fd_null)
if fd_cmp is None:
continue
# 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中
# 冲突 → llc "Terminator found in the middle of a basic block")
vtc2: int = trans._label_counter
trans._label_counter = vtc2 + 1
fd_bb_buf2: str = pool.alloc(64)
if fd_bb_buf2 is None:
continue
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
fd_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, cur_func, fd_bb_buf2)
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
fd_inject_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, cur_func, fd_bb_buf2)
if fd_skip_bb is None or fd_inject_bb is None:
continue
llvmlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
# inject 块:bitcast + GEP + store,跳回 skip
llvmlite.position_at_end(builder, fd_inject_bb)
fd_cast: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, fd_ptr, fe.Ty)
if fd_cast is not None:
fd_fp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, sty, fe.Ty, meta_ptr, fe.Index)
if fd_fp is not None:
llvmlite.build_store(builder, fd_cast, fd_fp)
llvmlite.build_br(builder, fd_skip_bb)
# skip 块:恢复 CurBlock,继续后续代码生成
llvmlite.position_at_end(builder, fd_skip_bb)
injected += 1
return injected
# ============================================================
# _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文
# provider push/pop(__provides__ 元数据,fd1 注册)
#
# 对齐 TPC HandlesWith.py L138-174:
# with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8);
# 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责
# 维护 TLS provider 栈,供 _inject_requires_fields 查找。
# ============================================================
# ============================================================
def _emit_provider_push(self, trans: HT.Translator | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cls_name: str,
ctx_val: llvmlite.Value | t.CPtr) -> int:
"""生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数"""
if trans is None or pool is None or builder is None or mod is None:
return 0
if cls_name is None or ctx_val is None:
return 0
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(
GenTypes.CLASS_META_PROVIDES, cls_name)
if fd_pc <= 0:
return 0
# _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫:
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
fd_pfn_probe: llvmlite.Function | t.CPtr = self.find_func_in_module(
mod, "_push_provider")
if fd_pfn_probe is None:
return 0
fd_pfn: str = llvmlite.function_get_name(fd_pfn_probe)
if fd_pfn is None:
return 0
i8_ty_p: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if i8_ty_p is None:
return 0
i8_ptr_ty_p: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_p)
if i8_ptr_ty_p is None:
return 0
ret_i32_p: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if ret_i32_p is None:
return 0
# ctx 值 bitcast 到 i8*(provider 栈存 void*)
fd_ctx_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, ctx_val, i8_ptr_ty_p)
if fd_ctx_i8 is None:
return 0
for pi in range(fd_pc):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(
GenTypes.CLASS_META_PROVIDES, cls_name, pi)
if fname is None:
continue
fd_fstr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, fname, trans)
if fd_fstr is None:
continue
llvmlite.value_set_next(fd_fstr, fd_ctx_i8)
llvmlite.value_set_next(fd_ctx_i8, None)
self._ensure_method_declare(
pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
llvmlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
return fd_pc
def _emit_provider_pop(self, trans: HT.Translator | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cls_name: str) -> int:
"""生成 _pop_provider() 调用(每提供字段一次),返回字段数"""
if trans is None or pool is None or builder is None or mod is None:
return 0
if cls_name is None:
return 0
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(
GenTypes.CLASS_META_PROVIDES, cls_name)
if fd_pc <= 0:
return 0
# _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫:
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
fd_pon_probe: llvmlite.Function | t.CPtr = self.find_func_in_module(
mod, "_pop_provider")
if fd_pon_probe is None:
return 0
fd_pon: str = llvmlite.function_get_name(fd_pon_probe)
if fd_pon is None:
return 0
ret_i32_q: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if ret_i32_q is None:
return 0
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
for _pi in range(fd_pc):
llvmlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
return fd_pc
File diff suppressed because it is too large. Load diff
-584
View File
@@ -1,584 +0,0 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.CallGenBase as CallGenBase
# ============================================================
# CallGenLib - CallGenMixin 拆分(阶段 C):C 库声明 / 返回类型推断 /
# 变长参数 / memcpy / kwargs 调用辅助方法
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class CallGenLib(CallGenBase.CallGenBase):
"""C 库声明 / 返回类型推断 / kwargs 调用辅助(继承 Base)"""
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
name: str) -> llvmlite.Function | t.CPtr:
"""为已知的 C 库函数创建 declare 声明"""
if pool is None or mod is None or name is None:
return None
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
ret_ty: llvmlite.LLVMType | t.CPtr = None
params: int = 0 # 参数数量
if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0:
ret_ty = i64_ty
params = 1
elif string.strcmp(name, "memset") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "memcpy") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "memmove") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "memcmp") == 0:
ret_ty = i32_ty
params = 3
elif string.strcmp(name, "strcmp") == 0:
ret_ty = i32_ty
params = 2
elif string.strcmp(name, "strncmp") == 0:
ret_ty = i32_ty
params = 3
elif string.strcmp(name, "strcpy") == 0:
ret_ty = i8_ptr_ty
params = 2
elif string.strcmp(name, "strncpy") == 0:
ret_ty = i8_ptr_ty
params = 3
elif string.strcmp(name, "strcat") == 0:
ret_ty = i8_ptr_ty
params = 2
elif string.strcmp(name, "puts") == 0:
ret_ty = i32_ty
params = 1
elif string.strcmp(name, "atoi") == 0:
ret_ty = i32_ty
params = 1
elif string.strcmp(name, "atol") == 0:
ret_ty = i64_ty
params = 1
elif string.strcmp(name, "abs") == 0:
ret_ty = i32_ty
params = 1
elif string.strcmp(name, "labs") == 0:
ret_ty = i64_ty
params = 1
else:
return None
# 创建 declare
fn: llvmlite.Function | t.CPtr = llvmlite.create_declare(pool, mod, name, ret_ty)
if fn is None:
return None
# 添加参数
if params == 1:
if string.strcmp(name, "abs") == 0:
llvmlite.add_param(pool, fn, i32_ty, "n")
elif string.strcmp(name, "labs") == 0:
llvmlite.add_param(pool, fn, i64_ty, "n")
else:
llvmlite.add_param(pool, fn, i8_ptr_ty, "s")
elif params == 2:
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
llvmlite.add_param(pool, fn, i8_ptr_ty, "src")
elif params == 3:
if string.strcmp(name, "memset") == 0:
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
llvmlite.add_param(pool, fn, i32_ty, "c")
llvmlite.add_param(pool, fn, i64_ty, "n")
else:
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
llvmlite.add_param(pool, fn, i8_ptr_ty, "src")
llvmlite.add_param(pool, fn, i64_ty, "n")
return fn
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
func_name: str) -> llvmlite.LLVMType | t.CPtr:
"""根据函数名推断外部 includes 函数的返回类型
支持三种函数名形式:
1. 裸名: "parse", "strlen"
2. 别名: "json_parse"(from X import parse as json_parse)
3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name)
"""
if pool is None or func_name is None:
return llvmlite.Int32(pool)
# 提取裸函数名:去掉 SHA1 前缀(如 "240a9a4157959a9f.parse" → "parse")
# SHA1 前缀是 16 位十六进制 + '.',检查是否有 '.' 分隔
bare_name: str = func_name
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
if dot_pos is not None:
# '.' 后的部分是裸函数名
bare_name = dot_pos + 1
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i8* 的函数(指针返回值,截断会导致错误)
if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strcpy") == 0 or string.strcmp(func_name, "strcpy") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "strncpy") == 0 or string.strcmp(func_name, "strncpy") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memset") == 0 or string.strcmp(func_name, "memset") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0:
return i8_ptr_ty
if string.strcmp(bare_name, "memmove") == 0 or string.strcmp(func_name, "memmove") == 0:
return i8_ptr_ty
# JSON 解析函数返回 JsonValue*(指针)
# from json.__parser import parse as json_parse → 别名 json_parse 也需覆盖
if string.strcmp(bare_name, "parse") == 0 or string.strcmp(func_name, "parse") == 0 or string.strcmp(func_name, "json_parse") == 0:
return i8_ptr_ty
# MemBuddy 方法(当类型信息丢失时可能走外部函数路径)
# 存疑
# malloc:裸名 CExport 跨模块调用(uhci/desktop 等 mm.malloc → @malloc)
# 在模块归属解析失败(mod_ret_sha1=None)或 vpi/text.ll 未命中时回退到
# 此白名单。缺失导致返回类型被推断为 i32 → call i32 @malloc 截断 64 位
# 指针 → mm.malloc 实际返回非空但调用方判 None(uhci frame list /
# desktop bg_row_buf 根因)。calloc/realloc/alloc 均已登记,malloc 遗漏。
if string.strcmp(bare_name, "malloc") == 0 or string.strcmp(func_name, "malloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "alloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "calloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "realloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "alloc_buf") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃)
if string.strcmp(func_name, "get_logger") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "fmt_buf") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏)
if string.strcmp(func_name, "get_includes_binary_dir") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# HandlesType 模块函数(返回 ast.AST|t.CPtr 联合类型,跨模块推断为 i32 导致指针截断)
if string.strcmp(bare_name, "_parse_type_string") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i64 的函数
if string.strcmp(func_name, "strlen") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "atoll") == 0:
return llvmlite.Int64(pool)
# 返回 double 的函数
if string.strcmp(func_name, "atof") == 0:
return llvmlite.Double(pool)
# 返回 i8 的函数
if string.strcmp(func_name, "samestr") == 0:
return llvmlite.Int8(pool)
# Win32 API 返回指针(VOIDPTR/HANDLE)的函数
# stubs 翻译时未注入,需推断返回 i8* 避免指针被截断为 i32 导致解引用崩溃
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
if string.strcmp(func_name, "VirtualAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "HeapAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "HeapReAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "HeapCreate") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetProcessHeap") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GlobalAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GlobalFree") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GlobalLock") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LocalAlloc") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LocalFree") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetProcAddress") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetModuleHandleA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetModuleHandleW") == 0:
return i8_ptr_ty
# GetCommandLineA/W 返回 LPSTR(i8*):缺则 64 位指针被截成 i32 →
# inttoptr 后 strlen 读到无效地址(SysLibTest argv 段 ASAN 崩溃,
# 非 ASAN 环境随机表现:IDE 0xC0000005 / 部分环境侥幸可读)。
if string.strcmp(func_name, "GetCommandLineA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "GetCommandLineW") == 0:
return i8_ptr_ty
# gethostbyname 返回 hostent*(i8*)
if string.strcmp(func_name, "gethostbyname") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LoadLibraryA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "LoadLibraryW") == 0:
return i8_ptr_ty
# Win32 Handle 返回函数(HANDLE = void* = i8*)
if string.strcmp(func_name, "GetStdHandle") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "CreateFileA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "CreateFileW") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "FindFirstFileA") == 0:
return i8_ptr_ty
if string.strcmp(func_name, "FindFirstFileW") == 0:
return i8_ptr_ty
# Win32 API 返回 i64 (SIZE_T) 的函数
if string.strcmp(func_name, "VirtualQuery") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "HeapSize") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "HeapCompact") == 0:
return llvmlite.Int64(pool)
if string.strcmp(func_name, "GlobalSize") == 0:
return llvmlite.Int64(pool)
# llvmlite build_* 函数返回类型推断
# stubs 翻译时未注入,需推断返回类型避免指针被截断为 i32
# 大多数 build_* 函数返回 Value*(指针),少数终止指令返回 void
if string.strncmp(func_name, "build_", 6) == 0:
# 返回 void 的 build_* 函数(终止指令和无返回值指令)
if string.strcmp(func_name, "build_ret") == 0 or string.strcmp(func_name, "build_ret_void") == 0 or \
string.strcmp(func_name, "build_store") == 0 or string.strcmp(func_name, "build_br") == 0 or \
string.strcmp(func_name, "build_cond_br") == 0 or string.strcmp(func_name, "build_unreachable") == 0 or \
string.strcmp(func_name, "build_switch") == 0:
return llvmlite.Void(pool)
# 其他 build_* 函数返回 Value*(指针)
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# llvmlite 类型工厂函数返回 LLVMType*(指针)
# Int1/Int8/Int16/Int32/Int64/Ptr/Func/Array/Struct/Void/Half/Float/Double/FP128/Label
# new_param_node/new_phi_incoming 返回 ParamNode*/PhiIncoming*(指针)
# const_int32/const_int64/ConstFloat/ConstZero/ConstNull/ConstInt 返回 Value*(指针)
# function_get_name/param_get_ty 等访问器返回指针
if string.strcmp(func_name, "Int1") == 0 or string.strcmp(func_name, "Int8") == 0 or string.strcmp(func_name, "Int16") == 0 or \
string.strcmp(func_name, "Int32") == 0 or string.strcmp(func_name, "Int64") == 0 or string.strcmp(func_name, "Ptr") == 0 or \
string.strcmp(func_name, "Func") == 0 or string.strcmp(func_name, "Array") == 0 or string.strcmp(func_name, "Struct") == 0 or \
string.strcmp(func_name, "Void") == 0 or string.strcmp(func_name, "Half") == 0 or string.strcmp(func_name, "Float") == 0 or \
string.strcmp(func_name, "Double") == 0 or string.strcmp(func_name, "FP128") == 0 or string.strcmp(func_name, "Label") == 0 or \
string.strcmp(func_name, "new_param_node") == 0 or string.strcmp(func_name, "new_phi_incoming") == 0 or \
string.strcmp(func_name, "const_int32") == 0 or string.strcmp(func_name, "const_int64") == 0 or \
string.strcmp(func_name, "ConstFloat") == 0 or string.strcmp(func_name, "ConstZero") == 0 or \
string.strcmp(func_name, "ConstNull") == 0 or string.strcmp(func_name, "ConstInt") == 0 or \
string.strcmp(func_name, "function_get_name") == 0 or string.strcmp(func_name, "param_get_ty") == 0 or \
string.strcmp(func_name, "value_get_ty") == 0 or string.strcmp(func_name, "value_get_next") == 0 or \
string.strcmp(func_name, "function_get_ret_ty") == 0 or string.strcmp(func_name, "function_get_params") == 0 or \
string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 默认 i32(整数):与本模块函数定义的默认返回类型一致(HandlesFunctions.py line 324)
# 已知返回指针的函数在上方显式列出(返回 i8*),已知返回 i64 的函数也显式列出
# 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
# WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断
# --nowarn-register 可抑制此 WARN(自举编译中大量出现,刷屏影响日志收敛)
if VLogger.nowarn_register() == 0:
VLogger.warning(
f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}",
"HandlesExprCall")
return llvmlite.Int32(pool)
def _get_external_func_param_ty(self, pool: memhub.MemBuddy | t.CPtr,
func_name: str,
param_idx: int) -> llvmlite.LLVMType | t.CPtr:
"""获取外部函数第 param_idx 个参数的期望类型,None 表示用字面量原类型
对 Win32 API 函数的所有非指针参数返回 i64,匹配 declare 中
ULONG/SIZE_T 被统一翻译为 i64 的情况。
"""
if pool is None or func_name is None:
return None
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
# Win32 API 函数列表:所有非指针参数在 declare 中都是 i64
# (因为 ULONG/DWORD/SIZE_T 在 LLVM stubs 翻译时都被映射为 i64)
win32_apis: list[str] | t.CPtr = [
"VirtualAlloc", "VirtualFree", "VirtualProtect", "VirtualQuery",
"VirtualLock", "VirtualUnlock",
"HeapCreate", "HeapDestroy", "HeapAlloc", "HeapReAlloc",
"HeapFree", "HeapSize", "HeapValidate", "HeapCompact",
"GlobalAlloc", "GlobalFree", "GlobalLock", "GlobalUnlock", "GlobalSize",
"LocalAlloc", "LocalFree",
"GetLastError", "SetLastError",
"Sleep", "SleepEx",
"GetTickCount", "GetProcessHeap"
]
# list 迭代不支持,用 range + get 替代
api_count: t.CSizeT = len(win32_apis)
for ai in range(api_count):
api_name: str = win32_apis.get(ai)
if string.strcmp(api_name, func_name) == 0:
return i64_ty
# 内存分配函数:size 参数在声明中为 i64(size_t),裸名回退时
# 不提升则 call(i32 size) 与 define(i64 size) 不匹配(desktop start
# 第 4 次 mm.malloc(w*4) 返回值被破坏收到 0x0 根因;HelloWorld
# vpsdk 的 malloc 同理)。calloc/realloc 的 size 参数在 idx 1。
if string.strcmp(func_name, "malloc") == 0 \
or string.strcmp(func_name, "alloc") == 0 \
or string.strcmp(func_name, "alloc_buf") == 0:
if param_idx == 0:
return i64_ty
if string.strcmp(func_name, "calloc") == 0:
if param_idx == 0 or param_idx == 1:
return i64_ty
if string.strcmp(func_name, "realloc") == 0:
if param_idx == 1:
return i64_ty
return None
def _is_sha1_mangled_call(self, call_name: str) -> int:
"""判断 call_name 是否为跨模块 Viper 函数("{16位hex}.func" 形式)
C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0,
不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。
"""
if call_name is None:
return 0
first_dot: str = string.strchr(call_name, '.')
if first_dot is None:
return 0
pre_len: t.CSizeT = t.CSizeT(t.CUInt64T(first_dot) - t.CUInt64T(call_name))
if pre_len != 16:
return 0
for hx in range(16):
ch: int = call_name[hx]
if ch < 48 or ch > 57:
if ch < 97 or ch > 102:
if ch < 65 or ch > 70:
return 0
return 1
def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
dst_val: llvmlite.Value | t.CPtr,
src_val: llvmlite.Value | t.CPtr,
num_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""将 memcpy 调用映射到 @llvm.memcpy 内联函数
直接接收三个 Value 参数,避免通过 Next 链表遍历(共享 alloca 对象的
Next 字段可能被其他操作污染,导致链表断裂)。
"""
if pool is None or builder is None or mod is None:
return None
if dst_val is None or src_val is None or num_val is None:
return None
# 在函数顶部声明 void_ty,避免后续遮蔽
void_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
# 声明 @llvm.memcpy(如未声明)
memcpy_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, "llvm.memcpy")
if memcpy_func is None:
memcpy_func = llvmlite.create_declare(pool, mod, "llvm.memcpy", void_ty)
if memcpy_func is not None:
i8_ptr_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i1_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "dst")
llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "src")
llvmlite.add_param(pool, memcpy_func, i64_ty_d, "len")
llvmlite.add_param(pool, memcpy_func, i1_ty_d, "isvolatile")
# 类型转换: dst, src → i8*; num → i64
# 注意: 使用 value_get_ty 访问器获取 Ty,避免本地 Value 类型字段索引偏移
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
# dst → i8*
dst_coerced: llvmlite.Value | t.CPtr = dst_val
dst_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(dst_val)
if self.Gen.ExprGenBase.is_ptr_type(dst_ty) != 0:
dst_coerced = llvmlite.build_bitcast(builder, dst_val, i8_ptr_ty)
else:
dst_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dst_ty)
if dst_bits != 0:
if dst_bits < 64:
dst_coerced = llvmlite.build_zext(builder, dst_val, i64_ty)
dst_coerced = llvmlite.build_inttoptr(builder, dst_coerced, i8_ptr_ty)
# src → i8*
src_coerced: llvmlite.Value | t.CPtr = src_val
src_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(src_val)
if self.Gen.ExprGenBase.is_ptr_type(src_ty) != 0:
src_coerced = llvmlite.build_bitcast(builder, src_val, i8_ptr_ty)
else:
src_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(src_ty)
if src_bits != 0:
if src_bits < 64:
src_coerced = llvmlite.build_zext(builder, src_val, i64_ty)
src_coerced = llvmlite.build_inttoptr(builder, src_coerced, i8_ptr_ty)
# num → i64
num_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(builder, num_val, i64_ty)
if dst_coerced is None or src_coerced is None or num_coerced is None:
return None
# 添加 i1 false 参数(isvolatile)— llvm.memcpy 需要 4 个参数
false_val: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, llvmlite.Int1(pool), 0, "false")
# 使用 includes 中的访问器函数构建参数链表,避免本地 Value 类型字段索引偏移
# (f26cd4c78b4d554c.Value 缺少 GSListNode 的 Next 字段,直接访问 .Next 会修改 .Ty)
llvmlite.value_set_next(dst_coerced, src_coerced)
llvmlite.value_set_next(src_coerced, num_coerced)
if false_val is not None:
llvmlite.value_set_next(num_coerced, false_val)
llvmlite.value_set_next(false_val, None)
else:
llvmlite.value_set_next(num_coerced, None)
# 使用 build_call 发射 call 指令(内部用 includes Value 类型访问字段,索引正确)
# _ll_name_needs_quote 已对 llvm. 前缀豁免引号
result: llvmlite.Value | t.CPtr = llvmlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0)
# 返回 dst(匹配 C memcpy 语义: 返回 dest 指针)
return dst_coerced
# ============================================================
# _translate_call_with_kwargs — 带关键字参数的函数调用
#
# 支持两种语法:
# add(b=4, a=3) — 全关键字乱序
# greet("Hello", name="World") — 位置 + 关键字混合
#
# 策略: 遍历 found_func.Params 链表,对每个参数:
# 1. 若位置参数仍有剩余(param_idx < can),取位置参数
# 2. 否则按参数名匹配关键字参数
# 3. 按参数顺序构建调用参数链表
# ============================================================
# ============================================================
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
func_name: str,
found_func: llvmlite.Function | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
keywords: list[ast.AST | t.CPtr] | t.CPtr,
kw_count: t.CSizeT,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""带关键字参数的函数调用,按参数顺序构建调用链表
使用 llvmlite 访问器函数绕过 stub 类型限制:
found_func.Params/RetTy/Name 和 cur_param.Name/Ty/Next 在 stub
类型中字段不足,直接访问会被 TransPyC 静默跳过。
"""
head: llvmlite.Value | t.CPtr = None
tail: llvmlite.Value | t.CPtr = None
actual_count: t.CSizeT = 0
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
mc_dst1: llvmlite.Value | t.CPtr = None
mc_src1: llvmlite.Value | t.CPtr = None
mc_num1: llvmlite.Value | t.CPtr = None
# 遍历函数参数链表(使用访问器获取 Head)
param_idx: t.CSizeT = 0
cur_param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
while cur_param is not None:
arg_val: llvmlite.Value | t.CPtr = None
param_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param)
if param_idx < can:
# 优先使用位置参数
arg: ast.AST | t.CPtr = cargs.get(param_idx)
if arg is not None:
# 传形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant)
arg_val = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans, param_ty)
else:
# 查找匹配的关键字参数
param_name: str = llvmlite.param_get_name(cur_param)
if param_name is not None:
for ki in range(kw_count):
kw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(keywords.get(ki))
if kw is None or kw.arg is None or kw.value is None:
continue
if self.Gen.CallGenCall._kwarg_name_matches(kw.arg, param_name) == 1:
arg_val = self.Gen.ExprGen.translate_value(
builder, pool, mod, kw.value, None, 0, trans, param_ty)
break
if arg_val is not None:
# 类型转换(按参数类型,使用访问器)
if param_ty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, param_ty)
arg_val.Next = None
if head is None:
head = arg_val
else:
tail.Next = arg_val
tail = arg_val
# memcpy 专用: 保存前三个参数的独立引用
if actual_count == 0:
mc_dst1 = arg_val
elif actual_count == 1:
mc_src1 = arg_val
elif actual_count == 2:
mc_num1 = arg_val
actual_count += 1
param_idx += 1
cur_param = llvmlite.param_get_next(cur_param)
# 使用模块中的实际函数名(可能带 SHA1 前缀,使用访问器)
# declare 和 define 统一用 found_func.Name:
# - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name)
# - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名
# 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀,
# 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference
call_name: str = func_name
found_name: str = llvmlite.function_get_name(found_func)
if found_name is not None:
call_name = found_name
# 返回类型(使用访问器)
call_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func)
if call_ret_ty is None:
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name)
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3:
return self._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1)
return llvmlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)
-246
View File
@@ -1,246 +0,0 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.ExprGenAttr as ExprGenAttr
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.CallGenBase as CallGenBase
# ============================================================
# CallGenVt - CallGenMixin 拆分(阶段 B):vtable 槽位写入
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class CallGenVt(CallGenBase.CallGenBase):
"""vtable 槽位写入"""
# ============================================================
# _write_vtable_slot / _write_vtable_attr — vtable 方法槽位直接写入
#
# serial0_file.open = serial0_open(devfs 驱动注册模式):把模块级函数
# 指针写入对象 vtable 的方法槽位。vtable 方法不是数据字段(FieldEntry),
# get_attribute_ptr 查不到 → 此前 fatal_error "属性写入失败"。
# 对齐 TPC HandlesAssign.py L905-949 的写时复制:
# 对象 vtable 仍指向类默认 vtable 时,先复制一份(internal 全局 +
# llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store
# 函数指针(i8*)。多次写入互不覆盖(第二次起 vtable 已非默认,
# 跳过复制直接写槽位)。
# ============================================================
# ============================================================
def _write_vtable_slot(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr,
class_name: str,
method_index: int,
func_val: llvmlite.Value | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""生成 vtable 槽位写入(写时复制),成功返回 1"""
if pool is None or builder is None or mod is None or trans is None:
return 0
if obj_ptr is None or class_name is None or func_val is None:
return 0
if method_index < 0 or trans._cur_func is None:
return 0
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if i8_ty is None:
return 0
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None:
return 0
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
if i8_ptr_ptr_ty is None:
return 0
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
if method_count <= 0:
return 0
# 构造 vtable 类型 { i8*, i8*, ... }
vt_first: llvmlite.ParamNode | t.CPtr = None
vt_prev: llvmlite.ParamNode | t.CPtr = None
for vi in range(method_count):
pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty)
if pn is None:
continue
if vt_first is None:
vt_first = pn
if vt_prev is not None:
llvmlite.paramnode_set_next(vt_prev, pn)
vt_prev = pn
vtable_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, vt_first, method_count, None)
if vtable_ty is None:
return 0
# 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段)
vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, obj_ptr, i8_ptr_ptr_ty)
if vtable_slot is None:
return 0
vtable_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i8_ptr_ty, vtable_slot)
if vtable_ptr is None:
return 0
# 类默认 vtable 地址(比较用)
class_vt: llvmlite.Value | t.CPtr = self.Gen.CallGenLeafKit._make_vtable_ref_i8(
pool, builder, mod, class_name, trans)
if class_vt is None:
return 0
cmp_vt: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_EQ, vtable_ptr, class_vt)
if cmp_vt is None:
return 0
# 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如
# devfs open/close/read/write 连续赋值)同名 label 冲突)
vtc3: int = trans._label_counter
trans._label_counter = vtc3 + 1
vt_bb_buf3: str = pool.alloc(64)
if vt_bb_buf3 is None:
return 0
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
vt_copy_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, trans._cur_func, vt_bb_buf3)
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
vt_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, trans._cur_func, vt_bb_buf3)
if vt_copy_bb is None or vt_skip_bb is None:
return 0
llvmlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
# copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本
llvmlite.position_at_end(builder, vt_copy_bb)
vtc: int = trans._label_counter
trans._label_counter = vtc + 1
vcopy_buf: str = pool.alloc(256)
if vcopy_buf is not None:
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
vcopy_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(
pool, vcopy_buf, vtable_ty)
if vcopy_gv is not None:
llvmlite.module_add_global(mod, vcopy_gv)
vcopy_gv.Linkage = None
vcopy_gv.Initializer = "zeroinitializer"
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
# 输出时加的,引用侧不自动加)
vcopy_ref_nm: str = pool.alloc(272)
if vcopy_ref_nm is None:
vcopy_ref_nm = vcopy_buf
else:
viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
vcopy_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(
pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
if vcopy_ref is not None:
vcopy_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, vcopy_ref, i8_ptr_ty)
if vcopy_i8 is not None:
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
# → llc "expected value token"(devfs memcpy 根因)
vtsz_buf: str = pool.alloc(32)
if vtsz_buf is not None:
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf)
if vt_size is not None:
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(
pool, builder, mod, vcopy_i8, class_vt, vt_size)
llvmlite.build_store(builder, vcopy_i8, vtable_slot)
llvmlite.build_br(builder, vt_skip_bb)
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
llvmlite.position_at_end(builder, vt_skip_bb)
vt_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i8_ptr_ty, vtable_slot)
if vt_ptr2 is None:
return 0
vt_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, vt_ptr2, llvmlite.Ptr(pool, vtable_ty))
if vt_typed is None:
return 0
vt_slotp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
if vt_slotp is None:
return 0
fptr_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, func_val, i8_ptr_ty)
if fptr_i8 is not None:
llvmlite.build_store(builder, fptr_i8, vt_slotp)
return 1
def _write_vtable_attr(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)"""
if node is None or builder is None or pool is None or mod is None:
return 0
if rhs_val is None or trans is None:
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
return 0
# 解析类名(AnnotClassName;去模块前缀)
class_nm_v: str = None
if at.value.kind() == ast.ASTKind.Name:
nm_v: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_v.id is not None:
ve_v: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm_v.id)
if ve_v is not None:
class_nm_v = ve_v.AnnotClassName
elif at.value.kind() == ast.ASTKind.Attribute:
# 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenAttr,经 self.Gen 调用
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
if class_nm_v is None:
return 0
fd_dot_v: str = string.strrchr(class_nm_v, '.')
if fd_dot_v is not None:
class_nm_v = fd_dot_v + 1
if self.Gen.StructGen.get_struct_type(class_nm_v) is None:
return 0
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
if vm_idx_v < 0:
return 0
# 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca)
obj_ptr_v: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name:
obj_ptr_v = self.Gen.VarGen.lookup_var(
trans.SymTab, ((ast.Name | t.CPtr)(at.value)).id)
else:
obj_ptr_v = self.Gen.ExprGen.translate_value(
builder, pool, mod, at.value, None, 0, trans)
if obj_ptr_v is None or obj_ptr_v.Ty is None:
return 0
# X|t.CPtr 变量(Ptr(Ptr))load 解引用取对象指针
match obj_ptr_v.Ty:
case llvmlite.LLVMType.Ptr(inner_v):
match inner_v:
case llvmlite.LLVMType.Ptr(_):
obj_ptr_v = llvmlite.build_load(builder, inner_v, obj_ptr_v)
case _:
pass
case _:
pass
if obj_ptr_v is None:
return 0
return self._write_vtable_slot(
pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
File diff suppressed because it is too large. Load diff
+753
View File
@@ -0,0 +1,753 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
from . import ClassGenBase
# ============================================================
# ClassGenDef - ClassGenMixin 拆分(阶段 D):translate_class_def 主入口
# ============================================================
novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
"""translate_class_def 主入口"""
# ============================================================
# translate_class_def — 翻译 class 定义(簇 D)
# ============================================================
def translate_class_def(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 class 定义,返回 0"""
if node is None or trans is None: return 0
cd: ast.ClassDef* = ast.ClassDef*(node)
if cd is None or cd.name is None: return 0
pool: memhub.MemBuddy* = trans.Pool
class_name: str = cd.name
if trans.ModuleSha1 is None:
# fail-fast:翻译类时模块 SHA1 缺失(命名空间隔离依赖它)——
VLogger.error("翻译类时 ModuleSha1 缺失", "CLASS")
self.Gen.TypeGenErr.fatal_error(cd, "翻译类时 ModuleSha1 缺失", "TypeError")
# 命名空间隔离:标记为当前文件可见(必须在 existing 检查之前,
# 因为 Phase B 时结构体已注册会提前 return,否则永远无法标记本地类)
self.Gen.StructGen.add_visible_struct(pool, class_name)
# 登记"本模块声明了这个类"(键 "{SHA1}.{类名}",与 struct 注册状态无关):
# 泛型类型实参命名规范化(TypeGenResolve._norm_type_arg_name 规则 5.5)据此
# 判定裸类名归属 —— 类体里的**自引用**(如 includes/ast/base.vp
# `AST.children: list[rawptr[AST]]`)解析时本类尚未 register_struct,
# 只查注册表会留在裸名 ⇒ 与后续带 SHA1 的形式分裂成两个特化名。
self.Gen.StructGen.mark_module_class(pool, trans.ModuleSha1, class_name)
# 枚举类:交给 HandlesEnum 处理
# 分派歧义检测(fail-fast):REnum 优先于 CEnum/CUnion。若某类同时被判为枚举,
# 说明被 `_is_enum_class` 抢先截胡 → 走 _translate_enum_def ⇒ 变体表永不注册
# ⇒ _is_renum_class_name 恒 0、变体构造拿不到 tag。两者语义互斥,禁止静默按枚举处理。
if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1:
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
self.Gen.TypeGenErr.fatal_error(
cd,
f"类 '{class_name}' 同时被判定为 CEnum 与 REnum(分派歧义),"
f"REnum 变体表将无法注册",
"TypeError")
return self.Gen.ClassGenEnum._translate_enum_def(trans, cd)
# 联合体类:交给联合体处理
if self.Gen.ClassGenBase._is_union_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_union_def(trans, cd)
# REnum 类:交给 REnum 处理({i32 tag, max_payload} 布局)
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_renum_def(trans, cd)
# 泛型类:存储为模板,不发射 IR(等实例化时特化)
# 用局部变量接收 cd.type_params,触发类型推断
tp_val: list[str]* = cd.type_params
if self.Gen.ClassGenGeneric._is_generic_class(tp_val) == 1:
self.Gen.ClassGenGeneric._register_generic_template(pool, cd, trans.ModuleSha1)
return 0
# 异常类(try-except):注册编码+继承树,不生成 struct
# (对齐 TPC HandlesClassDef.py L509-511:_is_exception_class →
# _RegisterExceptionClass → return,异常类不编译为结构体)
if self.Gen.ClassGenBase._is_exception_class_def(cd) == 1:
return self.Gen.ClassGenBase._register_exception_class_def(trans, cd)
# 嵌套类 / 嵌套 struct:类体里再写 class / struct 此前被**静默丢弃**
# (字段循环只认 AnnAssign、_translate_oop_methods 只认 FunctionDef),
# 现按既有扁平命名先例发射为独立结构体 `{外类}_{内类}`(与 TPC 的
# union/renum 嵌套命名 `{ClassName}_{Nested}` 同源)。
# 放在此处(enum/union/renum/泛型/异常分派之后、existing 检查之前):
# 位于**所有提前 return 之前**,故 declare_only=1/0 两条路径都会递归;
# 而 REnum/union 的嵌套类已由各自分支按"变体"处理并提前 return,不会重复发射。
self._translate_nested_classdefs(trans, cd)
# 检查是否已注册(用 SHA1 区分跨模块同名类)
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
# 空类注册不完整检测:existing FieldCount==0 但源类体声明了字段(vpi 注入/注册缺陷)。
# 标记 _deps_pending 延迟会让空 entry 持久存在、多遍死锁不收敛 ⇒ 改为中和旧 entry
# (置空 Name + Ty=None,使 find_struct_by_module 不再命中),走下方完整注册路径重建。
needs_rereg: int = 0
# 前向声明占位命中:本类此前被"指向未注册类的指针"注解按名字前向声明过
# (StructGenMixin.ensure_forward_class 建了 {sha}.Class 不透明占位)。
# 此处**不早退**,走下方"原位填充"分支——把字段体写进同一个 Ty 对象,
# 保住指针身份,使所有已发出的 Ptr(占位.Ty) 引用(如 CallGenBase.Gen)
# 在填充后自动指向完整定义(对齐 TPC 的 opaque 占位 + set_body 填充)。
fill_fwd: int = 0
if existing is not None:
if existing.IsForwardDecl != 0:
fill_fwd = 1
elif existing.FieldCount == 0 and trans._declare_only == 0:
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
if existing.Name is not None:
existing.Name[0] = '\0'
existing.Ty = None
existing.FieldCount = 0
needs_rereg = 1
if existing is not None and needs_rereg == 0 and fill_fwd == 0:
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
if existing.Ty is not None:
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
# 其 Name(如 "sha1.ClassName")指向 Phase 1a 的 pool 内存。虽然 mark1
# 之前分配不被 reset 回收,但 struct_ty 会被后续文件翻译引用(vtable/
# self 参数类型打印),部分实现下 pool 块被复用导致 Name 悬空崩溃。
# 保险起见:深拷贝 Name 到 stdlib,确保跨文件永久存活。
if trans._declare_only == 0:
# 深拷贝整个 existing.Ty 到 stdlib:1a pool Ty 跨 1b reset 悬空,
# _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表
# 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType
# 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。
ty_deep: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, existing.Ty)
if ty_deep is not None:
existing.Ty = ty_deep
old_name: str = llvmlite.get_struct_name(existing.Ty)
if old_name is not None:
on_len: sizet = string.strlen(old_name)
on_copy: str = CompilerFactory.factory.PermPool.alloc(on_len + 1)
if on_copy is not None:
string.strcpy(on_copy, old_name)
# in-place 覆盖所指 REnum 值(保留 existing.Ty 的指针同一性:
# Phase A 已把它登记进 named-type 表,换指针旧条目仍指向悬空
# Name)。REnum 变体构造返回 **boxed 指针**,`*槽 = <boxed 变体>`
# 属「构造物化」(boxed 指针 → 值槽的整对象拷贝,语义即
# load+store),按严格规则③的构造物化例外放行——与
# c.DerefAs(ptr, <boxed 变体>) 等价,用 * 写法更直白。
*existing.Ty = llvmlite.LLVMType.Struct(llvmlite.get_struct_fields(existing.Ty), llvmlite.get_struct_fcount(existing.Ty), on_copy)
# Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行)
llvmlite.module_add_named_type(trans.Module, pool, existing.Ty)
# B5:existing 路径(Phase B 已注册类)同样需设置 packed/aligned——
# Phase A 注册时虽已设置,但 Phase B 重新 add_named_type 后节点
# IsPacked=0,打印输出 `{...}`(idt_entry packed 未生效根因)。
if self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed") != 0:
llvmlite.module_set_named_type_packed(trans.Module, pool, existing.Ty, 1)
# StructTable 每 Phase 可能重建(existing 是 Phase B 新 entry),
# 必须补设 IsPacked——否则跨模块 stub 注入(ensure_struct_def
# _in_module)按 entry.IsPacked 判定 packed 时读 0 → stub 非 packed。
existing.IsPacked = 1
align_ex: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
if align_ex > 0 and existing.Align == 0:
existing.Align = align_ex
# Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体
# Phase B(declare_only=0)时全量翻译方法体
if trans._declare_only == 1:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name, 1)
else:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name)
# SHA1 已在 register_struct 时设置,无需再补
return 0
# ============================================================
# 触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])
# 必须在 _detect_vtable_status 和父类字段继承之前调用,
# 否则 find_struct_by_module("GSListNode[Value]") 返回 None
# ============================================================
self.Gen.ClassGenBase._trigger_base_specialization(trans, cd)
# ============================================================
# VTable 检测:判断是否启用虚表
# ============================================================
has_vtable: int = self.Gen.ClassGenBase._detect_vtable_status(cd, trans)
parent_name: str = self.Gen.ClassGenBase._get_parent_class(cd, trans)
# 多继承:收集所有真实父类名(跳过 marker base),供字段展平/vtable/MRO 使用
parents_buf: sizet* = pool.alloc(8 * GenTypes.PARENT_MAX)
parents_count: int = 0
if parents_buf is not None:
parents_count = self.Gen.ClassGenBase._collect_parents(pool, cd, parents_buf, GenTypes.PARENT_MAX, trans)
is_novtable_deco: int = 0
if cd.decorator_list is not None:
if self.Gen.ClassGenBase._has_decorator(cd.decorator_list, "NoVTable") == 1:
is_novtable_deco = 1
# ============================================================
# PhaseA 预注册延迟检查:父类未注册时延迟注册当前类
#
# 根因:AssignHandle(HandlesBase.Mixin) 有自己的字段 _CurrentClass,
# 当 Mixin 未注册时(字母序:HandlesAssign.py 在 HandlesBase.py 之前),
# field_count=1(不是0),field_count==0 静默返回不触发,
# AssignHandle 被注册为只有 _CurrentClass 字段(缺 Trans)。
# 后续遍 existing is not None,不补字段 → attribute ptr is None: self.Trans
#
# 修复:declare_only==1 且父类未注册时,延迟注册到下一遍。
# 多遍 PhaseA-pre 会在后续遍中重试(父类已注册后完整注册)。
# Phase B(declare_only==0)不延迟,避免死锁。
# ============================================================
if trans._declare_only == 1 and parent_name is not None:
parent_check: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
if parent_check is None:
parent_check = self.Gen.StructGen.find_struct(parent_name)
if parent_check is None:
return 0
# ============================================================
# 1. 遍历 class body,收集字段信息
# ============================================================
# 用 CSizeT 数组存储指针值(64 位系统上 8 字节)
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_defaults_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_annot_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段 volatile 标记缓冲(1=字段注解联合含 t.CVolatile);与 field_annot_buf
# 并行,取值 0/1(sizet 数组复用,避免另设类型)。
field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段「所指物 volatile」标记缓冲(1=字段注解为 rawptr[t.CVolatile | T],
# 等价 C 的 volatile T*);与 field_volatile_buf 并行,取值 0/1。
field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段 const 对象标记缓冲(1=字段注解含 t.CConst 且字段类型非指针);
# 与 field_volatile_buf 并行,取值 0/1。
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None:
return 0
if field_annot_buf is None or field_volatile_buf is None:
return 0
if field_pointee_volatile_buf is None or field_const_buf is None:
return 0
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_annot_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_const_buf, 0, 8 * GenTypes.FIELD_MAX)
field_count: int = 0
# ============================================================
# 1.5 继承父类字段(字段展平)
# ============================================================
#
# 父类字段排在子类字段之前,结构体布局:
# [vtable_ptr?, parent_field1, ..., child_field1, ...]
# 跳过父类的 __vtable__ 字段(子类会有自己的 vtable 指针)
# ============================================================
# 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明优先)
if parents_count > 0 and parents_buf is not None:
for pi in range(parents_count):
pname_addr: sizet = parents_buf[pi]
if pname_addr == 0: continue
pname: str = rawptr(pname_addr)
if pname is None: continue
# 用 SHA1 感知查找父类,规避跨模块同名找错
parent_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname, trans.ModuleSha1)
if parent_entry is None:
# 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin),
# find_struct_by_module 不回退全局查找,此处手动回退到 find_struct,
# 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败
parent_entry = self.Gen.StructGen.find_struct(pname)
if parent_entry is None:
# 父类尚未注册(Phase A 按文件序,子类模块可能先于父类模块):此时
# **不能**继续注册——StructTable 在 StdlibAlloc=1 时用 perm_pool 分配,
# 残缺条目跨 pass 存活,后续 pass 的 existing 早退永远无法修复 → 子类
# __init__ 写父类字段每遍永久失败。整类 defer(不注册);模块级变量/函数
# 参数解析失败静默降级 i32,不致命。
return 0
pfc: int = parent_entry.FieldCount
for pfi in range(pfc):
pfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(parent_entry, pfi)
if pfe is None: continue
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
if pfn is None: continue
# 跳过父类的 vtable 指针字段
if string.strcmp(pfn, "__vtable__") == 0: continue
# 多继承去重:字段名已存在跳过(先声明父类优先)
if self.Gen.ClassGenBase._field_name_exists(field_names_buf, field_count, pfn) != 0: continue
if field_count < GenTypes.FIELD_MAX:
pft: llvmlite.LLVMType* = self.Gen.StructGen.get_field_type_ptr(pfe)
pfd: ast.AST* = self.Gen.StructGen.get_field_default_ptr(pfe)
pfa: str = self.Gen.StructGen.get_field_annot_class_name(pfe)
field_names_buf[field_count] = sizet(pfn)
field_types_buf[field_count] = sizet(pft)
if pfd is not None:
field_defaults_buf[field_count] = sizet(pfd)
else:
field_defaults_buf[field_count] = 0
if pfa is not None:
stored_val: sizet = sizet(pfa)
field_annot_buf[field_count] = stored_val
else:
field_annot_buf[field_count] = 0
# 继承父类字段的 volatile 标记(字段展平后语义不变)
field_volatile_buf[field_count] = sizet(pfe.IsVolatile)
# 继承父类字段的「所指物 volatile」标记(展平后语义不变)
field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile)
# 继承父类字段的 const 对象标记(展平后语义不变)
field_const_buf[field_count] = sizet(pfe.IsConst)
field_count += 1
children: list[ast.AST*]* = cd.children
if children is None:
return 0
cn: sizet = len(children)
for ci in range(cn):
stmt: ast.AST* = children.get(ci)
if stmt is None: continue
sk: int = stmt.kind()
if sk != ast.ASTKind.AnnAssign: continue
# AnnAssign(target=Name(id), annotation=type, value=...)
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
if aa is None or aa.target is None: continue
# 获取字段名
if aa.target.kind() != ast.ASTKind.Name: continue
tgt: ast.Name* = ast.Name*(aa.target)
if tgt.id is None: continue
# 类体 CDefine 常量(任意位置,2026-08-10):注册到全局 CDefine 表。
# 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查
# CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册
# 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。
if self.Gen.AssignGen.is_cdefine_annotation(aa.annotation) != 0:
if aa.value is not None:
self.Gen.AssignGen._register_cdefine_from_value(pool, tgt.id, aa.value, aa.annotation)
# CDefine 字段类型固定 i32,直接收集后跳过通用注解解析:
# resolve_annotation_type 不识别 "CDefine"(map_t_type/CTypedef
# 表均无此名),类体尾部常量(ISINSTANCE_CLASS_MAX 是首个触发
# 该路径的实例)Phase B 会 NameError: 't.CDefine' is not defined。
# 与类头常量(MRO_MAX 等)的 i32 字段语义保持一致。
if field_count < GenTypes.FIELD_MAX:
cdef_ty_l: llvmlite.LLVMType* = llvmlite.Int32(pool)
if cdef_ty_l is not None:
field_names_buf[field_count] = sizet(tgt.id)
field_types_buf[field_count] = sizet(cdef_ty_l)
if aa.value is not None:
field_defaults_buf[field_count] = sizet(aa.value)
else:
field_defaults_buf[field_count] = 0
field_count += 1
continue
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
# 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码
# (FakeDuck:_str.__requires__=['__mbuddy__'])。此处解析 list[str]
# 字面量注册到持久元数据表,供 requires 注入 / with provider 查询。
if string.strcmp(tgt.id, "__provides__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_PROVIDES, class_name, aa.value)
continue
if string.strcmp(tgt.id, "__requires__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRES, class_name, aa.value)
continue
if string.strcmp(tgt.id, "__require_must__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRE_MUST, class_name, aa.value)
continue
# 解析字段类型
field_ty: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
# void 不是合法字段类型(用户 2026-10-01:void 只能用于 `void*` 与
# `typedef 不透明结构体 = void`);`void*` 已在上游归并为 i8*,不会命中。
# 放任会按 LLVM void 建字段槽 ⇒ 结构体布局非法(拖到 llc 才暴露),
# 故此处编译期 fail-fast。
if field_ty is not None and self.Gen.StmtGenDecl._is_void_type(field_ty) != 0:
self.Gen.TypeGenErr.fatal_error(aa, f"字段 '{tgt.id}' 不能是 void 类型(void 只能用于 void* 与 typedef 别名)", "TypeError")
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
# for x in self.field 会误走指针迭代(元素非整数 → null 终止符
# 检查失败报错)。推断为静态数组后走数组迭代(按长度 N)。
if field_ty is not None:
field_ty = self.Gen.AssignGen._infer_static_array_ty_from_literal(pool, aa.annotation, aa.value, field_ty)
if field_ty is None:
# BUG 5 方案1修改点1:消除 i32 静默回退
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
# Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译
# 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱)
# 例外:泛型 Subscript(如 list[AST|t.CPtr])未特化时回退为 Ptr(i8),
# 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name
# 供 translate_attribute 做后续 bitcast。
is_generic_sub: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
# t.CArray[elem, N](含裸 CArray[elem, N])是定长值数组,非泛型堆对象——
# 元素类型未就绪时若回退 Ptr(i8) 会注册成退化字段布局,Phase B 复用该 entry
# 后 GEP 元素成 i8、字段访问/赋值全部失效。仅真正的泛型类(list[T]/Vector[T]
# 等堆对象)未特化才回退 Ptr(i8)(AnnotClassName 携带 mangled name 供后续恢复)。
sub_g: ast.Subscript* = ast.Subscript*(aa.annotation)
is_carray_sub_g: int = 0
if sub_g is not None and sub_g.value is not None:
if sub_g.value.kind() == ast.ASTKind.Attribute:
svg_at: ast.Attribute* = ast.Attribute*(sub_g.value)
if svg_at.attr is not None and string.strcmp(svg_at.attr, "CArray") == 0:
is_carray_sub_g = 1
elif sub_g.value.kind() == ast.ASTKind.Name:
svg_nm: ast.Name* = ast.Name*(sub_g.value)
if svg_nm.id is not None and (string.strcmp(svg_nm.id, "CArray") == 0 or string.strcmp(svg_nm.id, "rawarray") == 0 or string.strcmp(svg_nm.id, "array") == 0):
is_carray_sub_g = 1
if is_carray_sub_g == 0:
is_generic_sub = 1
if is_generic_sub != 0:
# 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复
field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool))
else:
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,
# 静默 return 0 让多遍收敛机制生效(下一遍依赖类已注册即可成功)
if trans._declare_only == 1:
return 0
# Phase A(declare_only=0,Phase2.vp L573 多遍):依赖模块按
# 文件序可能晚于本模块翻译(如 TypeGenMixin.vp G < HandlesType.vp
# H,TraceFrames 注解的 HandlesType.TraceFrame 未注册)→ 不在本遍
# 以残缺字段注册结构体。
return 0
# 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时
# 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底)
# 存储到数组(指针转 CSizeT)
if field_count < GenTypes.FIELD_MAX:
diag_name_val: sizet = sizet(tgt.id)
field_names_buf[field_count] = diag_name_val
field_types_buf[field_count] = sizet(field_ty)
# 存储默认值 AST 指针(None=无默认值)
if aa.value is not None:
field_defaults_buf[field_count] = sizet(aa.value)
else:
field_defaults_buf[field_count] = 0
# 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体)
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]")
if aa.annotation is not None:
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(aa.annotation, trans._imported_modules, pool, trans._from_imports)
if annot_cn is not None:
diag_cn_val: sizet = sizet(annot_cn)
field_annot_buf[field_count] = diag_cn_val
else:
field_annot_buf[field_count] = 0
# t.CVolatile 存储类标记 → 该字段经对象访问的 load/store 带
# volatile(如 Spinlock.locked: t.CVolatile | t.CInt)。
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "CVolatile", 0) != 0:
field_volatile_buf[field_count] = 1
else:
field_volatile_buf[field_count] = 0
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*)→ 经该字段解引用(self.ptr[i]/self.ptr.field)
# 的 load/store 带 volatile。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0:
field_pointee_volatile_buf[field_count] = 1
else:
field_pointee_volatile_buf[field_count] = 0
# const 对象字段(注解含 t.CConst 且字段类型非指针)→ 经对象给该
# 字段赋值 fail-fast。判据与 TPC 的 Gen.field_const_flags 一致。
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0:
field_const_buf[field_count] = 1
else:
field_const_buf[field_count] = 0
else:
field_annot_buf[field_count] = 0
field_volatile_buf[field_count] = 0
field_pointee_volatile_buf[field_count] = 0
field_const_buf[field_count] = 0
field_count += 1
if field_count == 0:
# PhaseA 预注册时,父类可能尚未注册(字母序单遍扫描),
# 导致子类无法继承父类字段 → field_count==0。
# 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。
if trans._declare_only == 1 and parent_name is not None:
return 0
# 无字段类(接口/标记类):不能整体跳过——类可能只有方法
# (如多继承中的 ShapeM/Vec2Extra),需注册类并翻译方法。
# 空 struct 在 LLVM 中合法({});@t.CVTable 类由下方补 vtable 指针字段。
# ============================================================
# 2. 创建 LLVM StructType
# ============================================================
# 构建 ParamNode 链表
first_node: rawptr[llvmlite.ParamNode] = None
prev_node: rawptr[llvmlite.ParamNode] = None
total_field_count: int = field_count
# 如果有虚表,首字段添加 i8* vtable 指针
if has_vtable == 1:
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
vtable_node: rawptr[llvmlite.ParamNode] = pool.alloc(llvmlite.ParamNode.__sizeof__())
if vtable_node is None:
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
first_node = vtable_node
prev_node = vtable_node
total_field_count = field_count + 1
for fi in range(field_count):
fty_addr: sizet = field_types_buf[fi]
if fty_addr == 0:
continue
fty: llvmlite.LLVMType* = (llvmlite.LLVMType*)(fty_addr)
# 创建 ParamNode
pnode: rawptr[llvmlite.ParamNode] = pool.alloc(llvmlite.ParamNode.__sizeof__())
if pnode is None:
continue
string.memset(pnode, 0, llvmlite.ParamNode.__sizeof__())
pnode.Ty = fty
if first_node is None:
first_node = pnode
if prev_node is not None:
prev_node.Next = pnode
prev_node = pnode
# 构造命名结构体类型名: "sha1.ClassName" 或 "ClassName"
# 注意: Phase 1b(declare_only=0)时 struct_ty 会注册到全局表并被后续
# 文件翻译引用(vtable/self 参数类型打印),其 Name 必须跨 1b 每文件
# reset_to(mark1) 存活 → 用 stdlib 深拷贝;Phase 1a 仍用 pool(无需跨文件)。
type_name: str = class_name
if trans.ModuleSha1 is not None:
name_buf: str = pool.alloc(64)
if name_buf is not None:
viperlib.snprintf(name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
type_name = name_buf
if trans._declare_only == 0 and type_name is not None:
tn_len: sizet = string.strlen(type_name)
tn_copy: str = CompilerFactory.factory.PermPool.alloc(tn_len + 1)
if tn_copy is not None:
string.strcpy(tn_copy, type_name)
type_name = tn_copy
# 创建 StructType(命名结构体)
struct_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, first_node, total_field_count, type_name)
if struct_ty is None:
VLogger.error(f"failed to create StructType for {class_name}", "CLASS")
return 0
# Phase 1b(declare_only=0)新注册的 struct_ty 会被注册到全局表并被后续文件
# 翻译引用(vtable/self 参数类型打印/find_struct_by_type 指针匹配)。LLVMType
# 对象和 Fields 链是 pool 分配的,本文件 reset_to(mark1) 后悬空。深拷贝整棵树
# 到 stdlib,并替换 struct_ty 指针,保证 entry.Ty 与调用方持有的 struct_ty
# 指向同一个 stdlib 拷贝(指针匹配不失效)且跨文件永久存活。
# StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc(declare_only=1)
# 预注册 includes 时若仍用 pool struct_ty,reset_to(mark2) 后 entry.Ty 悬空 →
# 后续文件字段类型引用(resolve → Ptr(entry.Ty))→ opaque 扫描遍历悬空 fields
# 崩溃(LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
st_copy: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, struct_ty)
if st_copy is not None:
struct_ty = st_copy
type_name = llvmlite.get_struct_name(struct_ty)
# 前向声明占位命中:**原位填充**——保持 existing.Ty 指针身份不变,仅把刚构建
# 的字段体写进同一个类型对象(名称沿用占位的 "{sha}.Class")。此后所有已发出的
# Ptr(占位.Ty) 引用(如 CallGenBase.Gen)自动指向完整定义。这一步必须早于
# module_add_named_type:避免同一名字先以占位名注册、又以新对象重复注册。
if fill_fwd != 0 and existing is not None and existing.Ty is not None:
_fwd_name: str = llvmlite.get_struct_name(existing.Ty)
_fwd_fields: rawptr[llvmlite.ParamNode] = llvmlite.get_struct_fields(struct_ty)
_fwd_fc: int = llvmlite.get_struct_fcount(struct_ty)
*existing.Ty = llvmlite.LLVMType.Struct(_fwd_fields, _fwd_fc, _fwd_name)
struct_ty = existing.Ty
type_name = _fwd_name
existing.IsForwardDecl = 0
existing.FieldCount = 0
# 注册命名结构体到模块(输出 %"name" = type { ... } 定义行)
llvmlite.module_add_named_type(trans.Module, pool, struct_ty)
# B5:@c.Attribute(t.attr.packed) 打包结构体 → 类型定义输出 <{...}>
# (LLVM packed struct,无字段填充,GEP 按 packed 布局计算偏移)。
# idt/gdt/elf/fat32 等二进制格式结构体依赖它读写硬件/磁盘布局。
is_packed_cd: int = self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed")
if is_packed_cd != 0:
llvmlite.module_set_named_type_packed(trans.Module, pool, struct_ty, 1)
# 扫描字段类型,为跨模块结构体引用添加 opaque 声明
# 例如 list[int].__pool__ 引用 memhub.MemManager,需要在当前模块添加 %"sha1.MemManager" = type opaque
for fi in range(field_count):
fty_addr_op: sizet = field_types_buf[fi]
if fty_addr_op == 0:
continue
fty_op: llvmlite.LLVMType* = (llvmlite.LLVMType*)(fty_addr_op)
llvmlite.module_ensure_opaque_for_type(trans.Module, pool, fty_op)
# ============================================================
# 3. 注册到 HandlesStruct
# ============================================================
entry: GenTypes.StructEntry* = None
if fill_fwd != 0 and existing is not None:
# 前向声明占位:字段体已原位写入 existing.Ty,登记条目沿用 existing
# (不新建条目/Ty,否则已发出的 Ptr(占位.Ty) 引用会指向旧空对象)。
entry = existing
else:
entry = self.Gen.StructGen.register_struct(pool, class_name, struct_ty, trans.ModuleSha1)
if entry is None:
VLogger.error(f"failed to register {class_name}", "CLASS")
return 0
# B5:记录 packed 标记到 StructEntry(供 __sizeof__/字段布局等派生查询)
if is_packed_cd != 0:
entry.IsPacked = 1
# B5:记录 aligned 对齐值(@c.Attribute(t.attr.aligned(N)),如 fpu_context 16 对齐),
# 供全局变量/局部 alloca 生成时施加对齐。
align_cd: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
if align_cd > 0:
entry.Align = align_cd
# 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错)
if has_vtable == 1:
entry.HasVTable = 1
if is_novtable_deco == 1:
entry.IsNoVTable = 1
if parent_name is not None:
# 1b 阶段父类名是 pool 字符串,reset 后悬空;_translate_oop_methods/
# _detect_vtable_status 会在后续文件 find_struct(ParentName) strcmp 解引用
# → 用 stdlib 拷贝保证跨文件存活。
if self.Gen.StructGen.get_stdlib_alloc() != 0:
pn_len: sizet = string.strlen(parent_name)
pn_buf: str = CompilerFactory.factory.PermPool.alloc(pn_len + 1)
if pn_buf is not None:
string.strcpy(pn_buf, parent_name)
entry.ParentName = pn_buf
else:
entry.ParentName = parent_name
# 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活)
if parents_count > 0 and parents_buf is not None:
self.Gen.StructGen.set_parent_names(pool, class_name, parents_buf, parents_count)
# 添加字段信息
# 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始
if has_vtable == 1:
i8_ptr_ty2: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
for fi in range(field_count):
fname_addr: sizet = field_names_buf[fi]
if fname_addr == 0:
continue
fname: str = rawptr(fname_addr)
fty_addr2: sizet = field_types_buf[fi]
if fty_addr2 == 0:
continue
fty2: llvmlite.LLVMType* = (llvmlite.LLVMType*)(fty_addr2)
# 恢复默认值 AST 指针
fdef_addr: sizet = field_defaults_buf[fi]
fdef: ast.AST* = None
if fdef_addr != 0:
fdef = (ast.AST*)(fdef_addr)
# 恢复注解类名指针
fannot_addr: sizet = field_annot_buf[fi]
fannot: str = None
if fannot_addr != 0:
fannot = str(fannot_addr)
if fname is not None and fty2 is not None:
fidx_add: int = self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
# t.CVolatile 字段标记(field_volatile_buf[fi] 与 add_field 顺序一一对应)
if fidx_add >= 0 and field_volatile_buf[fi] != 0:
fe_vol: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_vol is not None:
fe_vol.IsVolatile = 1
# rawptr[t.CVolatile | T] 字段标记(field_pointee_volatile_buf[fi] 与
# add_field 顺序一一对应):经该字段解引用时 load/store 带 volatile。
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
fe_pv: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_pv is not None:
fe_pv.PointeeVolatile = 1
# const 对象字段标记(field_const_buf[fi] 与 add_field 顺序一一对应)
if fidx_add >= 0 and field_const_buf[fi] != 0:
fe_c: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_c is not None:
fe_c.IsConst = 1
# Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体
if trans._declare_only == 1:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1)
return 0
# ============================================================
# 4. OOP 方法处理:扫描 FunctionDef,标记 OOP,翻译方法,生成 __before_init__
# ============================================================
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name)
return 0
# ============================================================
# _translate_nested_classdefs - 递归翻译类体内的嵌套 class / struct
#
# 命名与引用口径(用户 2026-10-01 定,与 TPC union/renum 嵌套命名同源):
# · 内层结构体 IR 名 = `{sha1}.{外类}_{内类}`(sha1 = 与外层相同的定义模块);
# · 引用两种写法都可:限定名 `外类.内类`(Attribute 兜底,见 TypeGenResolve)
# 与扁平名 `外类_内类`(裸名直查结构体表即可命中);
# · 多层嵌套自然递归为 `A_B_C`(递归时子节点名已被改写为 A_B)。
# 实现选择"临时改写子节点 name 再递归"而非另写发射器:本模块所有名字派生点
# (IR 名 {sha}.{名}、register_struct/add_field、AnnotClassName、方法符号名
# `{类}.{方法}` 的 mangling、mark_module_class 键)都读 cd.name ⇒ 改名即全链路
# 一致,且复用继承展平/packed/OOP/vtable 等全部既有逻辑,零新增判据。
# 两个必须的护栏:
# ① 扁平名字符串分配在 PermPool —— 它要跨 Phase(1a/1b)存活,trans.Pool 会随
# mark/reset 回收成悬空串(后续 strcmp 读悬空内存)。
# ② 递归返回后**还原"正在定义类"窗口**(CurDefModuleSha1/CurDefClassName 会被
# 内层 mark_module_class 覆盖)——不还原会让外层随后的字段注解解析误判
# "当前定义类"(规则 5.5 的自引用窗口据此判定,误判会冻结裸名票名)。
# ============================================================
def _translate_nested_classdefs(self, trans: HT.Translator*,
cd: ast.ClassDef*) -> None:
"""把本类体内的嵌套 ClassDef 逐个按 `{外类}_{内类}` 递归翻译"""
if trans is None or cd is None or cd.name is None:
return
children: list[ast.AST*]* = cd.children
if children is None:
return
cn: sizet = len(children)
ci: sizet = 0
while ci < cn:
child_ast: ast.AST* = children.get(ci)
ci += 1
if child_ast is None:
continue
if child_ast.kind() != ast.ASTKind.ClassDef:
continue
child: ast.ClassDef* = ast.ClassDef*(child_ast)
if child is None or child.name is None:
continue
# 扁平名 `{外类}_{内类}`(优先 PermPool:名字要跨 Phase 存活;
# PermPool 缺失时退回本遍池,行为与旧路径一致)
flat_pool: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if flat_pool is None:
flat_pool = trans.Pool
if flat_pool is None:
continue
outer_len: sizet = string.strlen(cd.name)
inner_len: sizet = string.strlen(child.name)
flat_len: sizet = outer_len + 1 + inner_len
flat_buf: str = flat_pool.alloc(flat_len + 1)
if flat_buf is None:
continue
viperlib.snprintf(flat_buf, flat_len + 1, "%s_%s", cd.name, child.name)
# 改写子节点名 → 递归(内部一律以扁平名注册/发射)
old_name: str = child.name
child.name = flat_buf
saved_def_sha1: str = self.Gen.StructGen.CurDefModuleSha1
saved_def_cls: str = self.Gen.StructGen.CurDefClassName
self.translate_class_def(trans, child_ast)
self.Gen.StructGen.CurDefModuleSha1 = saved_def_sha1
self.Gen.StructGen.CurDefClassName = saved_def_cls
child.name = old_name
File diff suppressed because it is too large. Load diff
@@ -2,40 +2,29 @@ import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.ClassGenBase as ClassGenBase
from . import ClassGenBase
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# 泛型特化递归深度上限(泛型循环套用防护)
# 正常嵌套特化深度 ≤ 3(如 list[list[str]]);64 远超合理值,命中即循环。
# (原 HandlesClassDef/ClassGenMixin 顶层常量,阶段 D 拆分时遗漏,补回)
GENERIC_SPEC_MAX_DEPTH: t.CDefine = 64
define GENERIC_SPEC_MAX_DEPTH = 64
# 泛型模板表容量上限(与 GenericClassNames 等 pool 表可容纳的模板数对齐;
# 原 ClassGenMixin 顶层常量,阶段 D 拆分时遗漏,补回)
GENERIC_TEMPLATE_MAX: t.CDefine = 512
# 泛型特化缓存表容量(原 HandlesClassDef.SPEC_CACHE_MAX,阶段 D 拆分遗漏)
SPEC_CACHE_MAX: t.CDefine = 512
define GENERIC_TEMPLATE_MAX = 512
# 泛型特化缓存表容量
define SPEC_CACHE_MAX = 512
# ============================================================
# ClassGenGeneric - ClassGenMixin 拆分(阶段 D):泛型模板注册/特化/克隆
#
# 继承 ClassGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesClassDef 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ClassGenGeneric - 泛型模板注册/特化/克隆
# ============================================================
@t.NoVTable
class ClassGenGeneric(ClassGenBase.ClassGenBase):
novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
"""泛型模板注册/特化/克隆"""
# ============================================================
@@ -48,47 +37,82 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# - _spec_* 全局缓存迁移为 self.SpecKeys/SpecNames/SpecCount
# ============================================================
def _is_generic_class(self, tp: list[str] | t.CPtr) -> int:
def _is_generic_class(self, tp: list[str]*) -> int:
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否
接收 type_params 而非 cd,避免 cd.type_params 属性访问
因跨模块类型推断失败返回 None。
"""
if tp is None:
return 0
if tp is None: return 0
return 1
def _register_generic_template(self, pool: memhub.MemBuddy | t.CPtr,
cd: ast.ClassDef | t.CPtr,
def _dup_perm_str(self, s: str) -> str:
"""深拷贝字符串到永久池(无永久池/分配失败时原样返回入参)"""
if s is None: return None
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None: return s
n: sizet = string.strlen(s)
b: str = perm.alloc(n + 1)
if b is None: return s
string.strcpy(b, s)
return b
def _register_generic_template(self, pool: memhub.MemBuddy*,
cd: ast.ClassDef*,
module_sha1: str) -> None:
"""注册泛型类模板(类名 + ClassDef 节点 + 来源模块 SHA1)"""
"""注册泛型类模板(类名 + ClassDef 节点 + 来源模块 SHA1)
[池归属修复] 这四张表 + GenericClassTpParams 的语义都是**永久表**:
批量编译下要在 Phase1 的 reset_to(mark1) 与 Phase2 的 reset_to(mark2) 之后
继续可读(读点见 _find_generic_template/_find_generic_template_sha1/
_find_generic_template_file/_find_generic_template_tp_params,均有"遍历
悬空内存 strcmp 即崩溃"的记录),REPL 下还要跨片段存活。但注册点有三个:
- Phase1Scan.vp:322 → perm(永久池);
- Phase2/__init__.vp:321 → perm(永久池);
- ClassGenDef.vp:78 → trans.Pool(**会被 reset_to 回收的池**)。
于是"表在哪个池"取决于哪条路径先注册到表:批量编译里两遍预注册
(_PreRegisterAllGenericTemplates / _PreRegisterAppGenericTemplates)在前,
表被建在永久池 ⇒ 第三条路径只是追加;而两遍预注册都没建成表时(例如
includes 的泛型 marker 文本预筛未命中),第三条路径把表建在 trans.Pool 上,
随 mark 回收 ⇒ 后续 _find_generic_template 遍历悬空表(UAF)。REPL 从不跑
Phase1/Phase2,第三条路径是**唯一**注册点 ⇒ 表必然落在会被每片段 reset 的
池上,M4 一旦在片段里定义泛型类就是必现 UAF。
故此处统一:表体一律用永久池(无永久池时退回入参池,即旧行为),并把
调用方池里的三个串(类名/SHA1/来源文件)深拷贝进永久池——它们同样会随
调用方池回收而悬空(挂名 strcmp 即读悬空内存)。
批量编译不变量:变更只影响"表与串分配在哪个池",写入的**内容**逐字不变
(同样的类名/节点/ SHA1/文件路径,同样的插入顺序与查重),发射的 IR 与
符号名不受池选择影响 ⇒ 逐字不变。
"""
if cd is None or cd.name is None: return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
table_pool: memhub.MemBuddy* = perm
if table_pool is None:
table_pool = pool
if self.GenericClassNames is None:
self.GenericClassNames = list[str](pool, 8)
self.GenericClassNodes = list[ast.ClassDef | t.CPtr](pool, 8)
self.GenericClassSha1s = list[str](pool, 8)
self.GenericClassFiles = list[str](pool, 8)
self.GenericClassNames = list[str](table_pool, 8)
self.GenericClassNodes = list[ast.ClassDef*](table_pool, 8)
self.GenericClassSha1s = list[str](table_pool, 8)
self.GenericClassFiles = list[str](table_pool, 8)
if self.GenericClassTpParams is None:
self.GenericClassTpParams = CompilerFactory.get_perm_pool().alloc(GENERIC_TEMPLATE_MAX * 8)
if self.GenericClassTpParams is None:
return
if perm is None: return
self.GenericClassTpParams = perm.alloc(GENERIC_TEMPLATE_MAX * 8)
if self.GenericClassTpParams is None: return
string.memset(self.GenericClassTpParams, 0, GENERIC_TEMPLATE_MAX * 8)
# 用局部变量接收 list,触发类型推断(避免字段 __len__ 失败)
names_local: list[str] | t.CPtr = self.GenericClassNames
cn: t.CSizeT = len(names_local)
i: t.CSizeT
names_local: list[str]* = self.GenericClassNames
cn: sizet = len(names_local)
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, cd.name) == 0:
return
if nm is not None and string.strcmp(nm, cd.name) == 0: return
# type_params stdlib 深拷贝:模板节点的 type_params 是 pool 分配的
tp_names_buf: str = self._copy_type_params_stdlib(cd.type_params)
self.GenericClassNames.append(cd.name)
self.GenericClassNames.append(self._dup_perm_str(cd.name))
self.GenericClassNodes.append(cd)
self.GenericClassSha1s.append(module_sha1)
self.GenericClassSha1s.append(self._dup_perm_str(module_sha1))
# 同时保存模板来源文件路径(错误定位用,见字段注释)
self.GenericClassFiles.append(self.Gen.TypeGenResolve.get_current_file())
self.GenericClassFiles.append(self._dup_perm_str(self.Gen.TypeGenResolve.get_current_file()))
if tp_names_buf is not None and cn < GENERIC_TEMPLATE_MAX:
self.GenericClassTpParams[cn] = t.CSizeT(tp_names_buf)
def _copy_type_params_stdlib(self, tp: list[str] | t.CPtr) -> str:
self.GenericClassTpParams[cn] = sizet(tp_names_buf)
def _copy_type_params_stdlib(self, tp: list[str]*) -> str:
"""将模板 type_params 深拷贝为空格分隔、NUL 结尾的 stdlib 缓冲区
TPC Python 参考实现:type_params = [tp.name for tp in Node.type_params]
@@ -98,17 +122,17 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
"""
if tp is None:
return None
tp_n: t.CSizeT = len(tp)
tp_n: sizet = len(tp)
if tp_n == 0:
return None
# 计算总长:各名字长度 + (n-1) 个空格 + NUL
total: t.CSizeT = 1
ti: t.CSizeT
total: sizet = 1
ti: sizet
for ti in range(tp_n):
tp_name: str = tp.get(ti)
if tp_name is not None:
total += string.strlen(tp_name) + 1
buf: str = CompilerFactory.get_perm_pool().alloc(total)
buf: str = CompilerFactory.factory.PermPool.alloc(total)
if buf is None:
return None
buf[0] = '\0'
@@ -120,8 +144,8 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
string.strcat(buf, " ")
string.strcat(buf, tp_name)
return buf
def _find_generic_template_tp_params(self, pool: memhub.MemBuddy | t.CPtr,
class_name: str) -> list[str] | t.CPtr:
def _find_generic_template_tp_params(self, pool: memhub.MemBuddy*,
class_name: str) -> list[str]*:
"""查找泛型类模板的类型参数名列表,未找到返回 None
从注册时深拷贝的 stdlib 缓冲区重建当前 pool 的 list[str]
@@ -130,32 +154,32 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if self.GenericClassNames is None or class_name is None \
or self.GenericClassTpParams is None:
return None
names_local: list[str] | t.CPtr = self.GenericClassNames
cn: t.CSizeT = len(names_local)
i: t.CSizeT
names_local: list[str]* = self.GenericClassNames
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
if i >= GENERIC_TEMPLATE_MAX:
return None
buf_addr: t.CSizeT = self.GenericClassTpParams[i]
buf_addr: sizet = self.GenericClassTpParams[i]
if buf_addr == 0:
return None
buf_tp: str = (str | t.CPtr)(t.CVoid(buf_addr, t.CPtr))
buf_tp: str = rawptr(buf_addr)
# 空格分隔 → list[str]
# 注意:list[str](pool, 8) 第二个参数是 elem_size(指针 8 字节),
# 不是容量!传 4 会导致 get(i) 偏移算错、append 相互覆盖
# (Pair[T1,T2] 的 seg1 写 data+4 覆盖 seg0 高 4 字节 → strcmp
# 收到 0x15B78D5015B78CB0 混合垃圾崩溃,GenericTest 回归根因)
tp_list: list[str] | t.CPtr = list[str](pool, 8)
tp_list: list[str]* = list[str](pool, 8)
if tp_list is None:
return None
seg_start: t.CSizeT = 0
pos: t.CSizeT = 0
blen: t.CSizeT = string.strlen(buf_tp)
seg_start: sizet = 0
pos: sizet = 0
blen: sizet = string.strlen(buf_tp)
while pos <= blen:
if buf_tp[pos] == ' ' or buf_tp[pos] == '\0':
seg_len: t.CSizeT = pos - seg_start
seg_len: sizet = pos - seg_start
if seg_len > 0:
seg: str = pool.alloc(seg_len + 1)
if seg is not None:
@@ -166,19 +190,19 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
pos += 1
return tp_list
return None
def _find_generic_template(self, class_name: str) -> ast.ClassDef | t.CPtr:
def _find_generic_template(self, class_name: str) -> ast.ClassDef*:
"""查找泛型类模板,返回 ClassDef 节点或 None"""
if self.GenericClassNames is None or class_name is None:
return None
# 用局部变量接收 list,触发类型推断(避免字段 __len__ 失败)
names_local: list[str] | t.CPtr = self.GenericClassNames
nodes_local: list[ast.ClassDef | t.CPtr] | t.CPtr = self.GenericClassNodes
cn: t.CSizeT = len(names_local)
i: t.CSizeT
names_local: list[str]* = self.GenericClassNames
nodes_local: list[ast.ClassDef*] | t.CPtr = self.GenericClassNodes
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
node: ast.ClassDef | t.CPtr = nodes_local.get(i)
node: ast.ClassDef* = nodes_local.get(i)
return node
return None
def _find_generic_template_sha1(self, class_name: str) -> str:
@@ -186,10 +210,10 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if self.GenericClassNames is None or class_name is None:
return None
# 用局部变量接收 list,触发类型推断
names_local: list[str] | t.CPtr = self.GenericClassNames
sha1s_local: list[str] | t.CPtr = self.GenericClassSha1s
cn: t.CSizeT = len(names_local)
i: t.CSizeT
names_local: list[str]* = self.GenericClassNames
sha1s_local: list[str]* = self.GenericClassSha1s
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
@@ -204,35 +228,61 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
"""
if self.GenericClassNames is None or class_name is None:
return None
names_local: list[str] | t.CPtr = self.GenericClassNames
files_local: list[str] | t.CPtr = self.GenericClassFiles
cn: t.CSizeT = len(names_local)
i: t.CSizeT
names_local: list[str]* = self.GenericClassNames
files_local: list[str]* = self.GenericClassFiles
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
f_val: str = files_local.get(i)
return f_val
return None
def _mangle_generic_class_name(self, pool: memhub.MemBuddy | t.CPtr,
def _mangle_generic_class_name(self, pool: memhub.MemBuddy*,
class_name: str,
type_args: list[str] | t.CPtr) -> str:
type_args: list[str]*,
from_imports: str = None) -> str:
"""生成特化类名: list + [int] -> list[int]
**唯一生成器 + 唯一规范化点**:所有特化名(结构体注册名、方法符号里的
后缀段、查找键)都由这里产出,故类型实参的 SHA1 规范化只在这里做一次 ——
每个实参经 `TypeGenResolve._norm_type_arg_name` 归一到
`{定义模块SHA1}.{Class}`(该函数对已规范化的串**幂等**:规则 2/3/6 原样
返回,嵌套 `rawptr[X]` 也递归同形)⇒ 无论调用方(注解侧 / 构造侧 /
基类侧 / 字面量推断侧)传进来的实参是裸名还是已带前缀,注册名与查找名
**必然同形**。此前裸露名与已带前缀的两侧会分裂成两个特化结构体,已出现
三种现场:形参侧/实参侧(stmts.vp:60)、字段侧/构造侧(base.vp:357)、
基类侧/注解侧(ClassGenBase._get_base_name 的裸 Name.id)。
分配新缓冲区拼接,避免原地 strcat 导致缓冲区溢出
"""
if class_name is None or type_args is None or pool is None:
return class_name
n: t.CSizeT = len(type_args)
n: sizet = len(type_args)
if n == 0:
return class_name
# 逐实参规范化(幂等):先归一成新列表,长度计算与拼接都用归一后的串
norm_args: list[str]* = list[str](pool, 8)
if norm_args is None:
return class_name
i: sizet
for i in range(n):
ta_raw: str = type_args.get(i)
if ta_raw is None:
continue
ta_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, ta_raw, from_imports)
if ta_norm is None:
ta_norm = ta_raw
norm_args.append(ta_norm)
n = len(norm_args)
if n == 0:
return class_name
# 计算总长度: class_name + "[" + arg0 + "," + arg1 + ... + "]" + NUL
total_len: t.CSizeT = string.strlen(class_name) + 2 # "[" 和 "]"
i: t.CSizeT
total_len: sizet = string.strlen(class_name) + 2 # "[" 和 "]"
for i in range(n):
if i > 0:
total_len += 1 # ","
ta: str = type_args.get(i)
ta: str = norm_args.get(i)
if ta is not None:
total_len += string.strlen(ta)
total_len += 1 # NUL
@@ -246,15 +296,15 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
for i in range(n):
if i > 0:
string.strcat(mangled, ",")
ta: str = type_args.get(i)
ta: str = norm_args.get(i)
if ta is not None:
string.strcat(mangled, ta)
string.strcat(mangled, "]")
return mangled
def _clone_annotation(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
tp_names: list[str] | t.CPtr,
type_args: list[str] | t.CPtr) -> ast.AST | t.CPtr:
def _clone_annotation(self, pool: memhub.MemBuddy*,
node: ast.AST*,
tp_names: list[str]*,
type_args: list[str]*) -> ast.AST*:
"""拷贝类型注解表达式,替换类型参数 T 为具体类型名
tp_names: 类型参数名列表 ['T']
@@ -266,97 +316,97 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# Name: 可能是类型参数 T
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
n: t.CSizeT = len(tp_names) if tp_names is not None else 0
i: t.CSizeT
n: sizet = len(tp_names) if tp_names is not None else 0
i: sizet
for i in range(n):
tpn: str = tp_names.get(i)
if tpn is not None and string.strcmp(nm.id, tpn) == 0:
ta: str = type_args.get(i)
# 类型实参可能是复合类型(如 "AST|t.CPtr"),用 _parse_type_string 解析
parsed_node: ast.AST | t.CPtr = self.Gen.TypeGenResolve._parse_type_string(pool, ta, nm.ctx)
parsed_node: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, ta, nm.ctx)
return parsed_node
return ast.Name(pool, nm.id, nm.ctx)
return None
# Attribute: t.CPtr 等
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
new_value: ast.AST | t.CPtr = self._clone_annotation(pool, at.value, tp_names, type_args)
at: ast.Attribute* = ast.Attribute*(node)
new_value: ast.AST* = self._clone_annotation(pool, at.value, tp_names, type_args)
return ast.Attribute(pool, new_value, at.attr, at.ctx)
# BinOp: T | t.CPtr 等
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
new_left: ast.AST | t.CPtr = self._clone_annotation(pool, bop.left, tp_names, type_args)
new_right: ast.AST | t.CPtr = self._clone_annotation(pool, bop.right, tp_names, type_args)
bop: ast.BinOp* = ast.BinOp*(node)
new_left: ast.AST* = self._clone_annotation(pool, bop.left, tp_names, type_args)
new_right: ast.AST* = self._clone_annotation(pool, bop.right, tp_names, type_args)
return ast.BinOp(pool, new_left, bop.op, new_right)
# Subscript: list[int] 等
if k == ast.ASTKind.Subscript:
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
new_value2: ast.AST | t.CPtr = self._clone_annotation(pool, sub.value, tp_names, type_args)
new_slice: ast.AST | t.CPtr = self._clone_annotation(pool, sub.slice, tp_names, type_args)
sub: ast.Subscript* = ast.Subscript*(node)
new_value2: ast.AST* = self._clone_annotation(pool, sub.value, tp_names, type_args)
new_slice: ast.AST* = self._clone_annotation(pool, sub.slice, tp_names, type_args)
return ast.Subscript(pool, new_value2, new_slice, sub.ctx)
# Tuple: (T, int) 等
if k == ast.ASTKind.Tuple:
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
new_elts: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8)
tup: ast.Tuple* = ast.Tuple*(node)
new_elts: list[ast.AST*]* = list[ast.AST*](pool, 8)
if tup.elts is not None:
tup_elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts
en: t.CSizeT = len(tup_elts)
ei: t.CSizeT
tup_elts: list[ast.AST*]* = tup.elts
en: sizet = len(tup_elts)
ei: sizet
for ei in range(en):
el: ast.AST | t.CPtr = tup_elts.get(ei)
new_el: ast.AST | t.CPtr = self._clone_annotation(pool, el, tp_names, type_args)
el: ast.AST* = tup_elts.get(ei)
new_el: ast.AST* = self._clone_annotation(pool, el, tp_names, type_args)
if new_el is not None:
new_elts.append(new_el)
return ast.Tuple(pool, new_elts, tup.ctx)
# Constant: 默认值等
if k == ast.ASTKind.Constant:
cnst: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
cnst: ast.Constant* = ast.Constant*(node)
return ast.Constant(pool, cnst.const_kind, cnst.int_val, cnst.float_val,
cnst.str_val, cnst.lineno, cnst.col_offset)
# 其他: 直接引用(不拷贝)
return node
def _clone_arguments(self, pool: memhub.MemBuddy | t.CPtr,
args: ast.AST | t.CPtr,
tp_names: list[str] | t.CPtr,
type_args: list[str] | t.CPtr) -> ast.AST | t.CPtr:
def _clone_arguments(self, pool: memhub.MemBuddy*,
args: ast.AST*,
tp_names: list[str]*,
type_args: list[str]*) -> ast.AST*:
"""拷贝 Arguments 节点,替换 arg annotation 中的 T"""
if args is None:
return None
if args.kind() != ast.ASTKind.Arguments:
return args
old_args: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args)
old_args: ast.Arguments* = ast.Arguments*(args)
# 拷贝 args 列表,替换每个 Arg 的 annotation
new_arg_list: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8)
new_arg_list: list[ast.AST*]* = list[ast.AST*](pool, 8)
if old_args.args is not None:
old_arg_list: list[ast.AST | t.CPtr] | t.CPtr = old_args.args
an: t.CSizeT = len(old_arg_list)
ai: t.CSizeT
old_arg_list: list[ast.AST*]* = old_args.args
an: sizet = len(old_arg_list)
ai: sizet
for ai in range(an):
arg_node: ast.AST | t.CPtr = old_arg_list.get(ai)
arg_node: ast.AST* = old_arg_list.get(ai)
if arg_node is None:
continue
if arg_node.kind() == ast.ASTKind.Arg:
ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(arg_node)
new_anno: ast.AST | t.CPtr = self._clone_annotation(pool, ag.annotation, tp_names, type_args)
new_arg: ast.Arg | t.CPtr = ast.Arg(pool, ag.arg, new_anno)
ag: ast.Arg* = ast.Arg*(arg_node)
new_anno: ast.AST* = self._clone_annotation(pool, ag.annotation, tp_names, type_args)
new_arg: ast.Arg* = ast.Arg(pool, ag.arg, new_anno)
new_arg_list.append(new_arg)
else:
new_arg_list.append(arg_node)
return ast.Arguments(pool, new_arg_list, old_args.vararg, old_args.kwarg,
old_args.defaults, old_args.kw_defaults)
def _clone_classdef_for_spec(self, pool: memhub.MemBuddy | t.CPtr,
cd: ast.ClassDef | t.CPtr,
def _clone_classdef_for_spec(self, pool: memhub.MemBuddy*,
cd: ast.ClassDef*,
spec_name: str,
tp_names: list[str] | t.CPtr,
type_args: list[str] | t.CPtr) -> ast.ClassDef | t.CPtr:
tp_names: list[str]*,
type_args: list[str]*) -> ast.ClassDef*:
"""拷贝 ClassDef 节点用于泛型特化
- 替换类名为 spec_name
@@ -365,40 +415,40 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
- 方法体直接引用原始节点(通过 type_map 上下文处理 T)
"""
# 创建新 ClassDef(type_params=None 表示非泛型)
new_cd: ast.ClassDef | t.CPtr = ast.ClassDef(pool, spec_name, cd.bases, cd.keywords,
new_cd: ast.ClassDef* = ast.ClassDef(pool, spec_name, cd.bases, cd.keywords,
cd.decorator_list, None)
# 拷贝 children
if cd.children is not None:
cd_children: list[ast.AST | t.CPtr] | t.CPtr = cd.children
cn: t.CSizeT = len(cd_children)
ci: t.CSizeT
cd_children: list[ast.AST*]* = cd.children
cn: sizet = len(cd_children)
ci: sizet
for ci in range(cn):
child: ast.AST | t.CPtr = cd_children.get(ci)
child: ast.AST* = cd_children.get(ci)
if child is None:
continue
ck: int = child.kind()
if ck == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child)
new_target: ast.AST | t.CPtr = aa.target
new_anno2: ast.AST | t.CPtr = self._clone_annotation(pool, aa.annotation, tp_names, type_args)
new_value3: ast.AST | t.CPtr = aa.value
new_aa: ast.AnnAssign | t.CPtr = ast.AnnAssign(pool, new_target, new_anno2, new_value3, aa.simple)
aa: ast.AnnAssign* = ast.AnnAssign*(child)
new_target: ast.AST* = aa.target
new_anno2: ast.AST* = self._clone_annotation(pool, aa.annotation, tp_names, type_args)
new_value3: ast.AST* = aa.value
new_aa: ast.AnnAssign* = ast.AnnAssign(pool, new_target, new_anno2, new_value3, aa.simple)
new_cd.append(new_aa)
elif ck == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
new_args2: ast.AST | t.CPtr = self._clone_arguments(pool, fd.args, tp_names, type_args)
new_returns2: ast.AST | t.CPtr = self._clone_annotation(pool, fd.returns, tp_names, type_args)
new_fd: ast.FunctionDef | t.CPtr = ast.FunctionDef(pool, fd.name, new_args2,
fd: ast.FunctionDef* = ast.FunctionDef*(child)
new_args2: ast.AST* = self._clone_arguments(pool, fd.args, tp_names, type_args)
new_returns2: ast.AST* = self._clone_annotation(pool, fd.returns, tp_names, type_args)
new_fd: ast.FunctionDef* = ast.FunctionDef(pool, fd.name, new_args2,
fd.decorator_list, new_returns2, 0)
# 方法体直接引用原始节点
if fd.children is not None:
fd_children: list[ast.AST | t.CPtr] | t.CPtr = fd.children
bcn: t.CSizeT = len(fd_children)
bci: t.CSizeT
fd_children: list[ast.AST*]* = fd.children
bcn: sizet = len(fd_children)
bci: sizet
for bci in range(bcn):
body_stmt: ast.AST | t.CPtr = fd_children.get(bci)
body_stmt: ast.AST* = fd_children.get(bci)
if body_stmt is not None:
new_fd.append(body_stmt)
new_cd.append(new_fd)
@@ -413,21 +463,21 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
return None
i: int
for i in range(self.SpecCount):
key_addr: t.CSizeT = self.SpecKeys[i]
key_addr: sizet = self.SpecKeys[i]
if key_addr != 0:
k: str = (str | t.CPtr)(t.CVoid(key_addr, t.CPtr))
k: str = rawptr(key_addr)
if k is not None and string.strcmp(k, spec_key) == 0:
name_addr: t.CSizeT = self.SpecNames[i]
name_addr: sizet = self.SpecNames[i]
if name_addr != 0:
return (str | t.CPtr)(t.CVoid(name_addr, t.CPtr))
return rawptr(name_addr)
return None
def _cache_spec(self, pool: memhub.MemBuddy | t.CPtr, spec_key: str, spec_name: str):
def _cache_spec(self, pool: memhub.MemBuddy*, spec_key: str, spec_name: str):
"""缓存特化结果(stdlib 深拷贝存储,跨文件安全)"""
if spec_key is None or spec_name is None:
return
if self.SpecKeys is None:
self.SpecKeys = CompilerFactory.get_perm_pool().alloc(SPEC_CACHE_MAX * 8)
self.SpecNames = CompilerFactory.get_perm_pool().alloc(SPEC_CACHE_MAX * 8)
self.SpecKeys = CompilerFactory.factory.PermPool.alloc(SPEC_CACHE_MAX * 8)
self.SpecNames = CompilerFactory.factory.PermPool.alloc(SPEC_CACHE_MAX * 8)
if self.SpecKeys is None or self.SpecNames is None:
return
string.memset(self.SpecKeys, 0, SPEC_CACHE_MAX * 8)
@@ -436,18 +486,18 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if self.SpecCount >= SPEC_CACHE_MAX:
return
# 深拷贝字符串到 stdlib 区
key_len: t.CSizeT = string.strlen(spec_key)
name_len: t.CSizeT = string.strlen(spec_name)
key_copy: str = CompilerFactory.get_perm_pool().alloc(key_len + 1)
name_copy: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
key_len: sizet = string.strlen(spec_key)
name_len: sizet = string.strlen(spec_name)
key_copy: str = CompilerFactory.factory.PermPool.alloc(key_len + 1)
name_copy: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if key_copy is None or name_copy is None:
return
string.strcpy(key_copy, spec_key)
string.strcpy(name_copy, spec_name)
self.SpecKeys[self.SpecCount] = t.CSizeT(key_copy)
self.SpecNames[self.SpecCount] = t.CSizeT(name_copy)
self.SpecKeys[self.SpecCount] = sizet(key_copy)
self.SpecNames[self.SpecCount] = sizet(name_copy)
self.SpecCount += 1
def _merge_template_imports(self, trans: HT.Translator | t.CPtr,
def _merge_template_imports(self, trans: HT.Translator*,
template_sha1: str) -> None:
"""将模板模块的 imports 并入 trans._imported_modules(空格分隔)"""
if trans is None or template_sha1 is None:
@@ -459,8 +509,8 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if cur is None or cur[0] == '\0':
trans._imported_modules = tdeps
return
cur_len: t.CSizeT = string.strlen(cur)
td_len: t.CSizeT = string.strlen(tdeps)
cur_len: sizet = string.strlen(cur)
td_len: sizet = string.strlen(tdeps)
merged: str = trans.Pool.alloc(cur_len + td_len + 2)
if merged is None:
return
@@ -468,7 +518,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
merged[cur_len] = ' '
string.strcpy(merged + cur_len + 1, tdeps)
trans._imported_modules = merged
def _record_spec_trigger(self, pool: memhub.MemBuddy | t.CPtr,
def _record_spec_trigger(self, pool: memhub.MemBuddy*,
spec_name: str, trigger_sha1: str) -> int:
"""记录泛型特化类的触发文件 sha1(StructEntry.SpecTriggerSha1)
@@ -479,21 +529,21 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
"""
if spec_name is None or trigger_sha1 is None:
return 0
entry_rt: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
entry_rt: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if entry_rt is None:
return 0
if string.strcmp(entry_rt.Name, spec_name) != 0:
return 0
ts_len_r: t.CSizeT = string.strlen(trigger_sha1)
ts_buf_r: str = CompilerFactory.get_perm_pool().alloc(ts_len_r + 1)
ts_len_r: sizet = string.strlen(trigger_sha1)
ts_buf_r: str = CompilerFactory.factory.PermPool.alloc(ts_len_r + 1)
if ts_buf_r is None:
return 0
string.strcpy(ts_buf_r, trigger_sha1)
entry_rt.SpecTriggerSha1 = ts_buf_r
return 1
def _specialize_generic_class(self, trans: HT.Translator | t.CPtr,
def _specialize_generic_class(self, trans: HT.Translator*,
class_name: str,
type_args: list[str] | t.CPtr) -> str:
type_args: list[str]*) -> str:
"""特化泛型类,返回特化类名
1. 查找泛型模板
@@ -512,7 +562,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 特化触发文件 sha1:进入函数时 trans.ModuleSha1 是触发方文件,
# 翻译特化类时会被切换为模板 sha1(见下方),作废重译时按此重置
# 特化类 MethodsEmitted(见 _record_spec_trigger / reset_module_methods_emitted)
spec_trigger_sha1: str = trans.ModuleSha1
spec_trigger_sha1: str = trans.ModuleSha1
if self.GenericSpecDepth > GENERIC_SPEC_MAX_DEPTH:
err_spec: str = trans.Pool.alloc(256)
if err_spec is not None:
@@ -524,8 +574,8 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 防御:泛型上下文内收集的 type_args 若残留未替换的类型参数名(如 "T"),
# 统一替换为具体类型名,避免 ndarray[T] 二次递归特化崩溃。
# 调用方传入的 type_args 均为临时新建列表,就地替换无共享副作用。
ta_cnt_g: t.CSizeT = len(type_args)
ta_i_g: t.CSizeT
ta_cnt_g: sizet = len(type_args)
ta_i_g: sizet
for ta_i_g in range(ta_cnt_g):
ta_raw_g: str = type_args.get(ta_i_g)
if ta_raw_g is not None:
@@ -533,27 +583,29 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if ta_rsv_g is not None and string.strcmp(ta_raw_g, ta_rsv_g) != 0:
type_args.set(ta_i_g, ta_rsv_g)
pool: memhub.MemBuddy | t.CPtr = trans.Pool
pool: memhub.MemBuddy* = trans.Pool
# 1. 查找泛型模板
template_cd: ast.ClassDef | t.CPtr = self._find_generic_template(class_name)
template_cd: ast.ClassDef* = self._find_generic_template(class_name)
if template_cd is None:
self.GenericSpecDepth -= 1
return None
# 2. 生成特化名
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args)
# 2. 生成特化名(唯一生成器内部逐实参规范化:裸名/带前缀在此收敛为同一名)
_from_imports: str = None
if trans is not None:
_from_imports = trans._from_imports
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args, _from_imports)
if spec_name is None:
self.GenericSpecDepth -= 1
return None
# 治本修复:Phase B 补译泛型特化类方法体(统一入口,在 cache/existing 检查之前)
# Phase 1a(declare_only==1)特化时只注册结构体不译方法体(MethodsEmitted==0),
# Phase B(declare_only==0)重复进入时若方法体未译,补译方法体生成 define,
# 避免 GSList[Line].append 等符号只有 declare 无 define → 链接失败。
# Phase B 补译泛型特化类方法体(统一入口,在 cache/existing 检查之前):Phase 1a
# 特化时只注册结构体不译方法体(MethodsEmitted==0),Phase B 重复进入时补译以生成
# define,避免符号只有 declare 无 define → 链接失败。
# 首次 Phase B 特化(Phase 1a 未特化)find_struct 返回 None,不补译,走下方正常全量翻译。
if trans._declare_only == 0:
pre_existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
pre_existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if pre_existing is not None and pre_existing.MethodsEmitted == 0:
# 防重入:先标记 MethodsEmitted=1,避免补译过程中方法体/返回类型注解
# 再次触发 _specialize_generic_class(如 __iter__ 返回 list[T]|t.CPtr)
@@ -563,12 +615,10 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 方法体永不发射 → 特化方法只有 call 无 define → 链接 undefined symbol。
pre_existing.MethodsEmitted = 1
# 从 stdlib 深拷贝表取 type_params(模板节点 pool list 跨 Phase 可能失效)
tp_names_pre: list[str] | t.CPtr = self._find_generic_template_tp_params(
pool, class_name)
tp_names_pre: list[str]* = self._find_generic_template_tp_params(pool, class_name)
if tp_names_pre is None:
tp_names_pre = template_cd.type_params
spec_cd_pre: ast.ClassDef | t.CPtr = self._clone_classdef_for_spec(
pool, template_cd, spec_name, tp_names_pre, type_args)
spec_cd_pre: ast.ClassDef* = self._clone_classdef_for_spec(pool, template_cd, spec_name, tp_names_pre, type_args)
if spec_cd_pre is not None:
trans.GenericTypeParamNames = tp_names_pre
trans.GenericTypeArgs = type_args
@@ -599,9 +649,9 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 3. 检查缓存(spec_key 包含所有类型实参,避免多参数泛型缓存冲突)
# 分配新缓冲区避免原地 strcat 溢出
sk_n: t.CSizeT = len(type_args)
sk_total: t.CSizeT = string.strlen(class_name) + 2 # "<" 和 ">"
sk_i: t.CSizeT
sk_n: sizet = len(type_args)
sk_total: sizet = string.strlen(class_name) + 2 # "<" 和 ">"
sk_i: sizet
for sk_i in range(sk_n):
if sk_i > 0:
sk_total += 1 # ","
@@ -630,7 +680,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
return cached
# 4. 检查是否已注册(Phase B 重复特化)
existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if existing is not None:
self._cache_spec(pool, spec_key, spec_name)
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
@@ -642,12 +692,10 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 5. 拷贝 ClassDef 节点
# 从 stdlib 深拷贝表取 type_params(模板节点 pool list 跨 Phase 可能失效)
tp_names: list[str] | t.CPtr = self._find_generic_template_tp_params(
pool, class_name)
tp_names: list[str]* = self._find_generic_template_tp_params(pool, class_name)
if tp_names is None:
tp_names = template_cd.type_params
spec_cd: ast.ClassDef | t.CPtr = self._clone_classdef_for_spec(
pool, template_cd, spec_name, tp_names, type_args)
spec_cd: ast.ClassDef* = self._clone_classdef_for_spec(pool, template_cd, spec_name, tp_names, type_args)
if spec_cd is None:
self.GenericSpecDepth -= 1
return None
@@ -665,6 +713,18 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 避免模板方法体内的跨模块调用被误报"模块未导入"。
saved_module_sha1: str = trans.ModuleSha1
saved_imported: str = trans._imported_modules
# 【窗口同进同出】类定义窗口(CurDefModuleSha1/CurDefClassName)必须与
# ModuleSha1 一起保存/恢复:下方 translate_class_def(:729)内部会按
# **模板模块 sha1 + 特化类名** 调 mark_module_class(ClassGenDef.vp:50),
# 覆盖这两个窗口变量。原实现只恢复 trans.ModuleSha1 ⇒ 调用方文件在此之后
# 解析任何裸类名时,规则 5.5 的归属判据(证据 ② is_module_class /
# ③ CurDefClassName 逐字相同)全都在对着"模板模块 / 特化类"判 ⇒ 判不到
# ⇒ 该实参以**裸名**形式被首次注册并永久复用,与同一特化在别处解析出的
# 带 SHA1 形式分裂成两个名字,最终以"被指类型是非基本类型……不可隐式转换"
# 报出(现场:includes/ast/base.vp:359 `_append_child(self.children, ...)`
# 源 list[rawptr[AST]] vs 目标 list[rawptr<定义模块SHA1>.AST])。
saved_def_mod: str = self.Gen.StructGen.CurDefModuleSha1
saved_def_cls: str = self.Gen.StructGen.CurDefClassName
template_sha1: str = self._find_generic_template_sha1(class_name)
if template_sha1 is not None:
trans.ModuleSha1 = template_sha1
@@ -681,9 +741,11 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if tpl_file_full is not None:
self.Gen.TypeGenBase.pop_trace()
self.Gen.TypeGenResolve.set_current_file(saved_cur_full)
# 恢复原始 ModuleSha1 与 imports
# 恢复原始 ModuleSha1 / imports / **类定义窗口**(窗口不平移,见上方说明)
trans.ModuleSha1 = saved_module_sha1
trans._imported_modules = saved_imported
self.Gen.StructGen.CurDefModuleSha1 = saved_def_mod
self.Gen.StructGen.CurDefClassName = saved_def_cls
# 8. 清除 type_map 上下文
trans.GenericTypeParamNames = None
@@ -693,12 +755,10 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 特化结果字段名完整性检查(fail-fast:字段名缺失 = 字段表损坏,
# 静默继续会让后续 GEP 按错索引访问)
if string.strcmp(class_name, "GSListNode") == 0:
result_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
result_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if result_entry is not None:
ri: int
for ri in range(result_entry.FieldCount):
rfe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry(
result_entry, ri)
rfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(result_entry, ri)
if rfe is not None:
rfn: str = self.Gen.StructGen.get_field_name_ptr(rfe)
if rfn is None:
+9
View File
@@ -0,0 +1,9 @@
import t, c
from stdint import *
from . import ClassGenBase
# ============================================================
# ClassGenMixin - 类定义/继承/泛型/vtable/enum/union/renum
# ============================================================
novirtual class ClassGenMixin(ClassGenBase.ClassGenBase):
"""类定义处理 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
File diff suppressed because it is too large. Load diff
-579
View File
@@ -1,579 +0,0 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.ClassGenBase as ClassGenBase
# ============================================================
# ClassGenDef - ClassGenMixin 拆分(阶段 D):translate_class_def 主入口
#
# 继承 ClassGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesClassDef 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ClassGenDef(ClassGenBase.ClassGenBase):
"""translate_class_def 主入口"""
# ============================================================
# translate_class_def — 翻译 class 定义(簇 D)
# ============================================================
def translate_class_def(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译 class 定义,返回 0"""
if node is None or trans is None:
return 0
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(node)
if cd is None or cd.name is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
class_name: str = cd.name
if trans.ModuleSha1 is None:
# fail-fast:翻译类时模块 SHA1 缺失(命名空间隔离依赖它)——
VLogger.error("翻译类时 ModuleSha1 缺失", "CLASS")
self.Gen.TypeGenErr.fatal_error(cd, "翻译类时 ModuleSha1 缺失", "TypeError")
# 命名空间隔离:标记为当前文件可见(必须在 existing 检查之前,
# 因为 Phase B 时结构体已注册会提前 return,否则永远无法标记本地类)
self.Gen.StructGen.add_visible_struct(pool, class_name)
# 枚举类:交给 HandlesEnum 处理
if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_enum_def(trans, cd)
# 联合体类:交给联合体处理
if self.Gen.ClassGenBase._is_union_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_union_def(trans, cd)
# REnum 类:交给 REnum 处理({i32 tag, max_payload} 布局)
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_renum_def(trans, cd)
# 泛型类:存储为模板,不发射 IR(等实例化时特化)
# 用局部变量接收 cd.type_params,触发类型推断
tp_val: list[str] | t.CPtr = cd.type_params
if self.Gen.ClassGenGeneric._is_generic_class(tp_val) == 1:
self.Gen.ClassGenGeneric._register_generic_template(pool, cd, trans.ModuleSha1)
return 0
# 异常类(try-except):注册编码+继承树,不生成 struct
# (对齐 TPC HandlesClassDef.py L509-511:_is_exception_class →
# _RegisterExceptionClass → return,异常类不编译为结构体)
if self.Gen.ClassGenBase._is_exception_class_def(cd) == 1:
return self.Gen.ClassGenBase._register_exception_class_def(trans, cd)
# 检查是否已注册(用 SHA1 区分跨模块同名类)
existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
class_name, trans.ModuleSha1)
if existing is not None:
# BUG 5 方案1修改点2:existing 时字段完整性检查
# 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整)
# 注意:无字段类(接口/标记类,类体无 AnnAssign)合法(空 struct + 方法),
# 仅当"类体声明了字段但 FieldCount==0"才算注册不完整(Phase A-pre 漏注册)。
if existing.FieldCount == 0 and trans._declare_only == 0:
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
# Phase B 时字段数为 0 说明注册不完整(Phase A-pre 可能漏注册)
self.Gen.TypeGenErr.fatal_error(
node,
f"结构体已注册但字段数为0,类: {class_name},可能注册不完整",
"InternalCompilerError")
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
if existing.Ty is not None:
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
# 其 Name(如 "sha1.ClassName")指向 Phase 1a 的 pool 内存。虽然 mark1
# 之前分配不被 reset 回收,但 struct_ty 会被后续文件翻译引用(vtable/
# self 参数类型打印),部分实现下 pool 块被复用导致 Name 悬空崩溃。
# 保险起见:深拷贝 Name 到 stdlib,确保跨文件永久存活。
if trans._declare_only == 0:
# 深拷贝整个 existing.Ty 到 stdlib:1a pool Ty 跨 1b reset 悬空,
# _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表
# 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType
# 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。
ty_deep: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
CompilerFactory.get_perm_pool(), existing.Ty)
if ty_deep is not None:
existing.Ty = ty_deep
old_name: str = llvmlite.get_struct_name(existing.Ty)
if old_name is not None:
on_len: t.CSizeT = string.strlen(old_name)
on_copy: str = CompilerFactory.get_perm_pool().alloc(on_len + 1)
if on_copy is not None:
string.strcpy(on_copy, old_name)
c.DerefAs(existing.Ty, llvmlite.LLVMType.Struct(
llvmlite.get_struct_fields(existing.Ty),
llvmlite.get_struct_fcount(existing.Ty),
on_copy))
# Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行)
llvmlite.module_add_named_type(trans.Module, pool, existing.Ty)
# Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体
# Phase B(declare_only=0)时全量翻译方法体
if trans._declare_only == 1:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name, 1)
else:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name)
# SHA1 已在 register_struct 时设置,无需再补
return 0
# ============================================================
# 触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])
# 必须在 _detect_vtable_status 和父类字段继承之前调用,
# 否则 find_struct_by_module("GSListNode[Value]") 返回 None
# ============================================================
self.Gen.ClassGenBase._trigger_base_specialization(trans, cd)
# ============================================================
# VTable 检测:判断是否启用虚表
# ============================================================
has_vtable: int = self.Gen.ClassGenBase._detect_vtable_status(cd, trans)
parent_name: str = self.Gen.ClassGenBase._get_parent_class(cd, trans)
# 多继承:收集所有真实父类名(跳过 marker base),供字段展平/vtable/MRO 使用
parents_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.PARENT_MAX)
parents_count: int = 0
if parents_buf is not None:
parents_count = self.Gen.ClassGenBase._collect_parents(pool, cd, parents_buf, GenTypes.PARENT_MAX, trans)
is_novtable_deco: int = 0
if cd.decorator_list is not None:
if self.Gen.ClassGenBase._has_decorator(cd.decorator_list, "NoVTable") == 1:
is_novtable_deco = 1
# ============================================================
# PhaseA 预注册延迟检查:父类未注册时延迟注册当前类
#
# 根因:AssignHandle(HandlesBase.Mixin) 有自己的字段 _CurrentClass,
# 当 Mixin 未注册时(字母序:HandlesAssign.py 在 HandlesBase.py 之前),
# field_count=1(不是0),field_count==0 静默返回不触发,
# AssignHandle 被注册为只有 _CurrentClass 字段(缺 Trans)。
# 后续遍 existing is not None,不补字段 → attribute ptr is None: self.Trans
#
# 修复:declare_only==1 且父类未注册时,延迟注册到下一遍。
# 多遍 PhaseA-pre 会在后续遍中重试(父类已注册后完整注册)。
# Phase B(declare_only==0)不延迟,避免死锁。
# ============================================================
if trans._declare_only == 1 and parent_name is not None:
parent_check: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
parent_name, trans.ModuleSha1)
if parent_check is None:
parent_check = self.Gen.StructGen.find_struct(parent_name)
if parent_check is None:
return 0
# ============================================================
# 1. 遍历 class body,收集字段信息
# ============================================================
# 用 CSizeT 数组存储指针值(64 位系统上 8 字节)
field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
field_defaults_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
field_annot_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None:
return 0
if field_annot_buf is None:
return 0
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_annot_buf, 0, 8 * GenTypes.FIELD_MAX)
field_count: int = 0
# ============================================================
# 1.5 继承父类字段(字段展平)
# ============================================================
#
# 父类字段排在子类字段之前,结构体布局:
# [vtable_ptr?, parent_field1, ..., child_field1, ...]
# 跳过父类的 __vtable__ 字段(子类会有自己的 vtable 指针)
# ============================================================
# 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明优先)
if parents_count > 0 and parents_buf is not None:
for pi in range(parents_count):
pname_addr: t.CSizeT = parents_buf[pi]
if pname_addr == 0:
continue
pname: str = (str | t.CPtr)(t.CVoid(pname_addr, t.CPtr))
if pname is None:
continue
# 用 SHA1 感知查找父类,规避跨模块同名找错
parent_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
pname, trans.ModuleSha1)
if parent_entry is None:
# 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin),
# find_struct_by_module 不回退全局查找,此处手动回退到 find_struct,
# 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败
parent_entry = self.Gen.StructGen.find_struct(pname)
if parent_entry is None:
# 父类尚未注册(依赖模块未翻译:Phase A 按文件序,子类模块可能
# 先于父类模块,如 Generator.vp G < TypeGenMixin.vp T)。此时
# **不能**继续注册——StructTable 在 StdlibAlloc=1 时用 perm_pool
# 分配(init_struct_table L136-140),残缺条目跨 pass 存活,
# 后续 pass 的 existing 早退(L2995-3006)永远无法修复 → 子类
# __init__ 写父类字段(self.TraceFrames)每遍永久失败。
# 整类 defer(不注册)+ 标记 _deps_pending 下遍重译;模块级
# 变量/函数参数解析失败静默降级 i32(handle_module_level_var
# L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。
if trans is not None:
trans._deps_pending = 1
# [PHASEADBG] 临时诊断:父类未注册即 pending 的根因(用后移除)
stdio.printf("[PHASEADBG] 父类未注册: class=%s parent=%s\n", class_name, pname)
return 0
pfc: int = parent_entry.FieldCount
for pfi in range(pfc):
pfe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry(
parent_entry, pfi)
if pfe is None:
continue
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
if pfn is None:
continue
# 跳过父类的 vtable 指针字段
if string.strcmp(pfn, "__vtable__") == 0:
continue
# 多继承去重:字段名已存在跳过(先声明父类优先)
if self.Gen.ClassGenBase._field_name_exists(field_names_buf, field_count, pfn) != 0:
continue
if field_count < GenTypes.FIELD_MAX:
pft: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_field_type_ptr(pfe)
pfd: ast.AST | t.CPtr = self.Gen.StructGen.get_field_default_ptr(pfe)
pfa: str = self.Gen.StructGen.get_field_annot_class_name(pfe)
field_names_buf[field_count] = t.CSizeT(pfn)
field_types_buf[field_count] = t.CSizeT(pft)
if pfd is not None:
field_defaults_buf[field_count] = t.CSizeT(pfd)
else:
field_defaults_buf[field_count] = 0
if pfa is not None:
stored_val: t.CSizeT = t.CSizeT(pfa)
field_annot_buf[field_count] = stored_val
else:
field_annot_buf[field_count] = 0
field_count += 1
children: list[ast.AST | t.CPtr] | t.CPtr = cd.children
if children is None:
return 0
cn: t.CSizeT = len(children)
for ci in range(cn):
stmt: ast.AST | t.CPtr = children.get(ci)
if stmt is None:
continue
sk: int = stmt.kind()
if sk != ast.ASTKind.AnnAssign: continue
# AnnAssign(target=Name(id), annotation=type, value=...)
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt)
if aa is None or aa.target is None:
continue
# 获取字段名
if aa.target.kind() != ast.ASTKind.Name:
continue
tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target)
if tgt.id is None:
continue
# 类体 CDefine 常量(任意位置,2026-08-10):注册到全局 CDefine 表。
# 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查
# CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册
# 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。
if self.Gen.AssignGen.is_cdefine_annotation(aa.annotation) != 0:
if aa.value is not None:
self.Gen.AssignGen._register_cdefine_from_value(pool, tgt.id, aa.value)
# CDefine 字段类型固定 i32,直接收集后跳过通用注解解析:
# resolve_annotation_type 不识别 "CDefine"(map_t_type/CTypedef
# 表均无此名),类体尾部常量(ISINSTANCE_CLASS_MAX 是首个触发
# 该路径的实例)Phase B 会 NameError: 't.CDefine' is not defined。
# 与类头常量(MRO_MAX 等)的 i32 字段语义保持一致。
if field_count < GenTypes.FIELD_MAX:
cdef_ty_l: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if cdef_ty_l is not None:
field_names_buf[field_count] = t.CSizeT(tgt.id)
field_types_buf[field_count] = t.CSizeT(cdef_ty_l)
if aa.value is not None:
field_defaults_buf[field_count] = t.CSizeT(aa.value)
else:
field_defaults_buf[field_count] = 0
field_count += 1
continue
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
# 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码
# (FakeDuck:_str.__requires__=['__mbuddy__'])。此处解析 list[str]
# 字面量注册到持久元数据表,供 requires 注入 / with provider 查询。
if string.strcmp(tgt.id, "__provides__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_PROVIDES, class_name, aa.value)
continue
if string.strcmp(tgt.id, "__requires__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRES, class_name, aa.value)
continue
if string.strcmp(tgt.id, "__require_must__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRE_MUST, class_name, aa.value)
continue
# 解析字段类型
field_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
# for x in self.field 会误走指针迭代(元素非整数 → null 终止符
# 检查失败报错)。推断为静态数组后走数组迭代(按长度 N)。
if field_ty is not None:
field_ty = self.Gen.AssignGen._infer_static_array_ty_from_literal(
pool, aa.annotation, aa.value, field_ty)
if field_ty is None:
# BUG 5 方案1修改点1:消除 i32 静默回退
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
# Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译
# 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱)
# 例外:泛型 Subscript(如 list[AST|t.CPtr])未特化时回退为 Ptr(i8),
# 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name
# 供 translate_attribute 做后续 bitcast。
is_generic_sub: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
is_generic_sub = 1
if is_generic_sub != 0:
# 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复
field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool))
else:
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,
# 静默 return 0 让多遍收敛机制生效(下一遍依赖类已注册即可成功)
if trans._declare_only == 1:
return 0
# Phase A(declare_only=0,Phase2.vp L573 多遍):依赖模块按
# 文件序可能晚于本模块翻译(如 TypeGenMixin.vp G < HandlesType.vp
# H,TraceFrames 注解的 HandlesType.TraceFrame 未注册)→ 标记
# _deps_pending 下遍重译,不在本遍以残缺字段注册结构体。
# 真正的类型错误由 Phase A 收敛检查暴露("Phase A 无法完成
# 翻译: <file>,请检查依赖模块是否已编译/注册"),不静默掩盖。
if trans is not None:
trans._deps_pending = 1
# [PHASEADBG] 临时诊断:字段注解类型未解析即 pending 的根因(用后移除)
dbg_ak: t.CInt = -1
if aa.annotation is not None:
dbg_ak = aa.annotation.kind()
stdio.printf("[PHASEADBG] 字段类型未解析: class=%s field=%s annot_kind=%d\n",
class_name, tgt.id, dbg_ak)
return 0
# 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时
# 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底)
# 存储到数组(指针转 CSizeT)
if field_count < GenTypes.FIELD_MAX:
diag_name_val: t.CSizeT = t.CSizeT(tgt.id)
field_names_buf[field_count] = diag_name_val
field_types_buf[field_count] = t.CSizeT(field_ty)
# 存储默认值 AST 指针(None=无默认值)
if aa.value is not None:
field_defaults_buf[field_count] = t.CSizeT(aa.value)
else:
field_defaults_buf[field_count] = 0
# 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体)
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]")
if aa.annotation is not None:
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
aa.annotation, trans._imported_modules, pool, trans._from_imports)
if annot_cn is not None:
diag_cn_val: t.CSizeT = t.CSizeT(annot_cn)
field_annot_buf[field_count] = diag_cn_val
else:
field_annot_buf[field_count] = 0
else:
field_annot_buf[field_count] = 0
field_count += 1
if field_count == 0:
# PhaseA 预注册时,父类可能尚未注册(字母序单遍扫描),
# 导致子类无法继承父类字段 → field_count==0。
# 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。
if trans._declare_only == 1 and parent_name is not None:
return 0
# 无字段类(接口/标记类):不能整体跳过——类可能只有方法
# (如多继承中的 ShapeM/Vec2Extra),需注册类并翻译方法。
# 空 struct 在 LLVM 中合法({});@t.CVTable 类由下方补 vtable 指针字段。
# ============================================================
# 2. 创建 LLVM StructType
# ============================================================
# 构建 ParamNode 链表
first_node: llvmlite.ParamNode | t.CPtr = None
prev_node: llvmlite.ParamNode | t.CPtr = None
total_field_count: int = field_count
# 如果有虚表,首字段添加 i8* vtable 指针
if has_vtable == 1:
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:
fb_vn: str = VLogger.fmt_buf()
if fb_vn is not None:
viperlib.snprintf(fb_vn, 1024, "failed to alloc vtable node for %s", class_name)
VLogger.error(fb_vn, "CLASS")
return 0
string.memset(vtable_node, 0, llvmlite.ParamNode.__sizeof__())
vtable_node.Ty = i8_ptr_ty
first_node = vtable_node
prev_node = vtable_node
total_field_count = field_count + 1
for fi in range(field_count):
fty_addr: t.CSizeT = field_types_buf[fi]
if fty_addr == 0:
continue
fty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(t.CVoid(fty_addr, t.CPtr))
# 创建 ParamNode
pnode: llvmlite.ParamNode | t.CPtr = pool.alloc(llvmlite.ParamNode.__sizeof__())
if pnode is None:
continue
string.memset(pnode, 0, llvmlite.ParamNode.__sizeof__())
pnode.Ty = fty
if first_node is None:
first_node = pnode
if prev_node is not None:
prev_node.Next = pnode
prev_node = pnode
# 构造命名结构体类型名: "sha1.ClassName" 或 "ClassName"
# 注意: Phase 1b(declare_only=0)时 struct_ty 会注册到全局表并被后续
# 文件翻译引用(vtable/self 参数类型打印),其 Name 必须跨 1b 每文件
# reset_to(mark1) 存活 → 用 stdlib 深拷贝;Phase 1a 仍用 pool(无需跨文件)。
type_name: str = class_name
if trans.ModuleSha1 is not None:
name_buf: str = pool.alloc(64)
if name_buf is not None:
viperlib.snprintf(name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
type_name = name_buf
if trans._declare_only == 0 and type_name is not None:
tn_len: t.CSizeT = string.strlen(type_name)
tn_copy: str = CompilerFactory.get_perm_pool().alloc(tn_len + 1)
if tn_copy is not None:
string.strcpy(tn_copy, type_name)
type_name = tn_copy
# 创建 StructType(命名结构体)
struct_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, first_node, total_field_count, type_name)
if struct_ty is None:
fb_st: str = VLogger.fmt_buf()
if fb_st is not None:
viperlib.snprintf(fb_st, 1024, "failed to create StructType for %s", class_name)
VLogger.error(fb_st, "CLASS")
return 0
# Phase 1b(declare_only=0)新注册的 struct_ty 会被注册到全局表并被后续文件
# 翻译引用(vtable/self 参数类型打印/find_struct_by_type 指针匹配)。LLVMType
# 对象和 Fields 链是 pool 分配的,本文件 reset_to(mark1) 后悬空。深拷贝整棵树
# 到 stdlib,并替换 struct_ty 指针,保证 entry.Ty 与调用方持有的 struct_ty
# 指向同一个 stdlib 拷贝(指针匹配不失效)且跨文件永久存活。
# StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc(declare_only=1)
# 预注册 includes 时若仍用 pool struct_ty,reset_to(mark2) 后 entry.Ty 悬空 →
# 后续文件字段类型引用(resolve → Ptr(entry.Ty))→ opaque 扫描遍历悬空 fields
# 崩溃(LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
st_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
CompilerFactory.get_perm_pool(), struct_ty)
if st_copy is not None:
struct_ty = st_copy
type_name = llvmlite.get_struct_name(struct_ty)
# 注册命名结构体到模块(输出 %"name" = type { ... } 定义行)
llvmlite.module_add_named_type(trans.Module, pool, struct_ty)
# 扫描字段类型,为跨模块结构体引用添加 opaque 声明
# 例如 list[int].__pool__ 引用 memhub.MemManager,需要在当前模块添加 %"sha1.MemManager" = type opaque
for fi in range(field_count):
fty_addr_op: t.CSizeT = field_types_buf[fi]
if fty_addr_op == 0:
continue
fty_op: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(t.CVoid(fty_addr_op, t.CPtr))
llvmlite.module_ensure_opaque_for_type(trans.Module, pool, fty_op)
# ============================================================
# 3. 注册到 HandlesStruct
# ============================================================
entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.register_struct(
pool, class_name, struct_ty, trans.ModuleSha1)
if entry is None:
fb_re: str = VLogger.fmt_buf()
if fb_re is not None:
viperlib.snprintf(fb_re, 1024, "failed to register %s", class_name)
VLogger.error(fb_re, "CLASS")
return 0
# 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错)
if has_vtable == 1:
entry.HasVTable = 1
if is_novtable_deco == 1:
entry.IsNoVTable = 1
if parent_name is not None:
# 1b 阶段父类名是 pool 字符串(本文件 AST/1a 预存),reset 后悬空;
# _translate_oop_methods/_detect_vtable_status 会在后续文件
# find_struct(ParentName) strcmp 解引用 → 用 stdlib 拷贝保证跨文件存活
if self.Gen.StructGen.get_stdlib_alloc() != 0:
pn_len: t.CSizeT = string.strlen(parent_name)
pn_buf: str = CompilerFactory.get_perm_pool().alloc(pn_len + 1)
if pn_buf is not None:
string.strcpy(pn_buf, parent_name)
entry.ParentName = pn_buf
else:
entry.ParentName = parent_name
# 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活)
if parents_count > 0 and parents_buf is not None:
self.Gen.StructGen.set_parent_names(pool, class_name, parents_buf, parents_count)
# 添加字段信息
# 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始
if has_vtable == 1:
i8_ptr_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
for fi in range(field_count):
fname_addr: t.CSizeT = field_names_buf[fi]
if fname_addr == 0:
continue
fname: str = (str | t.CPtr)(t.CVoid(fname_addr, t.CPtr))
fty_addr2: t.CSizeT = field_types_buf[fi]
if fty_addr2 == 0:
continue
fty2: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(t.CVoid(fty_addr2, t.CPtr))
# 恢复默认值 AST 指针
fdef_addr: t.CSizeT = field_defaults_buf[fi]
fdef: ast.AST | t.CPtr = None
if fdef_addr != 0:
fdef = (ast.AST | t.CPtr)(t.CVoid(fdef_addr, t.CPtr))
# 恢复注解类名指针
fannot_addr: t.CSizeT = field_annot_buf[fi]
fannot: str = None
if fannot_addr != 0:
fannot = (str)(t.CVoid(fannot_addr, t.CPtr))
if fname is not None and fty2 is not None:
self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
# Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体
if trans._declare_only == 1:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1)
return 0
# ============================================================
# 4. OOP 方法处理:扫描 FunctionDef,标记 OOP,翻译方法,生成 __before_init__
# ============================================================
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name)
return 0
File diff suppressed because it is too large. Load diff
-25
View File
@@ -1,25 +0,0 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.ClassGenBase as ClassGenBase
# ============================================================
# ClassGenMixin - 类定义/继承/泛型/vtable/enum/union/renum
#
# 500~1500 行/文件架构重构阶段 D:全部方法已拆至
# ClassGenBase/Enum/Generic/VTable/Def,本文件保留类声明。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ClassGenMixin(ClassGenBase.ClassGenBase):
"""类定义处理 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
+471
View File
@@ -0,0 +1,471 @@
import t, c
from stdint import *
import llvmlite
import memhub
import string
import stdlib
import viperlib
import w32.win32file as w32file
import lib.core.VLogger as VLogger
# ============================================================
# DebugInfoGen - function-level DWARF metadata emitter (llvmlite text IR)
#
# Node layout mirrors TPC lib/core/LLVMCG/DebugInfo.py node-for-node:
# !DIFile -> !{ null } -> !DISubroutineType -> distinct !DICompileUnit
# -> distinct !DISubprogram (per define) -> !DILocation (per statement)
# module tail: !llvm.dbg.cu + !llvm.module.flags (Debug Info Version 3)
#
# Enabled only while --traceback is on: the Phase layer mirrors
# BuildPipeline._traceback_mode here via SetEnabled(). While disabled every
# entry point returns before touching any llvmlite DI API, so the emitted IR
# stays byte-identical to the pre-DWARF output.
#
# Scope/statement hooks are driven from the Gen layer:
# Translator.translate -> BeginModule / Finalize
# FuncGenDef.translate_function_def / ClassGenVTable._translate_method /
# FuncGenMixin (nested + lambda) -> PushFunction / PopFunction
# StmtGenMixin.translate_stmt -> SetStatement
# ============================================================
# ---- module-scope state (reset per module by BeginModule) ----
_g_enabled: int = 0
_g_finalized: int = 0
_g_mod: llvmlite.LLVMModule* = None
_g_file_path: str = None
_g_file_id: int = -1
_g_null_id: int = -1
_g_subtype_id: int = -1
_g_cu_id: int = -1
_g_cur_sp: int = -1
_g_cur_entry_ref: str = None
_g_cur_builder: llvmlite.IRBuilder* = None
_g_depth: int = 0
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder)
# per level. +1 so 0 means "no enclosing function" (sp ids are >= 0). The saved
# builder is what makes SetStatement reject a builder that is not the one the
# current DISubprogram was pushed on (e.g. an async state machine emitted inside
# a bracketed function): such instructions must carry no !dbg, otherwise LLVM's
# verifier rejects "!dbg attachment points at wrong subprogram for function".
DBG_STACK_MAX: t.CDefine = 256
_g_stk_sp: sizet* = None
_g_stk_ref: str = None
_g_stk_bld: str = None
# DILocation cache: direct-mapped, up to 8-way linear probe, key (sp, line, col).
DBG_LOC_SLOTS: t.CDefine = 8192
DBG_LOC_PROBE: t.CDefine = 8
DBG_LOC_KEY_STRIDE: t.CDefine = 3
_g_loc_key: sizet* = None
_g_loc_ref: str = None
_g_loc_used: bytes = None
# ============================================================
# SetEnabled / IsEnabled - mirror the --traceback switch
# ============================================================
def SetEnabled(flag: bool) -> int:
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
global _g_enabled
if flag:
_g_enabled = 1
else:
_g_enabled = 0
return 0
def IsEnabled() -> int:
return _g_enabled
# ============================================================
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers
# ============================================================
def _EnsureStorage() -> int:
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
if _g_stk_sp is not None:
return 0
sbuf1: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
sbuf2: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
sbuf3: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
kbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * DBG_LOC_KEY_STRIDE * 8)
rbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * 8)
ubuf: bytes = stdlib.malloc(DBG_LOC_SLOTS)
if sbuf1 is None or sbuf2 is None or sbuf3 is None or kbuf is None or rbuf is None or ubuf is None:
VLogger.error("--traceback: DWARF 元数据缓冲分配失败,终止编译", "DWARF")
return 1
string.memset(sbuf1, 0, DBG_STACK_MAX * 8)
string.memset(sbuf2, 0, DBG_STACK_MAX * 8)
string.memset(sbuf3, 0, DBG_STACK_MAX * 8)
string.memset(kbuf, 0, DBG_LOC_SLOTS * DBG_LOC_KEY_STRIDE * 8)
string.memset(rbuf, 0, DBG_LOC_SLOTS * 8)
string.memset(ubuf, 0, DBG_LOC_SLOTS)
_g_stk_sp = (sizet*)(sbuf1)
_g_stk_ref = sbuf2
_g_stk_bld = sbuf3
_g_loc_key = (sizet*)(kbuf)
_g_loc_ref = rbuf
_g_loc_used = ubuf
return 0
def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
"""Allocate a persistent "!N" reference string for a DI node id."""
buf: str = pool.alloc(24)
if buf is None:
return None
viperlib.snprintf(buf, 24, "!%d", node_id)
return buf
# ============================================================
# BeginModule - start of one module translation
# ============================================================
def BeginModule(mod: llvmlite.LLVMModule*, file_path: str) -> int:
"""Reset per-module DI state. Call once at the start of each module."""
global _g_mod, _g_file_path, _g_finalized, _g_file_id, _g_null_id
global _g_subtype_id, _g_cu_id, _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if _EnsureStorage() != 0:
return 1
_g_mod = mod
_g_file_path = file_path
_g_finalized = 0
_g_file_id = -1
_g_null_id = -1
_g_subtype_id = -1
_g_cu_id = -1
_g_cur_sp = -1
_g_cur_entry_ref = None
_g_cur_builder = None
_g_depth = 0
if _g_loc_used is not None:
string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
return 0
# ============================================================
# _EnsureModuleNodes - DIFile / !{ null } / DISubroutineType / DICompileUnit
# (created lazily at the first define, once per module)
# ============================================================
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id
if _g_cu_id >= 0:
return 0
mod: llvmlite.LLVMModule* = _g_mod
if mod is None:
return 1
# DIFile: **绝对**路径(正斜杠),filename 保留整条路径。
# 为什么必须绝对:本工程给 CurrentFile 的是**项目相对**路径(如
# "./Test/LLvmLiteTest/App/main.vp"),LLVM 会把该目录写成 CU 的 DW_AT_comp_dir,
# 而主文件条目用 `dir_index: 0` 指向 comp_dir;stackmap 写入端只解析
# `llvm-dwarfdump --debug-line`(其中**没有** comp_dir)⇒ 行记录只能退化成裸文件名
# "main.vp":运行期既显示不全,也无法按路径重读源码行(实证:曾整段栈都打印
# `File "main.vp", line N` 而无源码行)。
# 绝对化后,无论 LLVM 保留整条 name(写入端 `_tb_is_abs_path` 命中 ⇒ 原样采用)
# 还是把它拆成 dir+basename(dir_index 指向同一个绝对目录 ⇒ 拼回绝对路径),
# 结果都是绝对路径 —— 故本改动不需要动写入端。
src: str = _g_file_path
if src is None:
src = ""
slen: sizet = string.strlen(src)
cap_n: sizet = slen + 4112
nbuf: str = pool.alloc(cap_n)
if nbuf is None:
VLogger.error("--traceback: DIFile 路径缓冲分配失败,终止编译", "DWARF")
return 1
if slen > 0:
string.strncpy(nbuf, src, slen)
nbuf[slen] = '\0'
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
# 相对路径 → 前置当前工作目录。用 w32file 低层调用(同 Repl.vp),
# 不经 os._mbuddy:编译器进程未必初始化过那个内存管理器。
CwdBuf: bytes = pool.alloc(4112)
if CwdBuf is not None:
CwdBuf[0] = '\0'
nc: ULONG = w32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
if nc > 0:
# 防御:缓冲区不足时 GetCurrentDirectoryA 的写入内容未定义,强制补 NUL
CwdBuf[4110] = '\0'
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
# 归一所有反斜杠(CWD 段同样可能含 '\')
flen: sizet = string.strlen(nbuf)
ni: sizet = 0
for ni in range(flen):
if nbuf[ni] == '\\':
nbuf[ni] = '/'
# filename 取整条路径;directory 另存其目录前缀(两段各自 NUL 结尾,互不污染)
fname: str = nbuf
directory: str = ""
slash: str = string.strrchr(nbuf, '/')
if slash is not None:
dlen: sizet = string.strlen(nbuf) - string.strlen(slash + 1) - 1
dirbuf: str = pool.alloc(dlen + 1)
if dirbuf is not None:
string.strncpy(dirbuf, nbuf, dlen)
dirbuf[dlen] = '\0'
directory = dirbuf
dsize: sizet = string.strlen(fname) + string.strlen(directory) + 96
dbuf: str = pool.alloc(dsize)
if dbuf is None:
VLogger.error("--traceback: DIFile 节点缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(dbuf, dsize, "!DIFile(filename: \"%s\", directory: \"%s\")", fname, directory)
fid: int = llvmlite.module_add_di_node(mod, pool, dbuf)
if fid < 0:
VLogger.error("--traceback: DIFile 元数据节点分配失败,终止编译", "DWARF")
return 1
_g_file_id = fid
# types: !{ null } placeholder list, then DISubroutineType referencing it
nid: int = llvmlite.module_add_di_node(mod, pool, "!{ null }")
if nid < 0:
VLogger.error("--traceback: DISubroutineType 类型表分配失败,终止编译", "DWARF")
return 1
_g_null_id = nid
tbuf: str = pool.alloc(64)
if tbuf is None:
VLogger.error("--traceback: DISubroutineType 缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(tbuf, 64, "!DISubroutineType(types: !%d)", _g_null_id)
tid: int = llvmlite.module_add_di_node(mod, pool, tbuf)
if tid < 0:
VLogger.error("--traceback: DISubroutineType 元数据节点分配失败,终止编译", "DWARF")
return 1
_g_subtype_id = tid
cbuf: str = pool.alloc(256)
if cbuf is None:
VLogger.error("--traceback: DICompileUnit 缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(cbuf, 256,
"distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)",
_g_file_id)
cid: int = llvmlite.module_add_di_node(mod, pool, cbuf)
if cid < 0:
VLogger.error("--traceback: DICompileUnit 元数据节点分配失败,终止编译", "DWARF")
return 1
_g_cu_id = cid
return 0
# ============================================================
# _LocRefAt - read a cached DILocation ref string pointer
# ============================================================
def _LocRefAt(slot: int) -> str:
slotp: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot) * 8)
r: str = slotp[0]
return r
# ============================================================
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once
# ============================================================
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
if line <= 0:
return _g_cur_entry_ref
c: int = col
if c <= 0:
c = 1
if _g_loc_used is None or _g_mod is None:
return None
h: uint64 = (uint64(sp) * 1000003 + uint64(line) * 31 + uint64(c)) & uint64(DBG_LOC_SLOTS - 1)
pi: int = 0
while pi < DBG_LOC_PROBE:
slot: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
if _g_loc_used[slot] == 0:
break
kbase: int = slot * DBG_LOC_KEY_STRIDE
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line \
and int(_g_loc_key[kbase + 2]) == c:
return _LocRefAt(slot)
pi += 1
lbuf: str = pool.alloc(96)
if lbuf is None:
return None
viperlib.snprintf(lbuf, 96, "!DILocation(line: %d, column: %d, scope: !%d)", line, c, sp)
lid: int = llvmlite.module_add_di_node(_g_mod, pool, lbuf)
if lid < 0:
VLogger.error("--traceback: DILocation 元数据节点分配失败,终止编译", "DWARF")
return None
lref: str = _RefOf(pool, lid)
if lref is None:
VLogger.error("--traceback: DILocation 引用缓冲分配失败,终止编译", "DWARF")
return None
if pi < DBG_LOC_PROBE:
slot2: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
kbase2: int = slot2 * DBG_LOC_KEY_STRIDE
_g_loc_key[kbase2] = sizet(sp)
_g_loc_key[kbase2 + 1] = sizet(line)
_g_loc_key[kbase2 + 2] = sizet(c)
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot2) * 8)
rslot[0] = lref
_g_loc_used[slot2] = 1
return lref
# ============================================================
# PushFunction - create the DISubprogram for one define
# ============================================================
def PushFunction(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
func: llvmlite.Function*, line: int, col: int) -> int:
"""Attach a DISubprogram to one define and set the builder's entry DILocation.
Returns 1 when pushed (caller must call PopFunction), 0 when DI is disabled
or not applicable. Every instruction emitted afterwards on `builder` carries
", !dbg !N" until the next SetStatement.
"""
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if pool is None or builder is None or func is None or _g_mod is None:
return 0
if _g_depth >= DBG_STACK_MAX:
VLogger.error("--traceback: DWARF 函数嵌套深度超上限,终止编译", "DWARF")
return 0
if _EnsureModuleNodes(pool) != 0:
return 0
name: str = llvmlite.function_get_name(func)
if name is None:
VLogger.error("--traceback: 函数名为空,无法发射 DISubprogram,终止编译", "DWARF")
return 0
dline: int = line
if dline <= 0:
dline = 1
dcol: int = col
if dcol <= 0:
dcol = 1
ssize: sizet = string.strlen(name) + 320
spbuf: str = pool.alloc(ssize)
if spbuf is None:
VLogger.error("--traceback: DISubprogram 缓冲分配失败,终止编译", "DWARF")
return 0
viperlib.snprintf(spbuf, ssize,
"distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)",
name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
sp_id: int = llvmlite.module_add_di_node(_g_mod, pool, spbuf)
if sp_id < 0:
VLogger.error("--traceback: DISubprogram 元数据节点分配失败,终止编译", "DWARF")
return 0
sp_ref: str = _RefOf(pool, sp_id)
if sp_ref is None:
VLogger.error("--traceback: DISubprogram 引用缓冲分配失败,终止编译", "DWARF")
return 0
llvmlite.function_set_dbg(func, sp_ref)
# save the enclosing function's DI context (LIFO), then switch to this one
_g_stk_sp[_g_depth] = sizet(_g_cur_sp + 1)
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
rslot[0] = _g_cur_entry_ref
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
bslot[0] = _g_cur_builder
_g_depth += 1
_g_cur_sp = sp_id
_g_cur_builder = builder
entry_ref: str = _EnsureLocation(pool, sp_id, dline, dcol)
if entry_ref is None:
VLogger.error("--traceback: 入口 DILocation 生成失败,终止编译", "DWARF")
return 0
_g_cur_entry_ref = entry_ref
llvmlite.builder_set_debug_location(builder, entry_ref)
return 1
# ============================================================
# PopFunction - restore the enclosing function's DI context
# ============================================================
def PopFunction(builder: llvmlite.IRBuilder*) -> int:
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if _g_depth <= 0:
return 0
_g_depth -= 1
_g_cur_sp = int(_g_stk_sp[_g_depth]) - 1
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
_g_cur_entry_ref = rslot[0]
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
_g_cur_builder = (llvmlite.IRBuilder*)(bslot[0])
if builder is not None:
llvmlite.builder_clear_debug_location(builder)
return 0
# ============================================================
# SetStatement - point the current builder at a statement DILocation
# ============================================================
def SetStatement(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
line: int, col: int) -> int:
"""Update the builder's DILocation to the current statement (line precision).
No-op outside a DI-attached define (module-level code / synthetic defines),
so instructions outside a function body never reference a function's scope.
"""
if _g_enabled == 0 or pool is None or builder is None:
return 0
if _g_cur_sp < 0:
return 0
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
if ref is None:
return 0
llvmlite.builder_set_debug_location(builder, ref)
return 1
# ============================================================
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
# ============================================================
def _ModuleHasDefine(mod: llvmlite.LLVMModule*) -> int:
if mod is None:
return 0
cur: llvmlite.Function* = mod.FuncHead
steps: int = 0
while cur is not None:
steps += 1
if steps > 100000:
break
if llvmlite.function_is_declared(cur) == 0:
return 1
cur = llvmlite.function_get_next(cur)
return 0
# ============================================================
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir
# ============================================================
def Finalize(pool: memhub.MemBuddy*) -> int:
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
global _g_finalized
if _g_enabled == 0:
return 0
if _g_finalized != 0:
return 0
mod: llvmlite.LLVMModule* = _g_mod
if mod is None or pool is None:
return 0
_g_finalized = 1
# Self-check (mirrors TPC Phase2Translator.py:3444): a module that emits a
# define but carries no DI at all loses line info for its frames.
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0:
VLogger.warning("--traceback: 本模块发射了 define 但 IR 未携带 DWARF (!llvm.dbg.cu)——该模块帧在 Ctrl+C 栈里将只显示地址", "DWARF")
if _g_cu_id < 0:
return 0
cu_ref: str = _RefOf(pool, _g_cu_id)
if cu_ref is None:
VLogger.error("--traceback: DICompileUnit 引用缓冲分配失败,终止编译", "DWARF")
return 1
llvmlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
fbuf: str = pool.alloc(64)
if fbuf is None:
VLogger.error("--traceback: 模块级调试标志缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(fbuf, 64, "!{i32 2, !\"Debug Info Version\", i32 3}")
flags_id: int = llvmlite.module_add_di_node(mod, pool, fbuf)
if flags_id < 0:
VLogger.error("--traceback: llvm.module.flags 元数据节点分配失败,终止编译", "DWARF")
return 1
fl_ref: str = _RefOf(pool, flags_id)
if fl_ref is None:
VLogger.error("--traceback: llvm.module.flags 引用缓冲分配失败,终止编译", "DWARF")
return 1
llvmlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
return 0
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -8,92 +8,55 @@ import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# ============================================================
# ExprGenCdef - ExprGenMixin 拆分(阶段 D):跨模块 CDefine 查找
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenCdef(ExprGenBase.ExprGenBase):
novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
"""跨模块 CDefine 查找"""
# ============================================================
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .vp/.py 文件路径
#
# .vpi 文件可能未生成(Phase1 未输出 vpi),此时回退读取源 .py 文件。
# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径,
# 根据 rel_path 前缀选择基础目录:
# - rel_path 以 "lib/" 开头 → App 源文件,用 {Config.SourceDir}/{rel_path}
# - 其他 → includes 文件,用 {Config.IncludesDir}/{rel_path}
#
# Returns:
# stdlib.malloc 分配的路径字符串(调用者负责 free),None 失败
# ============================================================
# _build_source_path_from_sha1:通过 SHA1 从 StubMerger 全局存储器构建源 .vp/.py 文件路径(.vpi 未生成时回退读源 .py);rel_path 以 "lib/" 开头 → {Config.SourceDir}/{rel_path},其他 → {Config.IncludesDir}/{rel_path}。返回 stdlib.malloc 分配的路径字符串(调用者负责 free),失败返回 None
def _build_source_path_from_sha1(self, sha1: str) -> str:
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
if sha1 is None:
return None
if Config.IncludesDir is None:
return None
if sha1 is None: return None
if Config.IncludesDir is None: return None
# 从 StubMerger 全局存储器获取相对路径数组
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
count: int = StubMergerStore.GetSha1StoreCount()
if rel_arr_ptr is None or count <= 0:
return None
if rel_arr_ptr is None or count <= 0: return None
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
if sha1_arr_ptr is None:
return None
if sha1_arr_ptr is None: return None
# 遍历查找匹配的 SHA1
idx_s: int = -1
for i in range(count):
sidx: t.CSizeT = t.CSizeT(i) * 17
sidx: sizet = sizet(i) * 17
cur_sha1: str = sha1_arr_ptr + sidx
if cur_sha1[0] == '\0':
continue
if cur_sha1[0] == '\0': continue
if string.strcmp(cur_sha1, sha1) == 0:
idx_s = i
break
if idx_s < 0:
return None
# 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题)
MAX_RPL: t.CSizeT = 256
ridx: t.CSizeT = t.CSizeT(idx_s) * MAX_RPL
MAX_RPL: sizet = 256
ridx: sizet = sizet(idx_s) * MAX_RPL
rel_path: str = rel_arr_ptr + ridx
if rel_path[0] == '\0':
return None
# 选择基础目录(优先级):
# 1. 条目存储的 includes 根目录 IncDir(绝对路径,多 includes 目录
# 支持):PopulateSha1MapStore 按每条目保存所属根目录,跨模块
# CDefine 查找回退读源时按 IncDir 直接拼路径,避免"首 includes
# 目录"启发式对非首目录文件失效(Gargantua 多 includes 目录:
# stdint.vp 属于 TransPyC/includes,而 IncludesDir=首目录 ViperOS
# → 拼错路径 → 文件不存在 → XMOD-CD 误报根因)。
# 2. rel_path 前缀启发式:以 "lib/" 开头 → App 源文件(SourceDir);
# "includes/" 开头 → includes 文件(去掉前缀);其他无前缀路径
# (如 VKernel 的 "drivers/core/cpu/cpu.vp")先试 SourceDir,
# 文件不存在再试 IncludesDir。
# 选择基础目录(优先级):1) 条目存储的 includes 根目录 IncDir(绝对路径,支持多 includes 目录,避免"首 includes 目录"启发式对非首目录文件失效);2) rel_path 前缀启发式——以 "lib/" 开头 → App 源文件(SourceDir);"includes/" 开头 → includes 文件(去掉前缀);其他无前缀路径先试 SourceDir,不存在再试 IncludesDir
base_dir: str = None
rel_used: str = rel_path
inc_arr_ptr: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
if inc_arr_ptr is not None:
iidx: t.CSizeT = t.CSizeT(idx_s) * StubMerger.MAX_INC_DIR_LEN
iidx: sizet = sizet(idx_s) * StubMerger.MAX_INC_DIR_LEN
inc_dir: str = inc_arr_ptr + iidx
if inc_dir[0] != '\0':
base_dir = inc_dir
@@ -112,26 +75,26 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if base_dir is None:
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
# 构建完整路径: {base_dir}/{rel_used}
base_len: t.CSizeT = string.strlen(base_dir)
rel_len: t.CSizeT = string.strlen(rel_used)
path_len: t.CSizeT = base_len + rel_len + 2
base_len: sizet = string.strlen(base_dir)
rel_len: sizet = string.strlen(rel_used)
path_len: sizet = base_len + rel_len + 2
buf: str = stdlib.malloc(path_len)
if buf is None:
return None
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used)
# 存在性检查:SourceDir 下不存在且 IncludesDir 不同 → 重试 IncludesDir
pf_chk: fileio.File | t.CPtr = fileio.File(buf, fileio.MODE.R)
pf_chk: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
if pf_chk.closed:
pf_chk.close()
if Config.IncludesDir is not None and base_dir != Config.IncludesDir:
stdlib.free(buf)
base_len2: t.CSizeT = string.strlen(Config.IncludesDir)
path_len2: t.CSizeT = base_len2 + rel_len + 2
base_len2: sizet = string.strlen(Config.IncludesDir)
path_len2: sizet = base_len2 + rel_len + 2
buf = stdlib.malloc(path_len2)
if buf is None:
return None
viperlib.snprintf(buf, path_len2, "%s/%s", Config.IncludesDir, rel_used)
pf_chk2: fileio.File | t.CPtr = fileio.File(buf, fileio.MODE.R)
pf_chk2: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
if pf_chk2.closed:
pf_chk2.close()
stdlib.free(buf)
@@ -152,12 +115,12 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
#
# pyi 文件格式: "NAME: t.CDefine = value"
# ============================================================
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy | t.CPtr,
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*,
name: str,
from_imports: str,
imported_modules: str = None,
depth: int = 0,
node: ast.AST | t.CPtr = None) -> t.CInt64T:
node: ast.AST* = None) -> int64:
"""跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)
查找顺序:
@@ -209,13 +172,13 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 与 _lookup_sha1_from_modname_raw 的截断逻辑一致(空格/冒号/NUL 分隔)。
base_mod: str = None
if mod_name_raw is not None:
mbl: t.CSizeT = 0
mbl: sizet = 0
while mod_name_raw[mbl] != ' ' and mod_name_raw[mbl] != '\0' \
and mod_name_raw[mbl] != ':':
mbl += 1
mb_buf: str = pool.alloc(mbl + 1)
if mb_buf is not None:
mi2: t.CSizeT
mi2: sizet
for mi2 in range(mbl):
mb_buf[mi2] = mod_name_raw[mi2]
mb_buf[mbl] = '\0'
@@ -232,7 +195,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 仅 ALL_CAPS 全局唯一宏名才进入本函数(_is_cdefine_name_pattern 已过滤)。
xkey_buf: str = None
if base_mod is not None:
xk_len: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
xkey_buf = pool.alloc(xk_len)
if xkey_buf is not None:
viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod)
@@ -244,7 +207,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
xd_v: double = string.atof(xval)
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v)
else:
xv_i: t.CInt64T = string.atoll(xval)
xv_i: int64 = string.atoll(xval)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
return string.atoll(xval)
@@ -260,22 +223,22 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
self.Gen.TypeGenCDef.set_cdefine_found(0)
# 直接遍历 imported_modules
im_len: t.CSizeT = string.strlen(imported_modules)
im_ci: t.CSizeT = 0
im_len: sizet = string.strlen(imported_modules)
im_ci: sizet = 0
while im_ci < im_len:
while im_ci < im_len and imported_modules[im_ci] == ' ':
im_ci += 1
if im_ci >= im_len:
break
im_start: t.CSizeT = im_ci
im_start: sizet = im_ci
while im_ci < im_len and imported_modules[im_ci] != ' ':
im_ci += 1
im_end: t.CSizeT = im_ci
im_nlen: t.CSizeT = im_end - im_start
im_end: sizet = im_ci
im_nlen: sizet = im_end - im_start
if im_nlen > 0 and im_nlen < 256:
im_buf: str = pool.alloc(im_nlen + 1)
if im_buf is not None:
mk: t.CSizeT = 0
mk: sizet = 0
while mk < im_nlen:
im_buf[mk] = imported_modules[im_start + mk]
mk += 1
@@ -284,12 +247,12 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if sub_fi is not None:
sub_fi[0] = '*'
sub_fi[1] = ':'
mk2: t.CSizeT = 0
mk2: sizet = 0
while mk2 < im_nlen:
sub_fi[2 + mk2] = im_buf[mk2]
mk2 += 1
sub_fi[2 + im_nlen] = '\0'
sub_val: t.CInt64T = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
return sub_val
return -1
@@ -305,18 +268,18 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
return -1
# 6. 构建 vpi 文件路径
td_len: t.CSizeT = string.strlen(temp_dir)
td_len: sizet = string.strlen(temp_dir)
pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "vpi")
if pyi_path is None:
return -1
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
PYI_READ_BUF_SIZE: t.CSizeT = 65536
PYI_READ_BUF_SIZE: sizet = 65536
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
if pyi_buf is None:
stdlib.free(pyi_path)
return -1
pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.R)
pf: rawptr[fileio.File] = fileio.File(pyi_path, fileio.MODE.R)
need_py_fallback: int = 0
bytes_read: LONG = 0
if pf.closed:
@@ -331,8 +294,6 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
else:
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值
# (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT
# 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源
@@ -345,9 +306,9 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 一律回退读源文件。
cd_sig: int = 0
name_has_val: int = 0
cs_name_len: t.CSizeT = string.strlen(name)
cs_total: t.CSizeT = string.strlen(pyi_buf)
cs_i: t.CSizeT = 0
cs_name_len: sizet = string.strlen(name)
cs_total: sizet = string.strlen(pyi_buf)
cs_i: sizet = 0
while cs_i + 9 < cs_total:
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and
pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and
@@ -356,7 +317,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
pyi_buf[cs_i + 8] == 'e'):
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
cd_eq: int = 0
cs_j: t.CSizeT = cs_i + 9
cs_j: sizet = cs_i + 9
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
if pyi_buf[cs_j] == '=':
cd_eq = 1
@@ -365,7 +326,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if cd_eq != 0:
cd_sig = 1
# 定位该行行首,检查是否以 "name:" 开头(目标名带值)
cs_line_start: t.CSizeT = cs_i
cs_line_start: sizet = cs_i
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n':
cs_line_start -= 1
if cs_i - cs_line_start >= cs_name_len + 1:
@@ -408,25 +369,23 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
return -1
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
# 格式: NAME: t.CDefine = value
name_len: t.CSizeT = string.strlen(name)
total_len: t.CSizeT = string.strlen(pyi_buf)
pos: t.CSizeT = 0
result_val: t.CInt64T = 0
name_len: sizet = string.strlen(name)
total_len: sizet = string.strlen(pyi_buf)
pos: sizet = 0
result_val: int64 = 0
result_found: int = 0
res_is_double: int = 0
res_is_str: int = 0
while pos < total_len:
# 找到行首
line_start: t.CSizeT = pos
line_start: sizet = pos
# 找到行尾
while pos < total_len and pyi_buf[pos] != '\n':
pos += 1
line_len: t.CSizeT = pos - line_start
line_len: sizet = pos - line_start
if pos < total_len:
pos += 1 # 跳过 '\n'
@@ -446,7 +405,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 检查行是否包含 "t.CDefine"
# 在行中查找 "t.CDefine"
has_cdefine: int = 0
ci: t.CSizeT = line_start + name_len + 1
ci: sizet = line_start + name_len + 1
while ci + 8 <= line_start + line_len:
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
@@ -460,7 +419,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
continue
# 找到 "=" 后面的值
eq_pos: t.CSizeT = line_start + name_len + 1
eq_pos: sizet = line_start + name_len + 1
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
eq_pos += 1
if eq_pos >= line_start + line_len:
@@ -471,14 +430,14 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / t.CInt(...) 等类型构造函数
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / int(...) 等类型构造函数
# 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
# 如果是,跳过 "t.CXxx(" 前缀,解析括号内的值,忽略结尾 ')'
is_type_ctor: int = 0
if eq_pos + 2 < line_start + line_len:
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
# 找到 '(' 的位置
paren_pos: t.CSizeT = eq_pos + 2
paren_pos: sizet = eq_pos + 2
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(':
paren_pos += 1
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
@@ -502,8 +461,8 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
is_neg = 1
eq_pos += 1
val_str_start: t.CSizeT = eq_pos
val_str_len: t.CSizeT = 0
val_str_start: sizet = eq_pos
val_str_len: sizet = 0
is_hex: int = 0
is_float_val: int = 0
is_name_val: int = 0
@@ -530,7 +489,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
pyi_buf[eq_pos] == '_':
is_name_val = 1
while eq_pos < line_start + line_len:
c_tk: t.CChar = pyi_buf[eq_pos]
c_tk: char = pyi_buf[eq_pos]
if (c_tk >= 'A' and c_tk <= 'Z') or (c_tk >= 'a' and c_tk <= 'z') or \
(c_tk >= '0' and c_tk <= '9') or c_tk == '_':
val_str_len += 1
@@ -539,7 +498,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
break
# 数字/hex 字符续读(字符串值已整体读取,跳过)
while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0:
ch: t.CChar = pyi_buf[eq_pos]
ch: char = pyi_buf[eq_pos]
if is_hex == 1:
if ((ch >= '0' and ch <= '9') or
(ch >= 'a' and ch <= 'f') or
@@ -572,7 +531,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
val_buf[val_str_len] = '\0'
# 左 token 求值
lval: t.CInt64T = 0
lval: int64 = 0
l_is_double: int = 0
ldbl: double = 0.0
if is_str_val != 0:
@@ -598,10 +557,10 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
elif is_hex == 1:
# 手动解析十六进制(跳过 0x 前缀)——64 位累加,
# 0xFFFFFFFF=4294967295 保持全精度(常量生成端 i32 截断回 -1)
hex_result: t.CInt64T = 0
hex_result: int64 = 0
hex_buf: str = val_buf + 2 # 跳过 "0x"
for hi in range(val_str_len - 2):
hc: t.CChar = hex_buf[hi]
hc: char = hex_buf[hi]
hd: int = 0
if hc >= '0' and hc <= '9':
hd = hc - '0'
@@ -668,8 +627,8 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
r_neg = 1
eq_pos += 1
r_start: t.CSizeT = eq_pos
r_len: t.CSizeT = 0
r_start: sizet = eq_pos
r_len: sizet = 0
r_hex: int = 0
r_name: int = 0
if eq_pos + 1 < line_start + line_len:
@@ -682,7 +641,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
pyi_buf[eq_pos] == '_':
r_name = 1
while eq_pos < line_start + line_len:
c_r: t.CChar = pyi_buf[eq_pos]
c_r: char = pyi_buf[eq_pos]
if (c_r >= 'A' and c_r <= 'Z') or (c_r >= 'a' and c_r <= 'z') or \
(c_r >= '0' and c_r <= '9') or c_r == '_':
r_len += 1
@@ -690,7 +649,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
else:
break
while eq_pos < line_start + line_len and r_name == 0:
c_r2: t.CChar = pyi_buf[eq_pos]
c_r2: char = pyi_buf[eq_pos]
if r_hex == 1:
if ((c_r2 >= '0' and c_r2 <= '9') or
(c_r2 >= 'a' and c_r2 <= 'f') or
@@ -714,7 +673,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
for r_i in range(r_len):
r_buf[r_i] = pyi_buf[r_start + r_i]
r_buf[r_len] = '\0'
rval: t.CInt64T = 0
rval: int64 = 0
if r_name != 0:
# True/False 布尔字面量 → 1/0
if string.strcmp(r_buf, "True") == 0:
@@ -727,10 +686,10 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 名称未注册:无法求值,跳过该行
continue
elif r_hex != 0:
rhex_v: t.CInt64T = 0
rhex_v: int64 = 0
rh_buf: str = r_buf + 2
for rh_i in range(r_len - 2):
rc: t.CChar = rh_buf[rh_i]
rc: char = rh_buf[rh_i]
rd: int = 0
if rc >= '0' and rc <= '9':
rd = rc - '0'
@@ -796,39 +755,51 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if result_found == 0:
# base_mod 已在函数开头计算(mod_name_raw 去前导点),
# 供子模块递归构建 "base_mod.X" 全名。
name_len_rc: t.CSizeT = string.strlen(name)
rc_pos: t.CSizeT = 0
name_len_rc: sizet = string.strlen(name)
rc_pos: sizet = 0
while rc_pos < total_len and result_found == 0:
# 找到行首
rc_line_start: t.CSizeT = rc_pos
rc_line_start: sizet = rc_pos
# 找到行尾
while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
rc_pos += 1
rc_line_len: t.CSizeT = rc_pos - rc_line_start
rc_line_len: sizet = rc_pos - rc_line_start
if rc_pos < total_len:
rc_pos += 1
# 检查行是否以 "from ." 开头(相对导入子模块)
# 检查行是否以 "from " 开头(模块导入)
if rc_line_len < 12:
continue
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.':
pyi_buf[rc_line_start+4] != ' ':
continue
# 提取子模块名(从 rc_line_start+6 开始,到空格/tab 为止)
rc_sub_start: t.CSizeT = rc_line_start + 6
rc_sub_end: t.CSizeT = rc_sub_start
# 提取模块路径 token(从 rc_line_start+5 到空格/tab 为止,跳过前导点)
# 两种写法都要支持:
# 相对导入 `from .base import X` → 拼 base_mod.base
# 绝对导入 `from vqt6._types import *` → 直接用 token 自身
# 此前只认相对导入(硬要求 rc_line_start+5 是 '.'),绝对写法被
# 直接跳过 → vqt6 包(__init__.vp 用 `from vqt6._types import *`
# 重导出 QT_ALIGN_* 等)里的 CDefine 全部查不到 →
# "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'"。
rc_is_rel: int = 0
rc_sub_start: sizet = rc_line_start + 5
while rc_sub_start < rc_line_start + rc_line_len and \
pyi_buf[rc_sub_start] == '.':
rc_is_rel = 1
rc_sub_start += 1
rc_sub_end: sizet = rc_sub_start
while rc_sub_end < rc_line_start + rc_line_len and \
pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
rc_sub_end += 1
rc_sub_len: t.CSizeT = rc_sub_end - rc_sub_start
rc_sub_len: sizet = rc_sub_end - rc_sub_start
if rc_sub_len == 0:
continue
# 检查行是否包含 "import"
rc_has_import: int = 0
rc_ipos: t.CSizeT = rc_sub_end
rc_ipos: sizet = rc_sub_end
while rc_ipos + 6 <= rc_line_start + rc_line_len:
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
@@ -842,14 +813,14 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 检查 NAME 是否在导入列表中,或导入是 *
# 处理多行导入:from .X import (... NAME, ...)
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
rc_imp_start: t.CSizeT = rc_ipos + 6
rc_imp_start: sizet = rc_ipos + 6
rc_star: int = 0
rc_name_match: int = 0
# 确定扫描范围:单行或跨行(括号内)
rc_scan_end: t.CSizeT = rc_line_start + rc_line_len
rc_scan_end: sizet = rc_line_start + rc_line_len
rc_has_paren: int = 0
rc_pp: t.CSizeT = rc_imp_start
rc_pp: sizet = rc_imp_start
while rc_pp < rc_line_start + rc_line_len:
if pyi_buf[rc_pp] == '(':
rc_has_paren = 1
@@ -861,7 +832,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 多行导入:从 "(" 之后扫描到 ")" 为止
rc_scan_end = rc_pp + 1
rc_paren_closed: int = 0
rc_sp: t.CSizeT = rc_pp + 1
rc_sp: sizet = rc_pp + 1
while rc_sp < total_len and rc_paren_closed == 0:
if pyi_buf[rc_sp] == ')':
rc_paren_closed = 1
@@ -872,15 +843,15 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
rc_scan_end = total_len
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
rc_cpos: t.CSizeT = rc_imp_start
rc_cpos: sizet = rc_imp_start
while rc_cpos < rc_scan_end:
rc_ch: t.CChar = pyi_buf[rc_cpos]
rc_ch: char = pyi_buf[rc_cpos]
if rc_ch == '*':
rc_star = 1
# 检查 NAME 是否匹配(作为完整单词)
if rc_cpos + name_len_rc <= rc_scan_end:
rc_match: int = 1
rc_ki: t.CSizeT
rc_ki: sizet
for rc_ki in range(name_len_rc):
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
rc_match = 0
@@ -891,18 +862,18 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if rc_cpos == rc_imp_start:
rc_before_ok = 1
else:
rc_before_ch: t.CChar = pyi_buf[rc_cpos - 1]
rc_before_ch: char = pyi_buf[rc_cpos - 1]
if rc_before_ch == ' ' or rc_before_ch == '\t' or \
rc_before_ch == '(' or rc_before_ch == ',' or \
rc_before_ch == '\n' or rc_before_ch == '\r':
rc_before_ok = 1
# 检查后字符是否是非标识符字符
rc_after_ok: int = 0
rc_after_pos: t.CSizeT = rc_cpos + name_len_rc
rc_after_pos: sizet = rc_cpos + name_len_rc
if rc_after_pos >= rc_scan_end:
rc_after_ok = 1
else:
rc_after_ch: t.CChar = pyi_buf[rc_after_pos]
rc_after_ch: char = pyi_buf[rc_after_pos]
if rc_after_ch == ' ' or rc_after_ch == '\t' or \
rc_after_ch == ')' or rc_after_ch == ',' or \
rc_after_ch == '\r' or rc_after_ch == '\n':
@@ -918,23 +889,42 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
if rc_sub_buf is None:
continue
rc_si: t.CSizeT
rc_si: sizet
for rc_si in range(rc_sub_len):
rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
rc_sub_buf[rc_sub_len] = '\0'
# 构建完整子模块名 base_mod.X
rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1
rc_full_mod: str = pool.alloc(rc_full_len)
if rc_full_mod is None:
continue
viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf)
# 构建完整模块名:
# 相对导入(rc_is_rel=1)→ <当前模块所属包> + "." + token
# 基准必须是"包"(rel_path 的目录部分)而不是模块名本身:
# pkg/sub/mod.vp 里的 `from .X import Y` 指向 pkg.sub.X,
# 用 base_mod("pkg.sub.mod")会拼成 pkg.sub.mod.X(错)。
# __init__.vp 时包名 == 模块名,此前只在这种情况碰巧正确
# (CallGenBase._lookup_reexport_from_vpi 用的是包名,此处对齐)。
# 绝对导入(rc_is_rel=0)→ 直接用 token(vqt6._types)
# 自导入(目标模块 == base_mod)跳过,避免无意义递归
rc_full_mod: str = None
if rc_is_rel != 0:
rc_base_mod: str = base_mod
rc_pkg: str = self.Gen.CallGenBase._module_package_from_sha1(pool, sha1)
if rc_pkg is not None:
rc_base_mod = rc_pkg
rc_full_len: sizet = string.strlen(rc_base_mod) + 1 + rc_sub_len + 1
rc_full_buf: str = pool.alloc(rc_full_len)
if rc_full_buf is None:
continue
viperlib.snprintf(rc_full_buf, rc_full_len, "%s.%s", rc_base_mod, rc_sub_buf)
rc_full_mod = rc_full_buf
else:
if string.strcmp(rc_sub_buf, base_mod) == 0:
continue
rc_full_mod = rc_sub_buf
# 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼
# .base → ast.base.base),说明子模块递归在重复追加同一模块段
# (模块间循环 re-export / 后缀匹配容错命中自身),跳过该次递归
# (NewAstTest CONST_INT 查找 dump:ast.base.base.base 无限增长根因)。
rc_blen: t.CSizeT = string.strlen(base_mod)
rc_blen: sizet = string.strlen(base_mod)
if rc_blen > rc_sub_len + 1:
rc_tail: str = base_mod + (rc_blen - rc_sub_len)
if rc_tail[0] == '.' and \
@@ -942,7 +932,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
continue
# 构建临时 from_imports "name:full_mod"
rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
rc_fi_len: sizet = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
rc_fi: str = pool.alloc(rc_fi_len)
if rc_fi is None:
continue
@@ -961,22 +951,22 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
# 需要遍历 imported_modules 逐个查找
if result_found == 0 and imported_modules is not None:
im2_len: t.CSizeT = string.strlen(imported_modules)
im2_ci: t.CSizeT = 0
im2_len: sizet = string.strlen(imported_modules)
im2_ci: sizet = 0
while im2_ci < im2_len and result_found == 0:
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
im2_ci += 1
if im2_ci >= im2_len:
break
im2_start: t.CSizeT = im2_ci
im2_start: sizet = im2_ci
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
im2_ci += 1
im2_end: t.CSizeT = im2_ci
im2_nlen: t.CSizeT = im2_end - im2_start
im2_end: sizet = im2_ci
im2_nlen: sizet = im2_end - im2_start
if im2_nlen > 0 and im2_nlen < 256:
im2_buf: str = pool.alloc(im2_nlen + 1)
if im2_buf is not None:
mk3: t.CSizeT = 0
mk3: sizet = 0
while mk3 < im2_nlen:
im2_buf[mk3] = imported_modules[im2_start + mk3]
mk3 += 1
@@ -985,12 +975,12 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if sub_fi2 is not None:
sub_fi2[0] = '*'
sub_fi2[1] = ':'
mk4: t.CSizeT = 0
mk4: sizet = 0
while mk4 < im2_nlen:
sub_fi2[2 + mk4] = im2_buf[mk4]
mk4 += 1
sub_fi2[2 + im2_nlen] = '\0'
sub_val2: t.CInt64T = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
result_val = sub_val2
result_found = 1
@@ -1003,7 +993,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# 字符串 CDefine:值已在匹配循环 register_cdefine_string(val_buf
# 是命中行的字符串内容,pool 分配跨函数有效),此处只写跨模块缓存
if base_mod is not None:
ck_len_s: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
ck_len_s: sizet = string.strlen(name) + string.strlen(base_mod) + 2
ckey_s: str = pool.alloc(ck_len_s)
if ckey_s is not None:
viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod)
@@ -1014,7 +1004,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# key 与入口查询一致:"name@源模块"(用截断后的 base_mod,
# 与 mod_name_raw 一致——两者都是去点后的模块名,保证 key 匹配)
if base_mod is not None:
ck_len: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
ck_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
ckey_buf: str = pool.alloc(ck_len)
if ckey_buf is not None:
viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod)
@@ -1025,7 +1015,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
else:
# 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度)
if base_mod is not None:
ck_len2: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
ck_len2: sizet = string.strlen(name) + string.strlen(base_mod) + 2
ckey2: str = pool.alloc(ck_len2)
if ckey2 is not None:
viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod)
@@ -1057,9 +1047,9 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
if name is None:
return 0
has_upper: int = 0
i: t.CSizeT = 0
i: sizet = 0
while name[i] != '\0':
c: t.CChar = name[i]
c: char = name[i]
if c >= 'a' and c <= 'z':
return 0
if c >= 'A' and c <= 'Z':
@@ -1069,14 +1059,31 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
# ============================================================
# 翻译变量引用(Name 节点)→ load
# ============================================================
def _const_int_for_cdefine(self, pool: memhub.MemBuddy | t.CPtr,
value: t.CInt64T) -> llvmlite.Value | t.CPtr:
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*,
value: int64) -> llvmlite.Value*:
"""CDefine 整数常量 → LLVM 常量(对齐 TPC TypeConvert 的 64 位升级)
**使用处宽度("W_min + 使用处宽度"规则)与物化宽度的关系**(与 TPC 同口径,
对照表见 Debugger/define-constants.md):
· 本函数是"取不到使用处宽度"的物化点(调用方 ExprGenName/ExprGenAttr 只有
常量名与值,没有目标类型)⇒ 按语言默认整型宽度物化:能落进 32 位 ⇒ i32,
否则 i64。**不**降到 W_min——降到 i8/i16 会把窄立即数喂进 i32 上下文与
C 默认实参提升(printf 变参),属静默降级/ABI 破坏。
· "使用处更宽 ⇒ 以该宽度物化"由**消费端**实现:translate_binop 按较宽侧
zext/sext/coerce_to_type 对齐,赋值/形参按目标存储类型 coerce。
· "使用处更窄 ⇒ fail-fast"在**注册处与声明处**实现:
AssignGenMixin.require_cdefine_storable(显式类型 T 的位宽)与
require_cdefine_use_width(`a: T = C`);超 64 位字面量由
_int_text_exceeds_64 以**源码文本**判定(lexer 的 atoll 会在字面量层
回绕,回绕后的 int64 不可信,故本函数不承担溢出判定)。
规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义):
值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量
(如 sys.maxsize = 9223372036854775807);
否则 → i32 常量,内部 t.CInt 截断(0xFFFFFFFF → -1,保持既有
值低于 i32 下限(< -2147483648)→ i64 全精度常量。符号位为 1 的 64 位
模式(如 MEMBUDDY_CANARY_PATTERN = 0xA5A5A5A5A5A5A5A5)以 %lld 文本
存回即负数 -6510615555426900571,仅按 u32 截断回比较会漏判;
否则 → i32 常量,内部 int 截断(0xFFFFFFFF → -1,保持既有
INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295
生成非法 LLVM IR 文本 "i32 4294967295")。
@@ -1084,8 +1091,103 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
(> 0x7FFFFFFF)在字面量层回绕成 i32(sext 后等于 -1),阈值恒成立导致所有
正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变
i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。
改用截断回比较:t.CUInt32T(value) 回绕后还原,与 value 不等即超 u32 范围。
改用截断回比较:uint32(value) 回绕后还原,与 value 不等即超 u32 范围。
负数侧漏判的现场:`(0xA5A5A5A5 << 32) | 0xA5A5A5A5`(includes/memhub.vp:123)
经 CDefine 求值(ExprGenCdef 内 lval << rval 的 int64 回绕)得
-6510615555426900571,再经 TypeGenCDef.register_cdefine_constant 的
"%lld" 文本存回(TypeGenCDef.vp:156)仍为负数 → 原判据物化成 i32
-1515870811:写入路径 inttoptr i32(x86 movl 零扩展)实存
0x00000000A5A5A5A5,比较/打印路径 sext i32→i64 期望 0xFFFFFFFFA5A5A5A5
⇒ MemBuddy canary 校验 rc=6 假阳性。下限判据与既有
ExprGenConst.vp:108 / CallGenLeafKit.vp:1477 的写法一致。
"""
if value >= 0 and t.CUInt64T(t.CUInt32T(value)) != value:
if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648:
return llvmlite.const_int64(pool, value)
return llvmlite.const_int32(pool, t.CInt(value))
return llvmlite.const_int32(pool, int(value))
# ============================================================
# cdefine_use_width_value - "使用处宽度"下的 CDefine 常量物化
# (与 TPC HandlesExpr._HandleNameLlvm + ConstWidthHint 同口径)
# ============================================================
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
"""把 CDefine 值归一到 bits 位的**有符号表示**(LLVM 整数字面量口径)。
前提:调用方已用 W_min 判据保证 value 落在
[-(2^(bits-1)), 2^bits-1](两解释口径)。非负但最高位为 1 的值
(如 0xFFFFFFFF)转成同一位模式的负数(i32 4294967295 → i32 -1),
与 _const_int_for_cdefine 既有的 i32 截断口径一致,避免生成非法 IR 文本。
"""
if bits == 8:
if value > 127:
return value - 256
return value
if bits == 16:
if value > 32767:
return value - 65536
return value
if bits == 32:
if value > 2147483647:
return value - 4294967296
return value
return value
def cdefine_use_width_value(self, pool: memhub.MemBuddy*,
node: ast.AST*, name: str,
value: int64,
target_ty: llvmlite.LLVMType*,
use_ctx: str = None) -> llvmlite.Value*:
"""CDefine 整数常量在**已知使用处类型**时的物化(与 TPC 同口径)。
返回 None ⇒ "取不到使用处宽度"(调用方沿用 _const_int_for_cdefine 的
语言默认整型宽度 32/64)。只当 target_ty 是 i8/i16/i32/i64 时启用
(i1 是比较/逻辑结果、指针不是整数使用处 ⇒ 落回默认,否则 `C == 0`
之类会拿到位宽 1 而误报"使用处宽度不足"):
使用处更宽(bits >= W_min)⇒ 按 bits 就地物化(立即数升宽无损);
使用处更窄(bits < W_min)⇒ fail-fast(替换静默截断)。
只被 CDefine 整数物化点调用 ⇒ 不改变非常量实参/返回值的既有 IR。
use_ctx:**真实站点种类**标签(如 "局部变量注解" / "模块级全局" / "形参" /
"返回注解"),由 `translate_value` 的调用方按站点下传;None ⇒ 报文不写
括注(**不得**再写死"形参 / 返回类型"——AnnAssign 的 `a: T = C` 现场也被
本函数拦到时,硬编码标签会给出错误站点)。
"""
bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(target_ty)
if bits != 8 and bits != 16 and bits != 32 and bits != 64:
return None
wmin: int = self.Gen.AssignGen.define_min_width(value)
if bits < wmin:
# "该使用处可表示的范围"与判据同一口径 = TPC `DefineWidthLoHi`
# = [-(2^(bits-1)), 2^bits - 1](有符号/无符号两种解释的并集;
# 判据本身只按**位宽**比较,故报文必须报"位宽 w 的可接受值域")。
# 字面量纪律见 AssignGenMixin.define_min_width:2147483648 /
# 4294967295 会被 TPC 压成 i32 并在提升时 sext,故用安全字面量
# 2147483647 的算术等价式构造。
m31: int64 = 2147483647
lo: int64 = -m31 - 1
hi: int64 = m31 * 2 + 1
if bits == 8:
lo = -128
hi = 255
elif bits == 16:
lo = -32768
hi = 65535
ctx: str = ""
if use_ctx is not None:
# 本语言的 str 是 i8*,**没有** `str + str` 的拼接语义(`+` 会退化
# 成裸指针 add → "invalid operand type for instruction")。
# 构建字符串一律用本仓库惯用法:f-string 插值 / string.strcat /
# viperlib.snprintf。
ctx = f"({use_ctx})"
self.Gen.TypeGenErr.fatal_error(
node,
f"[TPV] define 常量使用处宽度不足: 常量 '{name}' 的实际值 {value} "
f"需要至少 {wmin} 位(最小可容纳宽度 W_min),"
f"但使用处{ctx}只提供 {bits} 位"
f"(该位宽按有符号/无符号两种解释可接受的范围: {lo}..{hi})。"
f"禁止静默截断/降级,已终止编译。"
f"修法: 把使用处{ctx}的类型加宽到至少 {wmin} 位"
f"(如 t.CInt{wmin}T / CUInt{wmin}T 及更宽类型),或改用更小的常量值。",
"TypeError")
return None
return llvmlite.const_int(pool, bits, self._cdefine_bits_value(value, bits))
@@ -1,50 +1,32 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.CallGenBase as CallGenBase
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.Translator as HT
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# ============================================================
# docstring 登记表容量常量(与 ExprGenMixin 保持一致,本地副本避免
# 跨模块 CDefine 查找 bug:ExprGenCls(C) 在 ExprGenMixin(M) 之前按
# 文件序编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
# docstring 登记表容量常量(与 ExprGenMixin 保持一致,存疑
# ============================================================
DOC_MAX: t.CDefine = 512
DOC_KEY_SZ: t.CDefine = 192
DOC_VAL_SZ: t.CDefine = 4096
define DOC_MAX = 512
define DOC_KEY_SZ = 192
define DOC_VAL_SZ = 4096
# ============================================================
# ExprGenCls - ExprGenMixin 拆分(阶段 D):类名推断/docstring 登记
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenCls(ExprGenBase.ExprGenBase):
novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
"""类名推断/docstring 登记"""
# ============================================================
@@ -57,42 +39,32 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
# ============================================================
def _extract_docstring_from_body(self, body: list[ast.AST | t.CPtr] | t.CPtr) -> str:
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
if body is None or len(body) == 0:
return None
first: ast.AST | t.CPtr = body.get(0)
if first is None or first.kind() != ast.ASTKind.Expr:
return None
ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(first)
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant:
return None
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(ex.value)
if cn is None or cn.const_kind != ast.CONST_STR:
return None
if body is None or len(body) == 0: return None
first: ast.AST* = body.get(0)
if first is None or first.kind() != ast.ASTKind.Expr: return None
ex: rawptr[ast.Expr] = rawptr[ast.Expr](first)
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant: return None
cn: ast.Constant* = ast.Constant*(ex.value)
if cn is None or cn.const_kind != ast.CONST_STR: return None
return cn.str_val
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
if module_sha1 is None or symbol is None or doc is None:
return
if string.strlen(doc) == 0:
return
if module_sha1 is None or symbol is None or doc is None: return
if string.strlen(doc) == 0: return
if self.DocKeys is None:
self.DocKeys = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_KEY_SZ)
self.DocValues = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_VAL_SZ)
if self.DocKeys is None or self.DocValues is None:
return
self.DocKeys = CompilerFactory.factory.PermPool.alloc(DOC_MAX * DOC_KEY_SZ)
self.DocValues = CompilerFactory.factory.PermPool.alloc(DOC_MAX * DOC_VAL_SZ)
if self.DocKeys is None or self.DocValues is None: return
string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ)
string.memset(self.DocValues, 0, DOC_MAX * DOC_VAL_SZ)
if self.DocCount >= DOC_MAX:
return
ml: t.CSizeT = string.strlen(module_sha1)
sl: t.CSizeT = string.strlen(symbol)
if self.DocCount >= DOC_MAX: return
ml: sizet = string.strlen(module_sha1)
sl: sizet = string.strlen(symbol)
key_buf: bytes = stdlib.malloc(ml + sl + 2)
if key_buf is None:
return
if key_buf is None: return
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
i: int
for i in range(self.DocCount):
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
stdlib.free(key_buf)
@@ -103,86 +75,75 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
stdlib.free(key_buf)
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
if self.DocKeys is None or module_sha1 is None or symbol is None:
return None
ml: t.CSizeT = string.strlen(module_sha1)
sl: t.CSizeT = string.strlen(symbol)
if self.DocKeys is None or module_sha1 is None or symbol is None: return None
ml: sizet = string.strlen(module_sha1)
sl: sizet = string.strlen(symbol)
key_buf: bytes = stdlib.malloc(ml + sl + 2)
if key_buf is None:
return None
if key_buf is None: return None
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
i: int
for i in range(self.DocCount):
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
stdlib.free(key_buf)
return self.DocValues + i * DOC_VAL_SZ
stdlib.free(key_buf)
return None
def extract_module_docstrings(self, tree: ast.AST | t.CPtr, module_sha1: str) -> None:
def extract_module_docstrings(self, tree: ast.AST*, module_sha1: str) -> None:
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
if tree is None or module_sha1 is None:
return
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is None:
return
cn: t.CSizeT = len(ch)
if tree is None or module_sha1 is None: return
ch: list[ast.AST*]* = tree.children
if ch is None: return
cn: sizet = len(ch)
for ci in range(cn):
child: ast.AST | t.CPtr = ch.get(ci)
if child is None:
continue
child: ast.AST* = ch.get(ci)
if child is None: continue
kd: int = child.kind()
if kd == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
fd: ast.FunctionDef* = ast.FunctionDef*(child)
if fd is not None and fd.name is not None:
fd_body: list[ast.AST | t.CPtr] | t.CPtr = fd.children
fd_body: list[ast.AST*]* = fd.children
fdoc: str = self._extract_docstring_from_body(fd_body)
if fdoc is not None:
self.register_docstring(module_sha1, fd.name, fdoc)
elif kd == ast.ASTKind.ClassDef:
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
if cd is None or cd.name is None:
continue
cd_body0: list[ast.AST | t.CPtr] | t.CPtr = cd.children
cd: ast.ClassDef* = ast.ClassDef*(child)
if cd is None or cd.name is None: continue
cd_body0: list[ast.AST*]* = cd.children
cdoc: str = self._extract_docstring_from_body(cd_body0)
if cdoc is not None:
self.register_docstring(module_sha1, cd.name, cdoc)
# 类方法 docstring(key="{ClassName}.{method}")
cd_body: list[ast.AST | t.CPtr] | t.CPtr = cd.children
cd_body: list[ast.AST*]* = cd.children
if cd_body is not None:
cbn: t.CSizeT = len(cd_body)
cbn: sizet = len(cd_body)
for mi in range(cbn):
m: ast.AST | t.CPtr = cd_body.get(mi)
if m is None or m.kind() != ast.ASTKind.FunctionDef:
continue
mfd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m)
if mfd is None or mfd.name is None:
continue
m: ast.AST* = cd_body.get(mi)
if m is None or m.kind() != ast.ASTKind.FunctionDef: continue
mfd: ast.FunctionDef* = ast.FunctionDef*(m)
if mfd is None or mfd.name is None: continue
mdoc: str = self._extract_docstring_from_body(mfd.children)
if mdoc is not None:
cl_nl: t.CSizeT = string.strlen(cd.name)
mt_nl: t.CSizeT = string.strlen(mfd.name)
cl_nl: sizet = string.strlen(cd.name)
mt_nl: sizet = string.strlen(mfd.name)
sym_buf: bytes = stdlib.malloc(cl_nl + mt_nl + 2)
if sym_buf is not None:
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
self.register_docstring(module_sha1, sym_buf, mdoc)
stdlib.free(sym_buf)
def _resolve_doc_symbol_name(self, trans: HT.Translator | t.CPtr, name_id: str,
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str,
out_mod: str, out_sym: str) -> int:
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
"""
if trans is None or name_id is None or out_mod is None or out_sym is None:
return 0
if trans.ModuleSha1 is None:
return 0
if trans is None or name_id is None or out_mod is None or out_sym is None: return 0
if trans.ModuleSha1 is None: return 0
# 1. 本地变量(函数变量/全局变量)
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
se: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(name_id)
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(name_id)
if se is not None:
if se.ModuleSha1 is not None:
string.strcpy(out_mod, se.ModuleSha1)
@@ -203,8 +164,8 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
def _resolve_doc_symbol_attr(self, trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
def _resolve_doc_symbol_attr(self, trans: HT.Translator*,
at: ast.Attribute*,
out_mod: str, out_sym: str) -> int:
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
@@ -212,28 +173,24 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
remote.RemoteClass.remote_method / LocalClass.local_method 等。
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
"""
if trans is None or at is None or out_mod is None or out_sym is None:
return 0
if trans.ModuleSha1 is None:
return 0
if trans is None or at is None or out_mod is None or out_sym is None: return 0
if trans.ModuleSha1 is None: return 0
# 收集 Attribute 链(最外层 attr 在前)
DOC_CHAIN_MAX: t.CInt = 8
DOC_CHAIN_MAX: int = 8
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
if items is None:
return 0
if items is None: return 0
n_items: int = 0
cur: ast.AST | t.CPtr = (ast.AST | t.CPtr)(at)
cur: ast.AST* = (ast.AST*)(at)
while cur is not None and cur.kind() == ast.ASTKind.Attribute and n_items < DOC_CHAIN_MAX:
cat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cur)
if cat.attr is None:
break
cat: ast.Attribute* = ast.Attribute*(cur)
if cat.attr is None: break
string.strcpy(items + n_items * 128, cat.attr)
n_items += 1
cur = cat.value
# 最左应为 Name
head_id: str = None
if cur is not None and cur.kind() == ast.ASTKind.Name:
hnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cur)
hnm: ast.Name* = ast.Name*(cur)
head_id = hnm.id
if head_id is None:
stdlib.free(items)
@@ -254,8 +211,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
string.strcpy(out_sym, head_id)
stdlib.free(items)
return 1
total: t.CSizeT = 0
j: int
total: sizet = 0
for j in range(n_items):
total += string.strlen(items + j * 128) + 1
sym_buf: bytes = stdlib.malloc(total + 1)
@@ -273,7 +229,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
stdlib.free(items)
return 1
# 变量/类:symbol = head_id + '.' + items 反转
total2: t.CSizeT = string.strlen(head_id)
total2: sizet = string.strlen(head_id)
for j in range(n_items):
total2 += string.strlen(items + j * 128) + 1
sym2: bytes = stdlib.malloc(total2 + 1)
@@ -285,7 +241,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
for j in range(n_items):
string.strcat(sym2, ".")
string.strcat(sym2, items + (n_items - 1 - j) * 128)
se2: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(head_id)
se2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(head_id)
if se2 is not None and se2.ModuleSha1 is not None:
string.strcpy(out_mod, se2.ModuleSha1)
else:
@@ -310,39 +266,30 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
# Attribute → 字段注解类名(_resolve_attr_field_class)
# 仅采用已注册结构体;无法精确推断返回 None(fail-fast 由调用方暴露)。
# ============================================================
def infer_expr_class(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
def infer_expr_class(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator*) -> str:
"""从表达式推断结构体类名,无法推断返回 None"""
if node is None or trans is None:
return None
if node is None or trans is None: return None
k: int = node.kind()
# Call: 类实例化 / 方法调用
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl is None or cl.func is None:
return None
cl: ast.Call* = ast.Call*(node)
if cl is None or cl.func is None: return None
if cl.func.kind() == ast.ASTKind.Name:
cand: str = ((ast.Name | t.CPtr)(cl.func)).id
if cand is None:
return None
if self.Gen.StructGen.find_struct(cand) is not None:
return cand
cand: str = (ast.Name*(cl.func)).id
if cand is None: return None
if self.Gen.StructGen.find_struct(cand) is not None: return cand
return None
if cl.func.kind() == ast.ASTKind.Attribute:
# 方法调用 a.cross(b):vpi 返回注解缓存精确解析返回类名
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3')。
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if ca is None or ca.attr is None or ca.value is None:
return None
ca: ast.Attribute* = ast.Attribute*(cl.func)
if ca is None or ca.attr is None or ca.value is None: return None
# 模块级函数调用(Module.func()):接收者是已导入模块名而非类对象
# (CompilerFactory.get_perm_pool())。返回注解可能是联合类型
# (memhub.MemBuddy | t.CPtr),查 vpi 模块函数返回注解(cls=None)
# 提取返回类,解决链式方法调用 .alloc() 推断失败退化裸名 @alloc
# (链接 undefined symbol: alloc 根因)。
if ca.value.kind() == ast.ASTKind.Name:
mod_nm2: str = ((ast.Name | t.CPtr)(ca.value)).id
mod_nm2: str = (ast.Name*(ca.value)).id
if mod_nm2 is not None and trans._from_imports is not None:
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(
pool, trans._from_imports, mod_nm2)
@@ -359,10 +306,8 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
if ret_cls2 is not None:
return ret_cls2
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
if obj_cls is None:
return None
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0:
return None
if obj_cls is None: return None
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: return None
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
if cls_sha1 is None and trans.ModuleSha1 is not None:
cls_sha1 = trans.ModuleSha1
@@ -374,24 +319,19 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
temp_dir = self.Gen.CallGenBase.TempDir
if temp_dir is not None:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
annot_ret: str = VpiReader._lookup_vpi_ret_annot(
cls_sha1, obj_cls, ca.attr)
annot_ret: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, obj_cls, ca.attr)
if annot_ret is not None:
ret_cls = self._extract_annot_class_name(pool, annot_ret)
return ret_cls
return None
# Name: 源变量注解
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None or nm.id is None:
return None
ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None:
return None
acn_n: str = ve.AnnotClassName
if acn_n is None:
return None
nm: ast.Name* = ast.Name*(node)
if nm is None or nm.id is None: return None
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if ve is None: return None
acn_n: str = self.Gen.VarGen.var_annot_class_name(ve)
if acn_n is None: return None
# 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带
# 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回
# "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/
@@ -403,26 +343,20 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
return acn_n
# BinOp/UnaryOp: 运算符重载返回同类对象
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop is None or bop.left is None:
return None
bop: ast.BinOp* = ast.BinOp*(node)
if bop is None or bop.left is None: return None
dunder: str = self.Gen.ExprGenOps._binop_to_dunder(bop.op)
if dunder is None:
return None
if dunder is None: return None
lhs_cls: str = self.infer_expr_class(pool, mod, bop.left, trans)
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0:
return None
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0: return None
return lhs_cls
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop is None or uop.operand is None:
return None
uop: ast.UnaryOp* = ast.UnaryOp*(node)
if uop is None or uop.operand is None: return None
dunder: str = self.Gen.ExprGenOps._unaryop_to_dunder(uop.op)
if dunder is None:
return None
if dunder is None: return None
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0:
return None
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: return None
return opr_cls
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
if k == ast.ASTKind.Attribute:
@@ -435,23 +369,18 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
# SHA1(stub 中声明)即视为存在。仅在确实存在时采用推断类名,
# 避免把原生运算误标为类类型。
# ============================================================
def _cls_has_dunder(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str, dunder: str) -> int:
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, class_name: str, dunder: str) -> int:
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
if class_name is None or dunder is None or mod is None:
return 0
if class_name is None or dunder is None or mod is None: return 0
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return 0
if lookup_name is None: return 0
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None:
return 1
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None: return 1
# 继承链祖先查找
mro_ptr: t.CSizeT | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, class_name)
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
anc: str = rawptr(mro_ptr[mi])
if anc is not None:
anc_lookup: str = pool.alloc(128)
if anc_lookup is not None:
@@ -460,8 +389,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
return 1
mi += 1
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
if self.Gen.StructGen.get_struct_sha1(class_name) is not None:
return 1
if self.Gen.StructGen.get_struct_sha1(class_name) is not None: return 1
return 0
# ============================================================
# _extract_annot_class_name - 从返回注解文本提取结构体类名
@@ -471,28 +399,28 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
# 多候选时取首个命中段。
# ============================================================
def _extract_annot_class_name(self, pool: memhub.MemBuddy | t.CPtr,
annot: str) -> str:
def _extract_annot_class_name(self, pool: memhub.MemBuddy*, annot: str) -> str:
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
if pool is None or annot is None:
return None
alen: t.CSizeT = string.strlen(annot)
if alen == 0:
return None
if pool is None or annot is None: return None
alen: sizet = string.strlen(annot)
if alen == 0: return None
seg: str = pool.alloc(256)
if seg is None:
return None
start: t.CSizeT = 0
i: t.CSizeT
if seg is None: return None
start: sizet = 0
i: sizet
for i in range(alen):
if annot[i] == '|':
s: t.CSizeT = start
e: t.CSizeT = i
s: sizet = start
e: sizet = i
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
# `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'):
e -= 1
seglen: t.CSizeT = e - s
seglen: sizet = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
@@ -507,13 +435,17 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
return seg_bare2
start = i + 1
# 最后一段(无 '|' 后缀)
s: t.CSizeT = start
e: t.CSizeT = alen
s: sizet = start
e: sizet = alen
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
# `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'):
e -= 1
seglen: t.CSizeT = e - s
seglen: sizet = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
@@ -1,4 +1,3 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
@@ -8,49 +7,35 @@ import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.Translator as HT
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# ExprGenConst - ExprGenMixin 拆分(阶段 D):常量/全局字符串/list 字面量翻译
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenConst(ExprGenBase.ExprGenBase):
novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
"""常量/全局字符串/list 字面量翻译"""
# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
def create_global_string(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
str_val: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""创建全局字符串常量并返回 i8* bitcast"""
escaped: str = self.escape_llvm_string(pool, str_val)
if escaped is None:
return None
if escaped is None: return None
slen: t.CSizeT = string.strlen(str_val)
slen: sizet = string.strlen(str_val)
count: int = slen + 1
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
arr_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, count)
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
str_idx: int = trans._label_counter
@@ -65,7 +50,7 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
else:
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, gv_name, arr_ty)
if gv is None:
return None
llvmlite.module_add_global(mod, gv)
@@ -77,70 +62,69 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
gv.Linkage = "internal"
gv.Initializer = escaped
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
arr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, arr_ty)
gv_ref_name: str = pool.alloc(64)
if gv_ref_name is None:
return None
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
gv_ref: llvmlite.Value* = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
bc: llvmlite.Value* = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
return bc
# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None,
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
def translate_constant(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None) -> llvmlite.Value*:
"""翻译常量(int/str/bool)"""
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
cn: ast.Constant* = ast.Constant*(node)
if cn is None:
return None
ck: int = cn.const_kind
if cn.const_kind == ast.CONST_INT:
# 超出 i32 范围则用 i64(避免常量创建时被截断)
iv: t.CInt64T = cn.int_val
# 位宽选择:
# 1) 目标上下文是 >32 位整数时直接取 i64 —— 必需:词法层 atoll 把
# `0xFFFFFFFFFFFFFFFF` 按有符号 int64 回绕成 -1,仅按"是否超出
# i32 范围"判定会把它物化成 i32 -1,随后强转路径 zext 到 i64
# → 0x00000000FFFFFFFF(N1:THREAD_CREATE 失败码错)。
# const_int 按十进制值打印,故 i64 -1 即全 1,语义正确。
# 2) 否则维持旧规则:超出 i32 范围用 i64,其余 i32。
iv: int64 = cn.int_val
if target_ty is not None and self.is_ptr_type(target_ty) == 0 \
and self.get_llvm_type_bits(target_ty) > 32:
return llvmlite.const_int64(pool, iv)
if iv > 2147483647 or iv < -2147483648:
return llvmlite.const_int64(pool, iv)
return llvmlite.const_int32(pool, iv)
elif cn.const_kind == ast.CONST_FLOAT:
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
double_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
elif cn.const_kind == ast.CONST_CHAR:
# 单引号单字符字面量:默认按字符值(i32,coerce_to_type 截断为 i8)。
# 但当目标类型是指针(str/bytes/t.CPtr 等)时,对齐 TPC 引号推断
# (HandlesExpr:"目标是 ptr 但值是 char → 走字符串路径"):物化为
# 1 字符字符串。否则 coerce inttoptr 把字符值当指针(如 '"'=0x22),
# 运行期 strlen 访问低地址 → ASan 崩溃(二代编 StringTest PyI 生成根因)。
# 1a 阶段(declare_only=1)不 LoadDepVpi(vpi 未预生成)→ 依赖函数
# found_func=None → translate_call 缺省参数补全不生效 → translate_value
# 调用参数数 < define(如 target_ty 缺省)→ x64 ABI 被调方读第 9 参
# 栈槽残留垃圾(0x4 等)。declare_only 不产出 IR,target_ty 无用,
# 未对齐(8 对齐失败)即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
# (SlotsBugTest 1a string.vp 崩溃根因;1b found_func 非 None 正常填充)。
if target_ty is not None and (t.CUInt64T(target_ty) & 7) == 0 \
# 单引号单字符字面量(CONST_CHAR):默认按字符值(i32,coerce 截断为 i8);但当目标类型是指针(str/bytes/t.CPtr)时须物化为 1 字符字符串,否则 coerce inttoptr 把字符值当指针(如 '"'=0x22)→ 运行期 strlen 访问低地址崩溃。1a 阶段(declare_only=1)依赖函数 found_func=None,target_ty 未对齐即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
if target_ty is not None and (uint64(target_ty) & 7) == 0 \
and self.is_ptr_type(target_ty) != 0:
cbuf: bytes = stdlib.malloc(2)
if cbuf is None:
return llvmlite.const_int32(pool, 0)
cbuf[0] = t.CChar(cn.int_val)
cbuf[0] = char(cn.int_val)
cbuf[1] = '\0'
r_cs: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, cbuf, trans)
r_cs: llvmlite.Value* = self.create_global_string(builder, pool, mod, cbuf, trans)
stdlib.free(cbuf)
return r_cs
cv: t.CInt64T = cn.int_val
cv: int64 = cn.int_val
return llvmlite.const_int32(pool, cv)
elif cn.const_kind == ast.CONST_STR:
sv: str = cn.str_val
if sv is None:
return None
r: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, sv, trans)
r: llvmlite.Value* = self.create_global_string(builder, pool, mod, sv, trans)
return r
elif cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
@@ -148,13 +132,13 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
return llvmlite.const_int32(pool, 0)
elif cn.const_kind == ast.CONST_NONE:
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
if i8_ty is None:
return None
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None:
return None
rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
rv_none: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
return rv_none
return None
# ============================================================
@@ -162,8 +146,8 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
#
# 用于 list 字面量 [a, b, c] 的元素类型推断
# ============================================================
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy | t.CPtr,
value: llvmlite.Value | t.CPtr) -> str:
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*,
value: llvmlite.Value*) -> str:
"""从 LLVM Value 的类型推断 list 元素类型名
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
@@ -171,7 +155,7 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
"""
if value is None or value.Ty is None:
return None
ty: llvmlite.LLVMType | t.CPtr = value.Ty
ty: llvmlite.LLVMType* = value.Ty
bits: int = self.get_llvm_type_bits(ty)
if bits != 0:
if bits == 32:
@@ -197,7 +181,7 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(self, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
def _find_pool_var(self, trans: HT.Translator*) -> llvmlite.Value*:
"""查找上下文中的 pool 变量,返回 alloca 或 None
查找顺序: pool → _mbuddy → mbuddy → mb
@@ -208,7 +192,7 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
"""
if trans is None:
return None
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
if alloca is not None:
return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
@@ -226,26 +210,26 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
# 链接器合并同模板重复特化定义,zeroinitializer 保证编译链接通过。
# 运行时若该模块未在入口 main 赋值 _mbuddy(null pool 上动态建 list),
# 由真实崩溃暴露(fail-fast 哲学,不掩盖上游未初始化)。
pool: memhub.MemBuddy | t.CPtr = trans.Pool
pool: memhub.MemBuddy* = trans.Pool
if pool is None or trans.Module is None or trans.SymTab is None:
return None
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
existing: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
if existing is not None:
return existing
# 创建 i8* 类型(t.CVoid | t.CPtr = i8*)
i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb)
# 创建 i8* 类型(t.CPtr = i8*)
i8_ty_fb: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_fb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_fb)
# 当前模块 mangled 名(@<模块sha1>._mbuddy)
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
# 创建 linkonce_odr 定义(可被链接器合并,自给自足保证链接通过)
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
gv_ext: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
if gv_ext is not None:
gv_ext.Linkage = "linkonce_odr"
gv_ext.Initializer = "zeroinitializer"
llvmlite.module_add_global(trans.Module, gv_ext)
# 创建 Value 引用(@<模块sha1>._mbuddy, 类型为 i8**)
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
i8_ptr_ptr_ty_fb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty_fb)
ref_name_fb: str = pool.alloc(96)
if ref_name_fb is not None:
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(见 make_global_ref)
@@ -253,80 +237,63 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
else:
viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
gv_ref_fb: llvmlite.Value* = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
# 注册到模块作用域,避免重复创建
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
return gv_ref_fb
# ============================================================
# translate_list_literal — 翻译 list 字面量 [a, b, c]
#
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池(MemBuddy)
# mod: LLVMModule
# node: ast.List 节点
# elem_type_name: 元素类型名(如 "str", "int"),None 表示从元素推断
# trans: Translator 对象
#
# Returns:
# list 对象指针(Ptr(list[T] struct)),None 失败
# ============================================================
def translate_list_literal(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
# translate_list_literal:把 list 字面量 [a, b, c] 翻译为 list[T](pool) 构造 + append 调用序列。elem_type_name 为元素类型名(如 "str"/"int"),None 表示从元素推断。返回 list 对象指针(Ptr(list[T] struct)),失败返回 None
def translate_list_literal(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
elem_type_name: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
trans: HT.Translator*) -> llvmlite.Value*:
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
if node is None or trans is None:
return None
list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
list_node: ast.List* = ast.List*(node)
if list_node is None:
return None
elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
elts_count: t.CSizeT = 0
elts: list[ast.AST*]* = list_node.elts
elts_count: sizet = 0
if elts is not None:
elts_count = len(elts)
# 推断元素类型
inferred_type_name: str = elem_type_name
first_elem_val: llvmlite.Value | t.CPtr = None
first_elem_val: llvmlite.Value* = None
if inferred_type_name is None:
if elts_count > 0:
first_elem_node: ast.AST | t.CPtr = elts.get(0)
first_elem_node: ast.AST* = elts.get(0)
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
if first_elem_val is None or first_elem_val.Ty is None:
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
if inferred_type_name is None:
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
self.Gen.TypeGenErr.fatal_error(
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
else:
# 空 list 无元素可推断,保持默认 str(list[str])
inferred_type_name = "str"
# 查找 pool 变量
pool_alloca: llvmlite.Value | t.CPtr = self._find_pool_var(trans)
pool_alloca: llvmlite.Value* = self._find_pool_var(trans)
if pool_alloca is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
return None
# load pool 变量值(i8*)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
pool_val: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
if pool_val is None:
return None
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
type_args: list[str] | t.CPtr = list[str](pool, 8)
type_args: list[str]* = list[str](pool, 8)
type_args.append(inferred_type_name)
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
if spec_name is None:
@@ -334,18 +301,18 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
return None
# 查找 struct entry
struct_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
struct_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if struct_entry is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
return None
struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
struct_ty: llvmlite.LLVMType* = struct_entry.Ty
if struct_ty is None:
return None
# alloca 临时 list 空间
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
tmp: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None:
return None
@@ -353,59 +320,51 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
# 避免为无 __new__ 的类生成过度声明)
storage_ptr: llvmlite.Value | t.CPtr = tmp
storage_ptr: llvmlite.Value* = tmp
has_new_flag: int = 0
if struct_entry is not None:
has_new_flag = struct_entry.HasNew
if has_new_flag != 0:
new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
new_args: sizet* = pool.alloc(8 * 32)
if new_args is None:
return None
string.memset(new_args, 0, 8 * 32)
new_args[0] = t.CSizeT(pool_val)
new_ptr: llvmlite.Value | t.CPtr = self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
new_args[0] = sizet(pool_val)
new_ptr: llvmlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
if new_ptr is not None:
storage_ptr = new_ptr
# 调用 __before_init__(storage_ptr)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if init_args is None:
return None
init_args: sizet* = pool.alloc(8 * 32)
if init_args is None: return None
string.memset(init_args, 0, 8 * 32)
init_args[0] = t.CSizeT(pool_val)
elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
init_args[1] = t.CSizeT(elem_size_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
init_args[0] = sizet(pool_val)
elem_size_val: llvmlite.Value* = llvmlite.const_int64(pool, 0)
init_args[1] = sizet(elem_size_val)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
# 对每个元素调用 append(storage_ptr, elem_val)
if elts_count > 0:
# 第一个元素可能已经翻译过
if first_elem_val is not None:
append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
append_args: sizet* = pool.alloc(8 * 32)
if append_args is not None:
string.memset(append_args, 0, 8 * 32)
append_args[0] = t.CSizeT(first_elem_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
append_args[0] = sizet(first_elem_val)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
# 翻译剩余元素
i: t.CSizeT = 1
while i < elts_count:
elem_node_i: ast.AST | t.CPtr = elts.get(i)
elem_val_i: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
for i in range(1, elts_count):
elem_node_i: ast.AST* = elts.get(i)
elem_val_i: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
if elem_val_i is not None:
ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
ap_args: sizet* = pool.alloc(8 * 32)
if ap_args is not None:
string.memset(ap_args, 0, 8 * 32)
ap_args[0] = t.CSizeT(elem_val_i)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
i += 1
ap_args[0] = sizet(elem_val_i)
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
return storage_ptr
@@ -4,95 +4,72 @@ import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.Translator as HT
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块
# CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序
# 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
# ============================================================
FSTR_BUF_SIZE: t.CDefine = 1024
FSTR_SPEC_MAX: t.CDefine = 64
FSTR_FMT_MAX: t.CDefine = 64
define FSTR_BUF_SIZE = 1024
define FSTR_SPEC_MAX = 64
define FSTR_FMT_MAX = 64
# ============================================================
# ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenFstr(ExprGenBase.ExprGenBase):
novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
"""f-string/格式化值翻译"""
# ============================================================
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
#
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
# 栈缓冲。之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
# 栈缓冲(每站点独立,线程安全);结果是**函数内临时值,禁止逃逸**
# (return / 存字段 / 存全局 由 GenApi.fail_if_fstring_escape 拦;
# 需逃逸用 GenApi.escape_fstring_copy 显式拷贝)。
# 之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
# ============================================================
def translate_joined_str(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""f-string → snprintf 生成 i8* 栈缓冲"""
if node is None or builder is None or trans is None:
return None
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
if js is None or js.values is None:
return None
def translate_joined_str(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""f-string → snprintf 生成 i8* 栈缓冲(函数内临时值,禁止逃逸)"""
if node is None or builder is None or trans is None: return None
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](node)
if js is None or js.values is None: return None
# 编译期格式串缓冲(pool 分配)
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
if fmt_buf is None: return None
fmt_buf[0] = '\0'
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_head: llvmlite.Value* = None
arg_tail: llvmlite.Value* = None
arg_count: int = 0
parts: list[ast.AST | t.CPtr] | t.CPtr = js.values
pn: t.CSizeT = len(parts)
parts: list[ast.AST*]* = js.values
pn: sizet = len(parts)
for pi in range(pn):
part: ast.AST | t.CPtr = parts.get(pi)
if part is None:
continue
part: ast.AST* = parts.get(pi)
if part is None: continue
if part.kind() == ast.ASTKind.Constant:
pcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
pcn: ast.Constant* = ast.Constant*(part)
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
self._fstr_append_literal(fmt_buf, pcn.str_val)
continue
if part.kind() != ast.ASTKind.FormattedValue:
continue
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
if fv.value is None:
continue
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
continue
if part.kind() != ast.ASTKind.FormattedValue: continue
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](part)
if fv.value is None: continue
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
continue
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
c_fmt: str = None
@@ -107,8 +84,7 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
cast_val: llvmlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
@@ -120,30 +96,28 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
arg_count += 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def translate_formatted_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
def translate_formatted_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
if node is None or builder is None or trans is None:
return None
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](node)
if fv is None or fv.value is None:
return None
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_head: llvmlite.Value* = None
arg_tail: llvmlite.Value* = None
arg_count: int = 0
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
else:
c_fmt: str = None
if fv.format_spec is not None:
@@ -155,8 +129,7 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
cast_val: llvmlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
@@ -164,37 +137,44 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
arg_tail = cast_val
arg_count = 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def _fstr_finalize(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
def _fstr_finalize(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
fmt_buf: str,
arg_head: llvmlite.Value | t.CPtr,
arg_tail: llvmlite.Value | t.CPtr,
arg_head: llvmlite.Value*,
arg_tail: llvmlite.Value*,
arg_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf,返回 i8*"""
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
trans: HT.Translator*) -> llvmlite.Value*:
"""f-string 尾部:全局格式串 + 每站点栈缓冲 + snprintf,返回 i8*(函数内临时值)"""
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
if fmt_buf is None or fmt_buf[0] == '\0':
# 空 f-string(f"")→ 空字符串常量
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
# 格式串 → 全局字符串常量(i8*)
fmt_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, fmt_buf, trans)
if fmt_ptr is None:
return None
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
if buf_alloca is None:
return None
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
buf_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, buf_ty, i8_ty, buf_alloca, zero_i32)
if buf_ptr is None:
return None
fmt_ptr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
if fmt_ptr is None: return None
# 结果缓冲:**每站点独立栈缓冲**(entry 块 alloca;每次翻译本站点各建一个,
# 站点之间不共享 ⇒ 同函数多个 f-string 不再互相覆盖)。
#
# 语义(用户 2026-09-27 决定):f-string 结果是**当前函数内的临时值**。
# · 栈缓冲 ⇒ 线程安全(每个线程各自一份栈帧,同站点并发也不串味)
# + 零运行时依赖(内核没有 C-ABI malloc,也不能假设任何分配器);
# · 函数返回即失效 ⇒ **禁止逃逸**:return / 存字段 / 存全局 / 存容器一律
# 由 GenApi.fail_if_fstring_escape 在翻译期 fail-fast 拦下。历史事故:
# 旧实现让调用方把该栈指针 return 出去,再作 %s 喂 snprintf ⇒
# ASan stack-buffer-underflow in vsnprintf(READ size 1,栈左红区)。
# · 确需逃逸时**显式分配后拷贝**:GenApi.escape_fstring_copy
# (stdlib.malloc + string.strcpy);编译器自身的报文构造即此写法
# (AssignGenMixin.const_assign_msg)。
buf_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
if buf_alloca is None: return None
zero_i32: llvmlite.Value* = llvmlite.const_int32(pool, 0)
buf_ptr: llvmlite.Value* = llvmlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
if buf_ptr is None: return None
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
size_val: llvmlite.Value* = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
buf_ptr.Next = size_val
size_val.Next = fmt_ptr
fmt_ptr.Next = arg_head
@@ -205,16 +185,14 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
def _fstr_append_literal(self, fmt_buf: str,
lit: str) -> None:
"""追加字面量到格式串(% 转义为 %%)"""
if fmt_buf is None or lit is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
rem: t.CSizeT = FSTR_BUF_SIZE - 1 - cur_len
if rem == 0:
return
src_len: t.CSizeT = string.strlen(lit)
si: t.CSizeT = 0
if fmt_buf is None or lit is None: return
cur_len: sizet = string.strlen(fmt_buf)
rem: sizet = FSTR_BUF_SIZE - 1 - cur_len
if rem == 0: return
src_len: sizet = string.strlen(lit)
si: sizet = 0
while si < src_len and rem > 0:
ch: t.CChar = lit[si]
ch: char = lit[si]
if ch == '%':
if rem < 2:
break
@@ -232,14 +210,14 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
"""追加原始字符串(不做 % 转义)"""
if fmt_buf is None or s is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
src_len: t.CSizeT = string.strlen(s)
cur_len: sizet = string.strlen(fmt_buf)
src_len: sizet = string.strlen(s)
if cur_len + src_len < FSTR_BUF_SIZE:
string.strcpy(fmt_buf + cur_len, s)
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy | t.CPtr,
spec_node: ast.AST | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr) -> str:
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*,
spec_node: ast.AST*,
val: llvmlite.Value*,
arg_node: ast.AST*) -> str:
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
if spec_node is None or pool is None or val is None:
return None
@@ -250,50 +228,43 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
spec[0] = '\0'
sk: int = spec_node.kind()
if sk == ast.ASTKind.JoinedStr:
jss: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(spec_node)
if jss.values is None:
return None
vn: t.CSizeT = len(jss.values)
jss: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](spec_node)
if jss.values is None: return None
vn: sizet = len(jss.values)
for vi in range(vn):
vpart: ast.AST | t.CPtr = jss.values.get(vi)
if vpart is None:
continue
if vpart.kind() != ast.ASTKind.Constant:
return None
vcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(vpart)
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None:
return None
vpart: ast.AST* = jss.values.get(vi)
if vpart is None: continue
if vpart.kind() != ast.ASTKind.Constant: return None
vcn: ast.Constant* = ast.Constant*(vpart)
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None: return None
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
string.strcat(spec, vcn.str_val)
elif sk == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(spec_node)
cn: ast.Constant* = ast.Constant*(spec_node)
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
return None
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
else:
return None
slen: t.CSizeT = string.strlen(spec)
if slen == 0:
return None
else: return None
slen: sizet = string.strlen(spec)
if slen == 0: return None
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
pos: t.CSizeT = 0
align_char: t.CChar = 0
pos: sizet = 0
align_char: char = 0
if pos < slen:
ch0: t.CChar = spec[pos]
ch0: char = spec[pos]
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
align_char = ch0
pos += 1
elif pos + 1 < slen:
ch1: t.CChar = spec[pos + 1]
ch1: char = spec[pos + 1]
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
align_char = ch1
pos += 2
c_flags: str = pool.alloc(FSTR_FMT_MAX)
c_width: str = pool.alloc(32)
c_prec: str = pool.alloc(32)
if c_flags is None or c_width is None or c_prec is None:
return None
if c_flags is None or c_width is None or c_prec is None: return None
c_flags[0] = '\0'
c_width[0] = '\0'
c_prec[0] = '\0'
@@ -317,7 +288,7 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
string.strcat(c_flags, "-")
# width digits
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
wl: t.CSizeT = string.strlen(c_width)
wl: sizet = string.strlen(c_width)
if wl < 30:
c_width[wl] = spec[pos]
c_width[wl + 1] = '\0'
@@ -330,7 +301,7 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
string.strcat(c_prec, ".")
pos += 1
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
pl: t.CSizeT = string.strlen(c_prec)
pl: sizet = string.strlen(c_prec)
if pl < 30:
c_prec[pl] = spec[pos]
c_prec[pl + 1] = '\0'
@@ -338,32 +309,28 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
if string.strlen(c_prec) == 1:
return None # 只有 '.' 无效
# type char
fmt_type: t.CChar = 0
fmt_type: char = 0
if pos < slen:
fmt_type = spec[pos]
pos += 1
if pos != slen:
return None # 多余字符无法解析
if pos != slen: return None # 多余字符无法解析
# 3. 长度修饰(按值类型位宽)
len_mod: str = pool.alloc(4)
if len_mod is None:
return None
if len_mod is None: return None
len_mod[0] = '\0'
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
if ibits == 64:
string.strcpy(len_mod, "ll")
elif ibits == 32:
string.strcpy(len_mod, "l")
elif ibits == 16:
string.strcpy(len_mod, "h")
elif ibits == 8:
string.strcpy(len_mod, "hh")
# 32 位**不写**长度修饰符(N14):`{x:08X}` 的实参是 i32,此前推导出
# `l` → 格式串 `%08lX`(long)与实参宽度不符(LP64 下 printf 按 64 位
# 读参 → 变参错位)。与同文件无 spec 路径 `_fstr_infer_c_fmt`(32 位用
# `%u/%d`)以及 TPC 侧 HandlesExprFormat.py 保持一致。
if ibits == 64: string.strcpy(len_mod, "ll")
elif ibits == 16: string.strcpy(len_mod, "h")
elif ibits == 8: string.strcpy(len_mod, "hh")
# 4. 组装 C 格式串
out: str = pool.alloc(FSTR_FMT_MAX)
if out is None:
return None
if out is None: return None
out[0] = '\0'
string.strcat(out, "%")
string.strcat(out, c_flags)
@@ -417,23 +384,19 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
else:
return None
return out
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*,
val: llvmlite.Value*,
arg_node: ast.AST*,
trans: HT.Translator*) -> str:
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
if val is None or pool is None:
return None
if val is None or pool is None: return None
out: str = pool.alloc(16)
if out is None:
return None
if out is None: return None
out[0] = '\0'
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if ibits == 1:
return "%d" # bool
if ibits == 8:
return "%c" # 字符
if ibits == 1: return "%d" # bool
if ibits == 8: return "%c" # 字符
if ibits != 0:
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
if ibits == 64:
@@ -453,29 +416,29 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
return None
def _fstr_cast_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
def _fstr_cast_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
val: llvmlite.Value*,
c_fmt: str,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
arg_node: ast.AST*,
trans: HT.Translator*) -> llvmlite.Value*:
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
if val is None or c_fmt is None:
return None
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
fmt_char: t.CChar = 0
flen: t.CSizeT = string.strlen(c_fmt)
fi: t.CSizeT = flen
fmt_char: char = 0
flen: sizet = string.strlen(c_fmt)
fi: sizet = flen
while fi > 0:
fi -= 1
ch: t.CChar = c_fmt[fi]
ch: char = c_fmt[fi]
if ch == '%':
break
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
if ch != 'l' and ch != 'h':
fmt_char = ch
break
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
if fmt_char == 's':
return val
if fmt_char == 'c':
+127
View File
@@ -0,0 +1,127 @@
import t, c
from stdint import *
import ast
import memhub
import llvmlite
import lib.core.Gen.Translator as HT
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
# ============================================================
# docstring 登记表容量(__doc__ 魔法属性支持)
#
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
# 函数/类/方法 docstring,幂等查重)
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
# DOC_VAL_SZ: docstring 内容缓冲宽度
# ============================================================
define DOC_MAX = 512
define DOC_KEY_SZ = 192
define DOC_VAL_SZ = 4096
# ============================================================
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
#
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
# ============================================================
define FSTR_BUF_SIZE = 1024
define FSTR_SPEC_MAX = 64
define FSTR_FMT_MAX = 64
# ============================================================
# ExprGenMixin - 表达式处理 Mixin(对象化 P3)
# ============================================================
# ============================================================
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
# ============================================================
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
# ============================================================
# 翻译值表达式(RHS 分派)— 模块级版本
# ============================================================
def translate_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None,
use_ctx: str = None) -> llvmlite.Value*:
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
use_ctx: 使用处**真实站点种类**标签("局部变量注解" / "模块级全局" /
"形参" / "返回注解" …),只被 CDefine 使用处宽度不足报文读取;不参与判定。
"""
if trans is not None:
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.TranslateDepth += 1
rv: llvmlite.Value* = self._translate_value_impl(builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty, use_ctx)
if trans is not None:
trans.TranslateDepth -= 1
return rv
def _translate_value_impl(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None,
use_ctx: str = None) -> llvmlite.Value*:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: llvmlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
return rv
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List: return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
elif k == ast.ASTKind.IfExp: return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Lambda: return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Await:
# 协程 await(TPV-M1):await 目标是 async 函数调用时内联翻译
# (子帧创建 + resume 到 DONE + 取子帧 ret 存父帧 await 槽 + 父挂起),
# 返回子协程 ret 值(i32)。顶层 await(AnnAssign/Assign 的 value 直接
# 是 Await)由 AsyncGen 段翻译特判处理,本分支覆盖表达式上下文中的
# await(M1 覆盖有限场景,详见 AsyncGenMixin._emit_await)。
aw: rawptr[ast.Await] = rawptr[ast.Await](node)
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
aw_cl: ast.Call* = ast.Call*(aw.value)
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
aw_nm: ast.Name* = ast.Name*(aw_cl.func)
if aw_nm.id is not None and trans is not None:
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0:
return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
return None
elif k == ast.ASTKind.Starred:
# *x 在值上下文(如 printf("%d", *k) 实参):解析器把调用参数位的
# '*' 按 Python *args 解包语义归类为 Starred,但 Viper 未实现解包,
# 全代码库无调用点使用;按 C 语义视为单目解引用(*ptr → load pointee,
# 对齐 UnaryOp(Deref))。构造 Deref UnaryOp 复用既有翻译/错误路径。
st: rawptr[ast.Starred] = rawptr[ast.Starred](node)
if st is None or st.value is None: return None
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
if deref_node is None: return None
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans)
return None
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-523
View File
@@ -1,523 +0,0 @@
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
# ============================================================
# ExprGenBase - ExprGenMixin 拆分(阶段 D):表达式基础工具(类型判断/指针层级/coerce)
#
# 最底层 Base(ExprGenMixin/Const/Fstr/Ops/Cdef/Name/Attr/Cls 均 1 级继承本类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenBase:
"""表达式基础工具(类型判断/指针层级/coerce)"""
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
Gen: Generator.Generator | t.CPtr
DocKeys: bytes
DocValues: bytes
DocCount: int
# --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) ---
DiagCDefineCount: int
# --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) ---
DiagNameValueCount: int
# --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) ---
DiagTranslateValueCount: int
# ============================================================
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
# ============================================================
# ============================================================
# 从 AST 节点提取函数名
# ============================================================
def get_func_name(self, func_node: ast.AST | t.CPtr) -> str:
"""从函数节点提取函数名"""
if func_node is None:
return None
k: int = func_node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
return nm.id
elif k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
return at.attr
return None
# ============================================================
# 转义 LLVM IR 字符串字面量
# ============================================================
def escape_llvm_string(self, pool: memhub.MemBuddy | t.CPtr,
src: str) -> str:
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
if src is None:
return None
slen: t.CSizeT = string.strlen(src)
alloc_size: t.CSizeT = 4 * slen + 8
buf: str = pool.alloc(alloc_size)
if buf is None:
return None
pos: t.CSizeT = 0
buf[pos] = 'c'
pos += 1
buf[pos] = '"'
pos += 1
for i in range(slen):
ch: t.CChar = src[i]
if ch == '\0':
break
if ch == '\"' or ch == '\\' or ch < 0x20:
buf[pos] = '\\'
pos += 1
hi: int = (ch >> 4) & 0xF
lo: int = ch & 0xF
buf[pos] = Utils.nibble_to_hex_char(hi, 1)
pos += 1
buf[pos] = Utils.nibble_to_hex_char(lo, 1)
pos += 1
else:
buf[pos] = ch
pos += 1
buf[pos] = '\\'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '"'
pos += 1
buf[pos] = '\0'
return buf
# ============================================================
# 获取 LLVM 整数类型的位宽
# ============================================================
def get_llvm_type_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Int(bits):
return bits
case _:
return 0
# ============================================================
# 获取 LLVM 浮点类型的位宽
# ============================================================
def get_llvm_float_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Float(bits):
return bits
case _:
return 0
# ============================================================
# is_ptr_type - 检查类型是否为 Ptr
#
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
# ============================================================
def is_ptr_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return 1
case _:
return 0
# ============================================================
# get_ptr_level - 计算指针层级
#
# 返回值:
# 0 = 非指针(数值/结构体等)
# 1 = 一级指针(如 i8*, Struct*)
# 2 = 二级指针(如 i8**, Struct**)
# 3+ = 更高级指针
#
# 用于比较运算的类型安全检查:
# - 数值(0) vs 数值(0) → 允许
# - 一级指针(1) vs 一级指针(1) → 允许
# - 数值(0) vs 指针(1+) → 禁止(fast-fail)
# - 不同级指针(1 vs 2) → 禁止(fast-fail)
# ============================================================
def get_ptr_level(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针..."""
if ty is None:
return 0
level: int = 0
cur: llvmlite.LLVMType | t.CPtr = ty
while cur is not None:
# 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接
# 更新 while 条件变量(cur = pointee)时自举编译出的 match 把赋值
# 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case
# 绑定到局部变量,match 外更新循环变量。
nxt_cur: llvmlite.LLVMType | t.CPtr = None
match cur:
case llvmlite.LLVMType.Ptr(pointee):
level += 1
nxt_cur = pointee
case _:
break
cur = nxt_cur
return level
# ============================================================
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
#
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
# ============================================================
def _is_zero_int_literal(self, node: ast.AST | t.CPtr) -> int:
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
if node is None:
return 0
if node.kind() != ast.ASTKind.Constant:
return 0
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn is None or cn.const_kind != ast.CONST_INT:
return 0
if cn.int_val == 0:
return 1
return 0
# ============================================================
# is_array_type - 检查类型是否为 Array
#
# 返回 1=是, 0=否。类型检查工具函数。
# ============================================================
def is_array_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Array(_, _):
return 1
case _:
return 0
# ============================================================
# _zext_index_to_i32 - 数组下标索引零扩展
#
# LLVM GEP 对窄整数索引默认符号扩展:i8 0xE2(226) 会被当成 -30,
# 导致数组下标越界读(字节 >= 128 的乱码根因)。
# 数组下标语义为非负,<32 位整数索引一律 zext 到 i32。
# 仅用于数组 GEP;指针负偏移(p[-1])不经过此路径,不受影响。
# ============================================================
def _zext_index_to_i32(self, builder: llvmlite.IRBuilder | t.CPtr,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""将 <32 位整数下标零扩展到 i32(数组下标非负语义)"""
if idx_val is None or idx_val.Ty is None or builder is None:
return idx_val
idx_bits: int = self.get_llvm_type_bits(idx_val.Ty)
if idx_bits != 0 and idx_bits < 32:
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(builder.Pool)
return llvmlite.build_zext(builder, idx_val, i32_ty)
return idx_val
# ============================================================
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
#
# 处理两种情况:
# - ty 直接是 Struct → 检查是否注册
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
#
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
# ============================================================
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> str:
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
if ty is None:
return None
# 直接检查 ty 是否是注册的结构体
cls_nm_direct: str = self.Gen.StructGen.get_class_name_by_type(pool, ty)
if cls_nm_direct is not None:
return cls_nm_direct
# 如果 ty 是 Ptr,检查 pointee
if self.is_ptr_type(ty) != 0:
pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee
if pointee_cs is not None:
return self.Gen.StructGen.get_class_name_by_type(pool, pointee_cs)
return None
# ============================================================
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
#
# 属性访问的 Subscript 基座(如 lst[i].x)需要区分:
# - list[T] 变量:translate_subscript 走 __getitem__ 内联返回元素地址
# - 普通结构体指针数组(如 pts: Point | t.CPtr):get_subscript_ptr 返回元素地址
# 路由错误会让 list[T] 被当作结构体数组 GEP,访问错位崩溃。
# ============================================================
def _is_list_subscript_base(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> int:
"""判断下标基座是否为 list[T] 变量(1=是)"""
if sub_node is None or trans is None:
return 0
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is None:
return 0
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return 0
if self.is_ptr_type(alloca.Ty) == 0:
return 0
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return 0
match pointee:
case llvmlite.LLVMType.Ptr(inner_ty):
lsn_ls: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, lsn3):
lsn_ls = lsn3
case _:
pass
if lsn_ls is not None and string.strstr(lsn_ls, "list[") is not None:
return 1
case _:
pass
return 0
# ============================================================
# _sub_base_class_name - 解析下标基座变量的结构体类名
# ============================================================
def _sub_base_class_name(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> str:
"""解析下标基座变量的结构体类名(无则 None)"""
if sub_node is None or trans is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return None
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is None:
return None
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return None
if self.is_ptr_type(alloca.Ty) == 0:
return None
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return None
if self.is_ptr_type(pointee) != 0:
inner: llvmlite.LLVMType | t.CPtr = pointee.Pointee
if inner is not None:
return self.Gen.StructGen.get_class_name_by_type(pool, inner)
return self.Gen.StructGen.get_class_name_by_type(pool, pointee)
# ============================================================
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
#
# 属性访问的 Subscript 基座路由依据:
# - list[T] / OOP 定义 __getitem__ 的类(如 dict[key])→ translate_value
# (translate_subscript 走 __getitem__ 内联/方法调用返回元素值或地址)
# - 普通结构体指针数组(如 pts: Point | t.CPtr)→ get_subscript_ptr
# (GEP 元素地址,避免结构体值 base=非指针 报错)
# ============================================================
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> int:
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)"""
if self._is_list_subscript_base(pool, trans, sub_node) != 0:
return 1
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
if cls_ugi is None:
return 0
lookup_ugi: str = pool.alloc(128)
if lookup_ugi is None:
return 0
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_ugi) is not None:
return 1
return 0
# ============================================================
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
#
# 用于支持 X|t.CPtr 类型变量的属性访问:
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
# ============================================================
def _deref_if_ptr_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针"""
if obj_ptr is None or obj_ptr.Ty is None:
return obj_ptr
if self.is_ptr_type(obj_ptr.Ty) == 0:
return obj_ptr
pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
if pointee is None:
return obj_ptr
if self.is_ptr_type(pointee) == 0:
return obj_ptr
# obj_ptr 是 Ptr(Ptr(...)),load 解引用
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
if loaded is None:
return obj_ptr
return loaded
# ============================================================
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
# ============================================================
def coerce_to_type(self, builder: llvmlite.IRBuilder | t.CPtr,
val: llvmlite.Value | t.CPtr,
target_ty: llvmlite.LLVMType | t.CPtr,
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""将 val 强制转换为 target_ty
整数: sext(扩展)/ trunc(截断)
浮点: fpext(扩展)/ fptrunc(截断)
int→float: sitofp
float→int: fptosi
node: AST 节点(用于错误定位),可为 None
"""
if val is None or target_ty is None:
return val
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
match val.Ty:
case llvmlite.LLVMType.Func(_, _, _):
if self.is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
case _:
pass
# 整数转换
val_bits: int = self.get_llvm_type_bits(val.Ty)
target_bits: int = self.get_llvm_type_bits(target_ty)
# 整数 → 指针: inttoptr(必须在纯整数转换之前检查,因为 ptr 的 bits 非零会误入 sext/trunc 路径)
# 适用于: 跨模块方法返回 i64/i32(默认推断),目标变量是指针类型
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
return llvmlite.build_inttoptr(builder, val, target_ty)
# 指针 → 整数: ptrtoint(当目标明确是整数而非指针时)
# 注意: target_bits == 8 时跳过 ptrtoint,走后面的 build_load 解引用首字符
# 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针,
# ptrtoint 会截断地址低 8 位(非 0),应 load 解引用取首字符 0
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and target_bits != 8 and self.is_ptr_type(target_ty) == 0:
return llvmlite.build_ptrtoint(builder, val, target_ty)
if val_bits != 0 and target_bits != 0:
if val_bits == target_bits:
return val
if val_bits < target_bits:
# i1 (bool) → 更宽整数用 zext(bool 语义:1 保持 1,而非 sext 的 0xFF...)
# 与 HandlesReturn 中 return 语句的 i1 扩展规则保持一致
if val_bits == 1:
return llvmlite.build_zext(builder, val, target_ty)
return llvmlite.build_sext(builder, val, target_ty)
return llvmlite.build_trunc(builder, val, target_ty)
# 浮点转换
val_fbits: int = self.get_llvm_float_bits(val.Ty)
target_fbits: int = self.get_llvm_float_bits(target_ty)
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or \
(self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
self.Gen.TypeGenErr.fatal_error(node, "unsupported type conversion: float <-> ptr", "CastError")
return val
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
if val_fbits < target_fbits:
return llvmlite.build_fpext(builder, val, target_ty)
return llvmlite.build_fptrunc(builder, val, target_ty)
# int → float
if val_bits != 0 and target_fbits != 0:
return llvmlite.build_si2fp(builder, val, target_ty)
# float → int
if val_fbits != 0 and target_bits != 0:
return llvmlite.build_fp2si(builder, val, target_ty)
# 指针 → 非指针值: build_load 解引用
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
return llvmlite.build_load(builder, target_ty, val)
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
# ============================================================
# translate_ptr_arith - 指针算术: ptr + int / ptr - int
#
# 用 getelementptr(按 pointee 元素大小缩放索引),对齐 TPC
# ExprGen.emit_binary_op / HandlesAugAssign.py。
# 旧实现用 ptrtoint + add + inttoptr(按字节加):i8* 元素大小 1
# 恰好正确,但 i32*/struct* 等指针 `p + 3` 只前进 3 字节(应 12),
# 导致 StringIterTest Test 4/8 失败(指针偏移/指针算术)。
#
# 数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),
# 避免以整个数组为步长(对齐 TPC ExprGen L53-55)。
# ============================================================
def translate_ptr_arith(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
ptr_val: llvmlite.Value | t.CPtr,
int_val: llvmlite.Value | t.CPtr,
op: int) -> llvmlite.Value | t.CPtr:
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)"""
if ptr_val is None or int_val is None or ptr_val.Ty is None:
return None
# 提取 pointee 类型(非指针 → 不处理返回 None)
elem_ty: llvmlite.LLVMType | t.CPtr = None
match ptr_val.Ty:
case llvmlite.LLVMType.Ptr(pointee):
elem_ty = pointee
case _:
return None
if elem_ty is None:
return None
# 索引统一到 i64(对齐 TPC zext 到 i64)
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
idx: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, int_val, i64_ty, None)
if idx is None:
return None
# ptr - int → 负索引(LLVM GEP 索引为加法语义)
if op == ast.OpKind.Sub:
neg_idx: llvmlite.Value | t.CPtr = llvmlite.build_sub(
builder, llvmlite.const_int64(pool, 0), idx)
if neg_idx is None:
return None
idx = neg_idx
# 数组 pointee → 先 decay 为元素指针(getelementptr [N x ty], [N x ty]* %p, i32 0, i32 0)
if self.is_array_type(elem_ty) != 0:
inner_ty: llvmlite.LLVMType | t.CPtr = None
match elem_ty:
case llvmlite.LLVMType.Array(inner, _):
inner_ty = inner
case _:
return None
if inner_ty is None:
return None
decayed: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, elem_ty, inner_ty, ptr_val, llvmlite.const_int64(pool, 0))
if decayed is None:
return None
return llvmlite.build_gep(builder, inner_ty, decayed, idx)
return llvmlite.build_gep(builder, elem_ty, ptr_val, idx)
def InitExprGen(self) -> int:
"""初始化 ExprGen 族字段(构造时由 Generator 调用)"""
if self is None:
return 1
self.DocKeys = None
self.DocValues = None
self.DocCount = 0
self.DiagCDefineCount = 0
self.DiagNameValueCount = 0
self.DiagTranslateValueCount = 0
return 0
-163
View File
@@ -1,163 +0,0 @@
import t, c
from stdint import *
import ast
import memhub
import string
import llvmlite
import stdlib
import stdio
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.ExprGenBase as ExprGenBase
# ============================================================
# docstring 登记表容量(__doc__ 魔法属性支持)
#
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
# 函数/类/方法 docstring,幂等查重)
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
# DOC_VAL_SZ: docstring 内容缓冲宽度
# ============================================================
DOC_MAX: t.CDefine = 512
DOC_KEY_SZ: t.CDefine = 192
DOC_VAL_SZ: t.CDefine = 4096
# ============================================================
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
#
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
# ============================================================
FSTR_BUF_SIZE: t.CDefine = 1024
FSTR_SPEC_MAX: t.CDefine = 64
FSTR_FMT_MAX: t.CDefine = 64
# ============================================================
# ExprGenMixin - 表达式处理 Mixin(对象化 P3,wiki/14-oo-refactor.md)
#
# 从 HandlesExpr.vp(48 函数,无全局状态)与 HandlesExprOps.vp
# 迁移:模块级纯函数 → 类方法(self 替代,参数保留含 builder/pool)。
#
# 迁移原则(与 P2 一致,行为零变化):
# 1. 方法保留原函数签名(含 builder/pool 首参),仅函数体内
# 本模块函数调用改为 self.xxx(...)
# 2. 外部模块调用(HandlesExprCall.xxx / HandlesType.xxx 等)保留模块前缀
# 3. HandlesExpr.vp / HandlesExprOps.vp 原函数保留为"委托":本地
# 模块级单例 _g_gen(BuildPipeline/main 注入)cast 成 Generator 类型
# 4. ExprHandle 类保留在 HandlesExpr.vp(translator handler 模式)
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
# ============================================================
@t.NoVTable
class ExprGenMixin(ExprGenBase.ExprGenBase):
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
# ============================================================
# 翻译值表达式(RHS 分派)— 模块级版本
# ============================================================
def translate_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None,
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
"""
if trans is not None:
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(
node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.TranslateDepth += 1
rv: llvmlite.Value | t.CPtr = self._translate_value_impl(
builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty)
if trans is not None:
trans.TranslateDepth -= 1
return rv
def _translate_value_impl(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None,
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
return rv
elif k == ast.ASTKind.Name:
return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans)
elif k == ast.ASTKind.BinOp:
return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
elif k == ast.ASTKind.Call:
return self.Gen.CallGen.translate_call(pool, builder, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Compare:
return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp:
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
elif k == ast.ASTKind.BoolOp:
return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript:
return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute:
return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List:
return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
elif k == ast.ASTKind.IfExp:
return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Lambda:
# 模块级委托:translate_lambda 定义在 FuncGenMixin(跨 Mixin
# 方法不能 self. 直接调用,见 HandlesExpr.translate_lambda)
return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
elif k == ast.ASTKind.JoinedStr:
return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
elif k == ast.ASTKind.FormattedValue:
# 解析器对 f"{expr:spec}"(单 FormattedValue 无字面量)返回裸 FormattedValue
# 而非 JoinedStr(见 ast/parser.vp _parse_fstring),需单独翻译
return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Await:
# 协程 await(TPV-M1):await 目标是 async 函数调用时内联翻译
# (子帧创建 + resume 到 DONE + 取子帧 ret 存父帧 await 槽 + 父挂起),
# 返回子协程 ret 值(i32)。顶层 await(AnnAssign/Assign 的 value 直接
# 是 Await)由 AsyncGen 段翻译特判处理,本分支覆盖表达式上下文中的
# await(M1 覆盖有限场景,详见 AsyncGenMixin._emit_await)。
aw: ast.Await | t.CPtr = (ast.Await | t.CPtr)(node)
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
aw_cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aw.value)
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
aw_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aw_cl.func)
if aw_nm.id is not None and trans is not None:
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0:
return self.Gen.AsyncGen._emit_await(
builder, pool, mod, trans, aw, aw_nm.id)
return None
return None
+371
View File
@@ -0,0 +1,371 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Call.CallGenBase as CallGenBase
# ============================================================
# FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具
# ============================================================
novirtual class FuncGenBase(CallGenBase.CallGenBase):
"""函数属性 / 命名 / mangle / create_function 基础工具"""
# ============================================================
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
#
# 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式
# 也支持 @c.Attribute(t.attr.llvm.xxx) 形式
#
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
# ============================================================
def extract_func_attrs(self, pool: memhub.MemBuddy*,
decorator_list: list[ast.AST*]*,
imported_modules: str) -> str:
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
if decorator_list is None: return None
dn: sizet = len(decorator_list)
if dn == 0: return None
attrs_buf: str = pool.alloc(256)
if attrs_buf is None: return None
attrs_buf[0] = '\0'
found_any: int = 0
for di in range(dn):
deco: ast.AST* = decorator_list.get(di)
if deco is None: continue
dk: int = deco.kind()
# 直接形式 `@t.attr.<name>`(与类侧 ClassGenBase._has_decorator 同口径,
# 类定义早就接受两种写法):`inline def f(...)` 合成的就是这一形态,
# 且**不要求** import c。此前只认 c.Attribute(...) ⇒ 没有 import c 的文件
# 会静默不内联。不支持的属性名由 _append_llvm_attr 返回 0 自然忽略
# (如 @t.CVTable / @t.NoVTable / packed / aligned 对函数无意义)。
if dk == ast.ASTKind.Attribute:
dan: str = self._get_attr_name_from_node(deco)
if dan is not None and string.strcmp(dan, "section") != 0:
if self._append_llvm_attr(attrs_buf, dan) != 0:
found_any = 1
continue
if dk != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None: continue
cfk: int = call_node.func.kind()
if cfk != ast.ASTKind.Attribute: continue
# 检测 c.Attribute
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
if func_attr.attr is None: continue
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
if func_attr.value is None: continue
fvk: int = func_attr.value.kind()
if fvk != ast.ASTKind.Name: continue
c_name: ast.Name* = ast.Name*(func_attr.value)
if c_name.id is None: continue
if string.strcmp(c_name.id, "c") != 0: continue
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
if imp_c == 0: continue
# 遍历参数提取属性
if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(an):
arg_node: ast.AST* = args_list.get(ai)
if arg_node is None: continue
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None: continue
# B5:section 字符串属性(t.attr.section(".text.startup"))不能内联
# 在函数签名行(llc: expected string constant),必须走属性组
# #N(function_set_attr_group)→ 由 FuncGenDef 单独提取处理。
if string.strcmp(attr_name, "section") == 0: continue
# 映射到 LLVM 属性名并追加
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
if ar != 0:
found_any = 1
if found_any == 0: return None
return attrs_buf
def extract_func_section(self, pool: memhub.MemBuddy*,
decorator_list: list[ast.AST*]*,
imported_modules: str) -> str:
"""提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。
LLVM 字符串属性(带值)必须用属性组 #N(函数行输出 #N + 函数尾
attributes #N = { ... }),不能内联(llc: expected string constant)。
main.vp boot 函数 t.attr.section(".text.startup") 供链接器按段放置。
"""
if pool is None or decorator_list is None: return None
dn: sizet = len(decorator_list)
if dn == 0: return None
for di in range(dn):
deco: ast.AST* = decorator_list.get(di)
if deco is None or deco.kind() != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue
if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(an):
arg_node: ast.AST* = args_list.get(ai)
if arg_node is None: continue
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
sec_val: str = self._get_attr_call_arg_str(arg_node)
if sec_val is None: continue
sec_buf: str = pool.alloc(128)
if sec_buf is None: return None
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
return sec_buf
return None
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
"""从 AST 节点提取属性名
支持:
- Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline"
- Attribute: t.attr.packed -> "packed"
- Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind"
"""
if node is None: return None
k: int = node.kind()
# Call 节点: t.attr.always_inline()
if k == ast.ASTKind.Call:
call: ast.Call* = ast.Call*(node)
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
at: ast.Attribute* = ast.Attribute*(call.func)
return at.attr
# Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind
if k == ast.ASTKind.Attribute:
at2: ast.Attribute* = ast.Attribute*(node)
return at2.attr
return None
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
"""取 t.attr.xxx("val") 调用的首参字符串常量(如 section(".text.startup"))"""
if node is None or node.kind() != ast.ASTKind.Call: return None
cl: ast.Call* = ast.Call*(node)
if cl.args is None or len(cl.args) == 0: return None
a0: ast.AST* = cl.args.get(0)
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
cn: ast.Constant* = ast.Constant*(a0)
if cn.const_kind != ast.CONST_STR: return None
return cn.str_val
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
"""将属性名映射到 LLVM 属性名并追加到缓冲区
返回 1 表示已追加,0 表示不支持该属性
"""
if attrs_buf is None or attr_name is None: return 0
# 先确定映射的 LLVM 属性名
llvm_name: str = None
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
else:
# packed/aligned/section/visibility/weak 等不支持作为函数内联属性
return 0
# 支持的属性:如果缓冲区非空,先追加空格分隔
if attrs_buf[0] != '\0':
self._append_str(attrs_buf, " ", 256)
self._append_str(attrs_buf, llvm_name, 256)
return 1
def _append_str(self, buf: str, s: str,
max_len: sizet = 256) -> None:
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
if buf is None or s is None: return
# 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0),
# 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。
if max_len == 0:
max_len = 256
cur_len: sizet = string.strlen(buf)
s_len: sizet = string.strlen(s)
if cur_len + s_len + 1 > max_len: return # 无剩余容量:丢弃本次追加,避免越界写 存疑
string.strcpy(buf + cur_len, s)
# ============================================================
# _mangle_name - 为名称添加 SHA1 前缀
#
# 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带
# SHA1 前缀(16 hex + '.'),则原样返回。
# ============================================================
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
"""为任意名称添加 SHA1 前缀(不做 main/export 检查)"""
if trans is None or name is None: return name
if trans.ModuleSha1 is None: return name
name_len: sizet = string.strlen(name)
# 已带 SHA1 前缀("{16hex}.")则跳过
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`。旧判据是 `name_len > 17`,
# 与真源的 `>= 17` 只差"前缀之后为空串"这一退化形态(无实际意义),一并对齐。
if GenApi.sha1_prefix_len(name) != 0: return name
sha1: str = trans.ModuleSha1
sha1_len: sizet = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None: return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# ============================================================
# _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀
#
# 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling,
# 而非当前模块的 SHA1。
# ============================================================
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
"""用指定 SHA1 为名称添加前缀(不做 main/export 检查)"""
if sha1 is None or name is None: return name
name_len: sizet = string.strlen(name)
# 已带 SHA1 前缀("{16hex}.")则跳过
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`(口径对齐同 _mangle_name)。
if GenApi.sha1_prefix_len(name) != 0: return name
sha1_len: sizet = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None: return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# ============================================================
# _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查)
#
# 规则:
# - main 函数不加前缀(程序入口点)
# - has_export 非 0 时不加前缀(t.CExport 导出函数)
# - 已带 SHA1 前缀的不再加
# - ModuleSha1 为 None 时不加前缀
# ============================================================
def _mangle_func_name(self, trans: HT.Translator*, name: str,
has_export: int) -> str:
"""为函数名添加 SHA1 前缀(检查 main/export)"""
if trans is None or name is None: return name
if has_export != 0: return name
if string.strcmp(name, "main") == 0: return name
return self._mangle_name(trans, name)
# ============================================================
# _body_has_raise - 函数体是否含 raise(非 StopIteration)
#
# 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名
# 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体
# 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。
# ============================================================
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
if fd is None or fd.children is None:
return 0
body_f: list[ast.AST*]* = fd.children
bn_f: sizet = len(body_f)
for bi_f in range(bn_f):
stmt_f: ast.AST* = body_f.get(bi_f)
if stmt_f is None: continue
if stmt_f.kind() != ast.ASTKind.Raise: continue
rn_f: rawptr[ast.Raise] = rawptr[ast.Raise](stmt_f)
if rn_f is None or rn_f.exc is None: continue
ek_f: int = rn_f.exc.kind()
is_stop_f: int = 0
if ek_f == ast.ASTKind.Name:
enm_f: ast.Name* = ast.Name*(rn_f.exc)
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0:
is_stop_f = 1
elif ek_f == ast.ASTKind.Call:
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0:
is_stop_f = 1
if is_stop_f == 0:
return 1
return 0
# ============================================================
# RejectCReturnDecorator - @c.CReturn 装饰器已移除,遇到即编译期报错
#
# 多返回值唯一入口是返回注解 `-> tuple[ty1, ty2, ...]`(tuple → struct 通路
# 逐字保留)。任何装饰器形态出现 CReturn 一律 fail-fast,不静默忽略。
# 供 FuncGenDef(pre_scan 前向声明 / translate_function_def 定义两阶段)复用;
# Converter 为模块级函数,同报文另写一份(见 StubGen/Converter.vp)。
# 无 CReturn 返回 0。
# ============================================================
def RejectCReturnDecorator(self, node: ast.AST*,
decorators: list[ast.AST*]*) -> int:
"""装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)"""
if decorators is None: return 0
rcr_dn: sizet = len(decorators)
for rcr_i in range(rcr_dn):
rcr_deco: ast.AST* = decorators.get(rcr_i)
if rcr_deco is None: continue
# 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态
rcr_attr: ast.Attribute* = None
if rcr_deco.kind() == ast.ASTKind.Call:
rcr_call: ast.Call* = ast.Call*(rcr_deco)
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute:
rcr_attr = ast.Attribute*(rcr_call.func)
elif rcr_deco.kind() == ast.ASTKind.Attribute:
rcr_attr = ast.Attribute*(rcr_deco)
if rcr_attr is None or rcr_attr.attr is None: continue
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct 返回)", "TypeError")
return 1
return 0
# ============================================================
# 创建 LLVM 函数(简单版本,仅声明)
# ============================================================
def create_function(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
name: str,
args_node: ast.AST*,
ret_ty: llvmlite.LLVMType*) -> llvmlite.Function*:
"""创建 LLVM 函数并添加参数"""
if name is None or mod is None: return None
func: llvmlite.Function* = llvmlite.create_function(pool, mod, name, ret_ty)
if func is None: return None
if args_node is not None:
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
for ai in range(an):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
# t\.CVoid 表示空参:跳过
if arg.annotation is not None:
if arg.annotation.kind() == ast.ASTKind.Attribute:
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation)
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue
param_ty: llvmlite.LLVMType* = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
if resolved is None:
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32。
# 本函数不传 trans/imports 上下文,解析失败即调用方未补齐
# 上下文(暴露上游,不由本函数兜底)。
self.Gen.TypeGenErr.fatal_error(arg, f"函数 '{name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
return None
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None:
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
llvmlite.add_param(pool, func, param_ty, pname)
return func
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-445
View File
@@ -1,445 +0,0 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
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.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Gen.CallGenBase as CallGenBase
# ============================================================
# FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class FuncGenBase(CallGenBase.CallGenBase):
"""函数属性 / 命名 / mangle / create_function 基础工具"""
# ============================================================
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
#
# 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式
# 也支持 @c.Attribute(t.attr.llvm.xxx) 形式
#
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
# ============================================================
def extract_func_attrs(self, pool: memhub.MemBuddy | t.CPtr,
decorator_list: list[ast.AST | t.CPtr] | t.CPtr,
imported_modules: str) -> str:
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
if decorator_list is None:
return None
dn: t.CSizeT = len(decorator_list)
if dn == 0:
return None
attrs_buf: str = pool.alloc(256)
if attrs_buf is None:
return None
attrs_buf[0] = '\0'
found_any: int = 0
# [ATTR-DIAG] 运行时诊断:定位 @c.Attribute 属性为何未进入 IR(stackrealign)
cur_file: str = self.Gen.TypeGenResolve.get_current_file()
if cur_file is None:
cur_file = "?"
stdio.printf("[ATTR] extract_func_attrs: file='%s' dlist_len=%d\n", cur_file, dn)
for di in range(dn):
deco: ast.AST | t.CPtr = decorator_list.get(di)
if deco is None:
stdio.printf("[ATTR] deco[%d]=None\n", di)
continue
dk: int = deco.kind()
stdio.printf("[ATTR] deco[%d] kind=%d\n", di, dk)
if dk != ast.ASTKind.Call:
continue
call_node: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco)
if call_node.func is None:
stdio.printf("[ATTR] call.func=None\n")
continue
cfk: int = call_node.func.kind()
stdio.printf("[ATTR] call.func kind=%d\n", cfk)
if cfk != ast.ASTKind.Attribute:
continue
# 检测 c.Attribute
func_attr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(call_node.func)
if func_attr.attr is None:
stdio.printf("[ATTR] func_attr.attr=None\n")
continue
stdio.printf("[ATTR] func_attr.attr='%s'\n", func_attr.attr)
if string.strcmp(func_attr.attr, "Attribute") != 0:
continue
if func_attr.value is None:
stdio.printf("[ATTR] func_attr.value=None\n")
continue
fvk: int = func_attr.value.kind()
stdio.printf("[ATTR] func_attr.value kind=%d\n", fvk)
if fvk != ast.ASTKind.Name:
continue
c_name: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_attr.value)
if c_name.id is None:
stdio.printf("[ATTR] c_name.id=None\n")
continue
stdio.printf("[ATTR] c_name.id='%s'\n", c_name.id)
if string.strcmp(c_name.id, "c") != 0:
continue
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
stdio.printf("[ATTR] is_module_imported('c')=%d\n", imp_c)
if imp_c == 0:
continue
# 遍历参数提取属性
if call_node.args is None:
stdio.printf("[ATTR] call.args=None\n")
continue
dbuf: bytes = pool.alloc(1024)
if dbuf is not None:
dbuf[0] = '\0'
dpos: t.CSizeT = deco.dump(dbuf, 1024, 0)
if dpos < 1024:
dbuf[dpos] = '\0'
stdio.printf("[ATTR] deco.dump='%s'\n", dbuf)
args_list: list[ast.AST | t.CPtr] | t.CPtr = call_node.args
an: t.CSizeT = len(args_list)
stdio.printf("[ATTR] args_len=%d\n", an)
if call_node.keywords is not None:
kwl: t.CSizeT = len(call_node.keywords)
stdio.printf("[ATTR] keywords_len=%d\n", kwl)
for kwi in range(kwl):
kwnd: ast.AST | t.CPtr = call_node.keywords.get(kwi)
if kwnd is not None:
stdio.printf("[ATTR] kw[%d] kind=%d\n", kwi, kwnd.kind())
for ai in range(an):
arg_node: ast.AST | t.CPtr = args_list.get(ai)
if arg_node is None:
stdio.printf("[ATTR] arg[%d]=None\n", ai)
continue
stdio.printf("[ATTR] arg[%d] kind=%d\n", ai, arg_node.kind())
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None:
stdio.printf("[ATTR] arg[%d] name=None\n", ai)
continue
# 映射到 LLVM 属性名并追加
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
stdio.printf("[ATTR] arg[%d] name='%s' append=%d buf='%s' blen=%zu b0=0x%02x\n",
ai, attr_name, ar, attrs_buf, string.strlen(attrs_buf), attrs_buf[0])
if ar != 0:
found_any = 1
# 追加成功后,如果不是最后一个属性,添加空格分隔
# 在下一次追加前由 _append_str 处理
stdio.printf("[ATTR] found_any=%d return='%s'\n", found_any, attrs_buf)
if found_any == 0:
return None
return attrs_buf
def _get_attr_name_from_node(self, node: ast.AST | t.CPtr) -> str:
"""从 AST 节点提取属性名
支持:
- Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline"
- Attribute: t.attr.packed -> "packed"
- Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind"
"""
if node is None:
return None
k: int = node.kind()
# Call 节点: t.attr.always_inline()
if k == ast.ASTKind.Call:
call: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if call.func is None or call.func.kind() != ast.ASTKind.Attribute:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(call.func)
return at.attr
# Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind
if k == ast.ASTKind.Attribute:
at2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
return at2.attr
return None
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
"""将属性名映射到 LLVM 属性名并追加到缓冲区
返回 1 表示已追加,0 表示不支持该属性
"""
if attrs_buf is None or attr_name is None:
return 0
# 先确定映射的 LLVM 属性名
llvm_name: str = None
if string.strcmp(attr_name, "always_inline") == 0:
llvm_name = "alwaysinline"
elif string.strcmp(attr_name, "noinline") == 0:
llvm_name = "noinline"
elif string.strcmp(attr_name, "noreturn") == 0:
llvm_name = "noreturn"
elif string.strcmp(attr_name, "pure") == 0:
llvm_name = "readonly"
elif string.strcmp(attr_name, "const") == 0:
llvm_name = "readnone"
elif string.strcmp(attr_name, "nounwind") == 0:
llvm_name = "nounwind"
elif string.strcmp(attr_name, "noredzone") == 0:
llvm_name = "noredzone"
elif string.strcmp(attr_name, "stackrealign") == 0:
# 动态重对齐栈(LLVM 序言 and rsp,-16)。入口函数(_start)由 bootloader
# jmp 进入(rsp≡0 mod 16),而 llc 按 call 约定假设入口 rsp≡8 → 帧整体
# 偏 8 → 所有调用 rsp≡8 → 变参函数保存 xmm 的 movaps #GP。stackrealign
# 使序言动态对齐,与入口约定无关(sched started 后 movaps #GP 根因)。
# 注意:stackrealign 是 LLVM 字符串属性,必须带引号("stackrealign"),
# 与 noinline/nounwind 等枚举属性(无引号)区分,否则 llc 报
# "expected '{' in function body"。
llvm_name = '"stackrealign"'
elif string.strcmp(attr_name, "willreturn") == 0:
llvm_name = "willreturn"
elif string.strcmp(attr_name, "mustprogress") == 0:
llvm_name = "mustprogress"
else:
# packed/aligned/section/visibility/weak 等不支持作为函数内联属性
return 0
# 支持的属性:如果缓冲区非空,先追加空格分隔
if attrs_buf[0] != '\0':
self._append_str(attrs_buf, " ", 256)
self._append_str(attrs_buf, llvm_name, 256)
return 1
def _append_str(self, buf: str, s: str,
max_len: t.CSizeT = 256) -> None:
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
if buf is None or s is None:
return
# 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0),
# 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。
if max_len == 0:
max_len = 256
cur_len: t.CSizeT = string.strlen(buf)
s_len: t.CSizeT = string.strlen(s)
stdio.printf("[ATTR] _append_str: max_len=%zu cur=%zu slen=%zu s='%s'\n", max_len, cur_len, s_len, s)
if cur_len + s_len + 1 > max_len:
return # 无剩余容量:丢弃本次追加,避免越界写
string.strcpy(buf + cur_len, s)
# ============================================================
# _mangle_name - 为名称添加 SHA1 前缀
#
# 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带
# SHA1 前缀(16 hex + '.'),则原样返回。
# ============================================================
def _mangle_name(self, trans: HT.Translator | t.CPtr, name: str) -> str:
"""为任意名称添加 SHA1 前缀(不做 main/export 检查)"""
if trans is None or name is None:
return name
if trans.ModuleSha1 is None:
return name
name_len: t.CSizeT = string.strlen(name)
# 已带 SHA1 前缀(16 hex + '.')则跳过
if name_len > 17:
is_sha1: int = 1
for i in range(16):
c: t.CChar = name[i]
if not (('0' <= c <= '9') or ('a' <= c <= 'f')):
is_sha1 = 0
break
if is_sha1 != 0 and name[16] == '.':
return name
sha1: str = trans.ModuleSha1
sha1_len: t.CSizeT = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None:
return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# ============================================================
# _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀
#
# 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling,
# 而非当前模块的 SHA1。
# ============================================================
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
"""用指定 SHA1 为名称添加前缀(不做 main/export 检查)"""
if sha1 is None or name is None:
return name
name_len: t.CSizeT = string.strlen(name)
# 已带 SHA1 前缀(16 hex + '.')则跳过
if name_len > 17:
is_sha1: int = 1
for i in range(16):
c2: t.CChar = name[i]
if not (('0' <= c2 <= '9') or ('a' <= c2 <= 'f')):
is_sha1 = 0
break
if is_sha1 != 0 and name[16] == '.':
return name
sha1_len: t.CSizeT = string.strlen(sha1)
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
if mangled is None:
return name
string.strcpy(mangled, sha1)
mangled[sha1_len] = '.'
string.strcpy(mangled + sha1_len + 1, name)
return mangled
# ============================================================
# _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查)
#
# 规则:
# - main 函数不加前缀(程序入口点)
# - has_export 非 0 时不加前缀(t.CExport 导出函数)
# - 已带 SHA1 前缀的不再加
# - ModuleSha1 为 None 时不加前缀
# ============================================================
def _mangle_func_name(self, trans: HT.Translator | t.CPtr, name: str,
has_export: int) -> str:
"""为函数名添加 SHA1 前缀(检查 main/export)"""
if trans is None or name is None:
return name
if has_export != 0:
return name
if string.strcmp(name, "main") == 0:
return name
return self._mangle_name(trans, name)
# ============================================================
# _body_has_raise - 函数体是否含 raise(非 StopIteration)
#
# 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名
# 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体
# 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。
# ============================================================
def _body_has_raise(self, fd: ast.FunctionDef | t.CPtr) -> int:
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
if fd is None or fd.children is None:
return 0
body_f: list[ast.AST | t.CPtr] | t.CPtr = fd.children
bn_f: t.CSizeT = len(body_f)
for bi_f in range(bn_f):
stmt_f: ast.AST | t.CPtr = body_f.get(bi_f)
if stmt_f is None:
continue
if stmt_f.kind() != ast.ASTKind.Raise:
continue
rn_f: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(stmt_f)
if rn_f is None or rn_f.exc is None:
continue
ek_f: int = rn_f.exc.kind()
is_stop_f: int = 0
if ek_f == ast.ASTKind.Name:
enm_f: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn_f.exc)
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0:
is_stop_f = 1
elif ek_f == ast.ASTKind.Call:
ecl_f: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn_f.exc)
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
ecn_f: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl_f.func)
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0:
is_stop_f = 1
if is_stop_f == 0:
return 1
return 0
# ============================================================
# _resolve_creturn_ret_ty - @c.CReturn(ty1, ty2, ...) → 多返回 struct 类型
#
# 对齐 TPC DeclarationGenerator:CReturn 声明函数返回多个值(如
# GetMemoryStats @c.CReturn(t.CUInt64T, t.CUInt64T, t.CUInt64T) →
# {i64, i64, i64})。pre_scan 前向声明与 translate_function_def 定义
# 两阶段必须一致,否则 declare i32 + define {i64,i64,i64} → llc
# "ret doesn't match function result type"。
# ============================================================
def _resolve_creturn_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
fd: ast.FunctionDef | t.CPtr,
imported_modules: str,
from_imports: str,
trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""解析 @c.CReturn 装饰器为 struct 返回类型;无 CReturn 返回 None"""
if fd is None or fd.decorator_list is None:
return None
cr_head: llvmlite.ParamNode | t.CPtr = None
cr_last: llvmlite.ParamNode | t.CPtr = None
cr_count: int = 0
cr_dn: t.CSizeT = len(fd.decorator_list)
for cdi in range(cr_dn):
deco_cr: ast.AST | t.CPtr = fd.decorator_list.get(cdi)
if deco_cr is None or deco_cr.kind() != ast.ASTKind.Call:
continue
deco_call_cr: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco_cr)
if deco_call_cr.func is None or deco_call_cr.func.kind() != ast.ASTKind.Attribute:
continue
deco_fa_cr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(deco_call_cr.func)
if deco_fa_cr.attr is None or string.strcmp(deco_fa_cr.attr, "CReturn") != 0:
continue
cr_args: list[ast.AST | t.CPtr] | t.CPtr = deco_call_cr.args
if cr_args is None:
continue
cr_an: t.CSizeT = len(cr_args)
for cai in range(cr_an):
cr_aty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, cr_args.get(cai), imported_modules, from_imports, trans, 0)
if cr_aty is None:
continue
cr_pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, cr_aty)
if cr_pn is not None:
if cr_head is None:
cr_head = cr_pn
else:
cr_last.Next = cr_pn
cr_last = cr_pn
cr_count += 1
if cr_head is not None and cr_count > 0:
return llvmlite.Struct(pool, cr_head, cr_count, None)
return None
# ============================================================
# 创建 LLVM 函数(简单版本,仅声明)
# ============================================================
def create_function(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
name: str,
args_node: ast.AST | t.CPtr,
ret_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Function | t.CPtr:
"""创建 LLVM 函数并添加参数"""
if name is None or mod is None:
return None
func: llvmlite.Function | t.CPtr = llvmlite.create_function(pool, mod, name, ret_ty)
if func is None:
return None
if args_node is not None:
ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
if ags.args is not None:
alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args
an: t.CSizeT = len(alist)
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
for ai in range(an):
arg: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(ai))
if arg is not None and arg.arg is not None:
# t.CVoid 表示空参:跳过
if arg.annotation is not None:
if arg.annotation.kind() == ast.ASTKind.Attribute:
at_cf: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(arg.annotation)
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0:
continue
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, arg.annotation, None, None, None, 0)
if resolved is not None:
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None:
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
llvmlite.add_param(pool, func, param_ty, pname)
return func
+463 -286
View File
File diff suppressed because it is too large. Load diff
+434 -193
View File
@@ -1,193 +1,434 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
# ============================================================
# GenTypes - Gen 侧共享数据类型与常量(去除 Handles 层迁移目标)
#
# 原分布于 Handles 目录的数据类与常量集中于此,供 Gen 子对象、
# 管线与保留实体文件引用。删除 Handles 目录后统一经本模块访问。
# ============================================================
# ============================================================
# 类编译期元数据(原 HandlesType.CLASS_META_*)
# ============================================================
CLASS_META_PROVIDES: t.CDefine = 1
CLASS_META_REQUIRES: t.CDefine = 2
CLASS_META_REQUIRE_MUST: t.CDefine = 3
# ============================================================
# 属性/静态/类方法元数据(原 HandlesClassDef.META_*)
# ============================================================
META_STATIC: t.CDefine = 1
META_CLASS: t.CDefine = 2
META_PROP_GETTER: t.CDefine = 4
META_PROP_SETTER: t.CDefine = 8
META_PROP_DELETER: t.CDefine = 16
# ============================================================
# 结构体容量(原 HandlesStruct.FIELD_MAX/FIELD_NAME_MAX/PARENT_MAX/MRO_MAX)
# ============================================================
FIELD_MAX: t.CDefine = 256
FIELD_NAME_MAX: t.CDefine = 64
PARENT_MAX: t.CDefine = 64
MRO_MAX: t.CDefine = 64
# ============================================================
# for 迭代序列种类(原 HandlesFor.SEQ_KIND_*)
# ============================================================
SEQ_KIND_RANGE: t.CDefine = 1
SEQ_KIND_STR: t.CDefine = 2
SEQ_KIND_LIST: t.CDefine = 3
# ============================================================
# TraceFrame - 仿 Python Traceback 调用栈帧(原 HandlesType.TraceFrame)
# ============================================================
class TraceFrame:
"""仿 Python Traceback 调用栈帧(push_trace/pop_trace 使用)"""
file_name: str
lineno: int
col_start: int
col_end: int
# ============================================================
# VarEntry - 变量表条目(原 HandlesVar.VarEntry)
# ============================================================
class VarEntry:
"""变量表条目"""
Name: str
Alloca: llvmlite.Value | t.CPtr
Used: t.CInt
AnnotClassName: str # 原始类型注解的类名(str 别名在结构体字段中触发编译器 bug,改用显式联合类型)
IsPtrElement: t.CInt # 标志: 1=注解为 bytes|t.CPtr 或 str|t.CPtr, 下标按 8 字节步长
# ============================================================
# Scope - 作用域节点(原 HandlesVar.Scope)
# ============================================================
@t.NoVTable
class Scope:
"""作用域节点"""
Parent: Scope | t.CPtr
Vars: VarEntry | t.CPtr
VarCount: t.CInt
Kind: t.CInt
# ============================================================
# SymbolTable - 嵌套作用域符号表(原 HandlesVar.SymbolTable)
# ============================================================
@t.NoVTable
class SymbolTable:
"""嵌套作用域符号表"""
Pool: memhub.MemBuddy | t.CPtr
Root: Scope | t.CPtr
Current: Scope | t.CPtr
# ============================================================
# FieldEntry - 字段信息条目(原 HandlesStruct.FieldEntry)
# ============================================================
class FieldEntry:
Name: str # 字段名(字符串)
Index: int # 字段在结构体中的索引
Ty: llvmlite.LLVMType | t.CPtr # 字段的 LLVM 类型
DefaultVal: ast.AST | t.CPtr # 默认值 AST 节点(None=无默认值)
AnnotClassName: str # 原始类型注解的类名(str 别名在结构体字段中触发编译器 bug,改用显式联合类型)
# ============================================================
# StructEntry - 结构体注册条目(原 HandlesStruct.StructEntry)
# ============================================================
@t.NoVTable
class StructEntry:
Name: str # 类名
Ty: llvmlite.LLVMType | t.CPtr # LLVM StructType
FieldCount: int # 字段数量
Fields: FieldEntry | t.CPtr # 字段数组(FIELD_MAX 个槽位)
IsUnion: int # 1=联合体 / 0=普通结构体
IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体
HasInit: int # 1=有__init__方法 / 0=无
HasNew: int # 1=有__new__方法 / 0=无
HasVTable: int # 1=有虚表 / 0=无虚表
IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记
ParentName: str # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用)
ParentNames: t.CSizeT | t.CPtr # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝)
ParentCount: t.CInt # 父类数量(0=无父类,1=单继承,>1=多继承)
VTableMethodCount: int # 虚表中的方法数量
VTableMethods: str # 虚表方法名数组(每个方法名 str,VTableMethodCount 个)
ModuleSha1: str # 定义该类的模块 SHA1(None=无 SHA1)
MethodsEmitted: int # 1=方法体已翻译 / 0=未译(Phase 1a mark_only 未译,Phase B 据此补译)
SpecTriggerSha1: str # 泛型特化类的触发文件 SHA1(None=非特化类)。特化类 ModuleSha1=模板
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
# → 链接 undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
# ============================================================
# EnumMember - 枚举成员(原 HandlesEnum.EnumMember)
# ============================================================
class EnumMember:
Name: str # 成员名(字符串)
Value: t.CInt64T # 成员的整数值
Ty: llvmlite.LLVMType | t.CPtr # 成员的 LLVM 类型(用于混用类型场景)
# ============================================================
# EnumEntry - 枚举类型条目(原 HandlesEnum.EnumEntry)
# ============================================================
@t.NoVTable
class EnumEntry:
Name: str # 枚举类名
BaseTy: llvmlite.LLVMType | t.CPtr # 基准类型(所有成员类型中最大的)
MemberCount: int # 成员数量
Members: EnumMember | t.CPtr # 成员数组(ENUM_MEMBER_MAX 个槽位)
# ============================================================
# AsmOperand - 内联汇编操作数条目(原 HandlesExprCall.AsmOperand)
# ============================================================
class AsmOperand:
"""内联汇编操作数条目"""
Value: llvmlite.Value | t.CPtr # 输入值或输出 alloca
Constraint: str # 约束字符串 (如 "r", "=r")
IsOutput: int # 1=输出, 0=输入
# ============================================================
# FuncEntry - 函数表条目(原 HandlesExprCall.FuncEntry)
# ============================================================
@t.NoVTable
class FuncEntry:
"""函数表条目"""
Name: str
Func: llvmlite.Function | t.CPtr
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值
DefaultCount: int # 默认值数量
ParamCount: int # 形参数量(不含 self)
# TypeStr: 函数类型文本(stdlib 深拷贝,跨文件安全)
# 1b 每文件 reset_to(mark1) 回收 pool 内 Function 对象后,
# vtable 生成 find_func_global 读类型改用此文本,避免悬空。
TypeStr: str
# ============================================================
# FuncDefaultsEntry - 默认参数表条目(原 HandlesExprCall.FuncDefaultsEntry)
# ============================================================
@t.NoVTable
class FuncDefaultsEntry:
"""默认参数表条目"""
Name: str
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
DefaultCount: int
ParamCount: int # 形参数量(不含 self)
# 常量快照(stdlib 分配,跨文件永久存活):
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
ConstKinds: t.CInt | t.CPtr # const_kind 数组(DefaultCount 个 i32)
ConstInts: t.CInt64T | t.CPtr # int_val 数组(DefaultCount 个 i64)
ConstStrs: t.CSizeT | t.CPtr # str_val 指针数组(DefaultCount 个 i8*)
import t, c
from stdint import *
import ast
import llvmlite
import memhub
# TypeSpec:统一类型描述符(Debugger/_Type.md 第 6 节设计)。
# 别名用 TSpec:模块内类名同为 TypeSpec,别名同名会被解析器误判为类静态方法。
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# GenTypes - Gen 侧共享数据类型与常量(去除 Handles 层迁移目标)
#
# 原分布于 Handles 目录的数据类与常量集中于此,供 Gen 子对象、
# 管线与保留实体文件引用。删除 Handles 目录后统一经本模块访问。
# ============================================================
# ============================================================
# 类编译期元数据(原 HandlesType.CLASS_META_*)
# ============================================================
CLASS_META_PROVIDES: t.CDefine = 1
CLASS_META_REQUIRES: t.CDefine = 2
CLASS_META_REQUIRE_MUST: t.CDefine = 3
# ============================================================
# 属性/静态/类方法元数据(原 HandlesClassDef.META_*)
# ============================================================
META_STATIC: t.CDefine = 1
META_CLASS: t.CDefine = 2
META_PROP_GETTER: t.CDefine = 4
META_PROP_SETTER: t.CDefine = 8
META_PROP_DELETER: t.CDefine = 16
# ============================================================
# 结构体容量(原 HandlesStruct.FIELD_MAX/FIELD_NAME_MAX/PARENT_MAX/MRO_MAX)
# ============================================================
FIELD_MAX: t.CDefine = 256
FIELD_NAME_MAX: t.CDefine = 64
PARENT_MAX: t.CDefine = 64
MRO_MAX: t.CDefine = 64
# ============================================================
# for 迭代序列种类(原 HandlesFor.SEQ_KIND_*)
# ============================================================
SEQ_KIND_RANGE: t.CDefine = 1
SEQ_KIND_STR: t.CDefine = 2
SEQ_KIND_LIST: t.CDefine = 3
# ============================================================
# TraceFrame - 仿 Python Traceback 调用栈帧(原 HandlesType.TraceFrame)
# ============================================================
class TraceFrame:
"""仿 Python Traceback 调用栈帧(push_trace/pop_trace 使用)"""
file_name: str
lineno: int
col_start: int
col_end: int
# ============================================================
# VarEntry - 变量表条目(原 HandlesVar.VarEntry)
# ============================================================
class VarEntry:
"""变量表条目"""
Name: str
Alloca: llvmlite.Value*
Used: int
# TypeSpec(S3 起为**唯一类型承载**):由 add_var 用 SpecFromLLVMType 从
# alloca.Ty 的 pointee 填充(PermPool 分配 ⇒ 跨文件安全),注解侧再由
# set_var_annot_class_name 经 SpecWithSign / SpecWithSource 补整数符号性与
# 注解来源名(Spec.SourceName,原 AnnotClassName 字段已并入),由
# set_var_ptr_element 经 SpecWithElemBits 补「指针元素」语义(原
# IsPtrElement 字段已并入 Spec.ElemBits)。
Spec: TSpec.TypeSpec*
# 1=该变量的 load/store 带 LLVM volatile 限定符(注解联合含 t.CVolatile)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
IsVolatile: int
# 1=该变量是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):解引用(p[i] / p.field / c.Deref(p))的 load/store 带 LLVM
# volatile,禁止优化器重排序/消除对所指对象的每次访问。注意与 IsVolatile
# 的区别:IsVolatile 限定"指针变量自身"的读写,本字段限定"指针所指物"。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
PointeeVolatile: int
# 1=该变量是 **const 对象**(注解含 t.CConst 且声明类型**不是指针**):
# 整个作用域内禁止对该变量名赋值 / 增量赋值 / 元素赋值(fail-fast)。
# 与 TPC 的 Gen.var_const_flags / global_const_flags 同判据、同报文,见
# Debugger/type-descriptors.md。指针类型(如 `p: t.CConst | T | t.CPtr`)的
# CConst 只约束 pointee,不使指针变量自身不可写,故不置 1。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
IsConst: int
# ============================================================
# Scope - 作用域节点(原 HandlesVar.Scope)
# ============================================================
novirtual class Scope:
"""作用域节点"""
Parent: Scope | t.CPtr
Vars: VarEntry | t.CPtr
VarCount: int
Kind: int
# ============================================================
# SymbolTable - 嵌套作用域符号表(原 HandlesVar.SymbolTable)
# ============================================================
novirtual class SymbolTable:
"""嵌套作用域符号表"""
Pool: memhub.MemBuddy*
Root: Scope | t.CPtr
Current: Scope | t.CPtr
# ============================================================
# FieldEntry - 字段信息条目(原 HandlesStruct.FieldEntry)
# ============================================================
class FieldEntry:
Name: str # 字段名(字符串)
Index: int # 字段在结构体中的索引
Ty: llvmlite.LLVMType* # 字段的 LLVM 类型
DefaultVal: ast.AST* # 默认值 AST 节点(None=无默认值)
# TypeSpec(S3 起为**唯一类型承载**):由 add_field 用 SpecFromLLVMType 从
# field_ty 填充(PermPool 分配 ⇒ 跨 1b reset 安全,无需 DeepCopyType),
# 注解侧再经 SpecWithSign / SpecWithSource 补符号性与注解来源名
# (Spec.SourceName,原 AnnotClassName 字段已并入)。
Spec: TSpec.TypeSpec*
# 1=该字段经对象访问的 load/store 带 LLVM volatile 限定符
# (字段注解联合含 t.CVolatile,如 Spinlock.locked: t.CVolatile | t.CInt)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
IsVolatile: int
# 1=该字段是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):经该字段解引用(self.ptr[i] / self.ptr.field)的 load/store
# 带 LLVM volatile。与 IsVolatile 的区别同 VarEntry.PointeeVolatile。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
PointeeVolatile: int
# 1=该字段是 **const 对象**(注解含 t.CConst 且字段类型**不是指针**):
# 经对象给该字段赋值(obj.f = ... / self.f = ...)即 fail-fast,与
# VarEntry.IsConst 同判据、同报文(TPC 侧对应 Gen.field_const_flags)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
IsConst: int
# ============================================================
# StructEntry - 结构体注册条目(原 HandlesStruct.StructEntry)
# ============================================================
novirtual class StructEntry:
Name: str # 类名
Ty: llvmlite.LLVMType* # LLVM StructType
FieldCount: int # 字段数量
Fields: FieldEntry | t.CPtr # 字段数组(FIELD_MAX 个槽位)
IsUnion: int # 1=联合体 / 0=普通结构体
IsPacked: int # 1=@c.Attribute(t.attr.packed) 打包结构体(无字段填充)
Align: int # @c.Attribute(t.attr.aligned(N)) 对齐字节数(0=默认,如 fpu_context=16)
IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体
HasInit: int # 1=有__init__方法 / 0=无
HasNew: int # 1=有__new__方法 / 0=无
HasVTable: int # 1=有虚表 / 0=无虚表
IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记
ParentName: str # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用)
ParentNames: sizet* # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝)
ParentCount: int # 父类数量(0=无父类,1=单继承,>1=多继承)
VTableMethodCount: int # 虚表中的方法数量
VTableMethods: str # 虚表方法名数组(每个方法名 str,VTableMethodCount 个)
ModuleSha1: str # 定义该类的模块 SHA1(None=无 SHA1)
MethodsEmitted: int # 1=方法体已翻译 / 0=未译(Phase 1a mark_only 未译,Phase B 据此补译)
SpecTriggerSha1: str # 泛型特化类的触发文件 SHA1(None=非特化类)。特化类 ModuleSha1=模板
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
# → 链接 undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
IsForwardDecl: int # 1=前向声明占位条目(Ty=不透明具名结构体 {sha}.Class,FieldCount=0;
# 定义侧随后**原位填充**同一 Ty 对象并清零本标记)。
# 语义对齐 TPC:未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充。
# ============================================================
# EnumMember - 枚举成员(原 HandlesEnum.EnumMember)
# ============================================================
class EnumMember:
Name: str # 成员名(字符串)
Value: int64 # 成员的整数值
Ty: llvmlite.LLVMType* # 成员的 LLVM 类型(用于混用类型场景)
# ============================================================
# EnumEntry - 枚举类型条目(原 HandlesEnum.EnumEntry)
# ============================================================
novirtual class EnumEntry:
Name: str # 枚举类名
BaseTy: llvmlite.LLVMType* # 基准类型(所有成员类型中最大的)
MemberCount: int # 成员数量
Members: EnumMember | t.CPtr # 成员数组(ENUM_MEMBER_MAX 个槽位)
# ============================================================
# AsmOperand - 内联汇编操作数条目(原 HandlesExprCall.AsmOperand)
# ============================================================
class AsmOperand:
"""内联汇编操作数条目"""
Value: llvmlite.Value* # 输入值或输出 alloca
Constraint: str # 约束字符串 (如 "r", "=r")
IsOutput: int # 1=输出, 0=输入
# ============================================================
# FuncEntry - 函数表条目(原 HandlesExprCall.FuncEntry)
# ============================================================
novirtual class FuncEntry:
"""函数表条目"""
Name: str
Func: llvmlite.Function*
Defaults: t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值
DefaultCount: int # 默认值数量
ParamCount: int # 形参数量(不含 self)
# TypeSpec(S3 条目双写):Kind=TS_FUNC 的**整签名**(Ret/Params/ParamCount/IsVarArg
# 都在其内),由 update_global_func_spec 在形参 add_param 完成后用 NewTypeFunc 构造。
# PermPool 驻留 ⇒ 跨 1b reset 安全。单字段即可完整表达签名,与 VarEntry/FieldEntry
# 的 Spec 命名一致,避免参数数组重复。
Spec: TSpec.TypeSpec*
# ============================================================
# FuncDefaultsEntry - 默认参数表条目(原 HandlesExprCall.FuncDefaultsEntry)
# ============================================================
novirtual class FuncDefaultsEntry:
"""默认参数表条目"""
Name: str
ModuleSha1: str # 定义该函数的模块 SHA1(限定名去重,避免跨模块同名串味)
Defaults: t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
DefaultCount: int
ParamCount: int # 形参数量(不含 self)
# 常量快照(stdlib 分配,跨文件永久存活):
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
ConstKinds: int | t.CPtr # const_kind 数组(DefaultCount 个 i32)
ConstInts: int64 | t.CPtr # int_val 数组(DefaultCount 个 i64)
ConstStrs: sizet* # str_val 指针数组(DefaultCount 个 i8*)
# ============================================================
# REPL 会话(M2/M3):ctx 缓冲区布局 + 会话表条目 + 结果区头
#
# ctx 缓冲区由宿主(lib/core/Repl.vp)在 PermPool 一次性分配,片段函数以首形参
# 接收(注解 bytes ⇒ i8*),同一进程内宿主与翻译器共享同一块内存。
#
# 布局(用户拍板子约定;字节全小端,无符号/有符号按位模式):
#
# 偏移 0 magic(u32 = 'REPL' = 0x5245504C) 4 version(u32 = 2)
# 偏移 8 result_kind(i32) 12 保留(0)
# 偏移 16 result_a(u64) 24 result_b(u64)
# 偏移 32 var_count(i32) 36 last_frag(i32)
# 偏移 40..63 保留(0)
# 偏移 64 + i*16 第 i 个会话变量的槽(步长 16 字节),i < REPL_VAR_CAP
# 偏移 1088 + j*16 第 j 个会话函数的指针槽(步长 16 字节),j < REPL_FUNC_CAP
#
# 版本与向后兼容(version 1 → 2,**纯尾部追加**):
# v1 = 头 64B + 变量槽区 1024B(共 1088B);v2 在 1088 之后追加函数指针槽区
# (REPL_FUNC_CAP*REPL_FUNC_SLOT_SIZE)。前 1088 字节的字段偏移/宽度/语义逐字
# 未变 ⇒ 按 v1 生成的旧片段 DLL 与 v2 宿主混用仍然正确(旧片段只读写
# < 1088 的字节);新片段读函数指针槽时宿主必为 v2 ⇒ 无版本协商需求。
# 头 +4 写 2 仅作"含函数指针槽区"的显式标记(片段侧不校验该字段)。
#
# result_kind:
# 0 = none 本片段无结果(声明类片段)
# 1 = int/uint result_a = 值(补码位模式,宿主按有符号 64 位回显)
# 2 = float64 result_a = IEEE754 double 位模式(宿主重解释后回显)
# 3 = str/bytes result_a = 指针;result_b = 长度(0 = 以 NUL 结尾,宿主按
# C 字符串处理——片段侧不额外调 strlen,故长度未知时写 0)
# 4 = struct/其它 result_a = 值地址(结构体值装箱后的盒子地址 / 非字符串指针)
#
# 槽内存放形态(ReplVarEntry.Kind):
# SCALAR — 标量/指针(≤8 字节)的值直接存槽;
# BOX — 结构体值:片段侧 malloc 盒子(值存盒内),槽内存盒子指针。
# 槽步长 16 字节(结构体/字符串统一走指针 ⇒ 宿主无需知道类型);负载一律
# ≤8 字节且 8 字节对齐即合法(MemBuddy 用户指针实测 8 字节对齐)。
# 槽数上限 REPL_VAR_CAP ⇒ 变量区尺寸 = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE。
#
# 函数指针槽(M3):槽内是会话函数在**定义片段 DLL** 里的入口地址(void*,8 字节;
# DLL 常驻不卸载 ⇒ 地址在会话期内有效)。宿主在片段 DLL 加载成功后
# GetProcAddress(裸名) 取得地址并写入槽;后续片段的调用点从槽 load 地址后
# 间接调用 ⇒ 函数代码只有一份(在定义片段里),调用点不复制代码。
# ============================================================
REPL_CTX_MARKER: t.CDefine = 1397571916 # 宿主侧对象标记(自比对;非 ctx 头字段)
REPL_CTX_MAGIC: t.CDefine = 0x5245504C # 'REPL'(ctx 头 +0,小端字节序为 R E P L)
REPL_CTX_VERSION: t.CDefine = 2 # ctx 头 +4(1 → 2:尾部追加函数指针槽区)
REPL_SLOT_SIZE: t.CDefine = 16 # 每个会话变量占用字节数(步长)
REPL_SLOT_BASE: t.CDefine = 64 # 槽区起点 = 固定头字节数(= +40..63 保留之后)
REPL_CTX_PARAM: t.CDefine = "__repl_ctx__" # 包裹模板里 ctx 形参名(与 Repl.vp 同源)
REPL_VAR_CAP: t.CDefine = 64 # 会话变量上限(变量区 = 64 + 64*16 = 1088 字节)
# 会话函数指针槽区(M3):起点紧跟变量区,步长与变量槽一致(16B,低 8 字节存地址)
REPL_FUNC_BASE: t.CDefine = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
REPL_FUNC_SLOT_SIZE: t.CDefine = 16 # 每个会话函数占用字节数(步长)
REPL_FUNC_CAP: t.CDefine = 32 # 会话函数上限(函数区 = 32*16 = 512 字节)
# ctx 总字节数 = 64 + 64*16 + 32*16 = 1600(宿主 Repl._REPL_CTX_SIZE 必须一致)
REPL_CTX_SIZE: t.CDefine = 1600
# 会话函数的形参上限(签名 token 数组定长;超出 ⇒ 注册期明确报错)
REPL_FUNC_PARAM_MAX: t.CDefine = 8
# 片段入口函数名前缀(Repl.vp 生成 "__repl_frag_%d";用于区分"片段入口函数"与
# 被提升的会话函数本体——前者是会话变量写入的作用域,后者只读会话槽)
REPL_ENTRY_PREFIX: t.CDefine = "__repl_frag_"
# 会话模块 SHA1(M4):片段翻译器的 ModuleSha1 取此**稳定**值 ⇒ 片段内定义的类型
# 名形如 "<16位hex>.<Type>",且**跨片段逐字一致**——这是"先前片段定义的类在后续
# 片段可见/可用"的基础(类型名是跨片段查找的唯一键)。
# 派生:sha1("TransPyV-REPL-Session").hexdigest()[:16]
# 得 71f67b7cb405f0e4(一次性计算,见 repl-design.md §M4 的可复算命令)。
# 形态:16 位小写 hex 与 includes 模块 SHA1 同形 ⇒ 被既有判定
# GenApi.is_sha1_hex16 / StructGen.find_struct 的"模块段即 SHA1"分支
# 直接当作模块限定名处理(无需新增解析规则)。
# 反例(为什么不给每个片段各设一个 sha1):类型名会随片段漂移,先前片段注册的
# 结构体/方法在后续片段的 find_struct_by_module 全部落空。
# 与裸名约定的关系:`export def`(含片段入口与会话函数)走 has_export 分支取裸名
# (FuncGenDef.vp:482-485),**不受**该值影响;受影响的只有类/方法/模块级全局
# 等经 _mangle_name/_mangle_name_with_sha1 命名的符号。
REPL_SESSION_SHA1: t.CDefine = "71f67b7cb405f0e4"
# ctx 头字段偏移(与 ReplResult 字段顺序/宽度一一对应)
REPL_RES_KIND_OFF: t.CDefine = 8 # i32
REPL_RES_A_OFF: t.CDefine = 16 # u64
REPL_RES_B_OFF: t.CDefine = 24 # u64
REPL_RES_VARCOUNT_OFF: t.CDefine = 32 # i32
REPL_RES_LASTFRAG_OFF: t.CDefine = 36 # i32
# 结果 Kind(宿主按此回显)
REPL_RES_NONE: t.CDefine = 0 # 本片段无结果(如仅声明)
REPL_RES_INT: t.CDefine = 1 # 整数(读 result_a 位模式)
REPL_RES_FLOAT: t.CDefine = 2 # float64(读 result_a 的 double 位模式)
REPL_RES_STR: t.CDefine = 3 # str/bytes(读 result_a 指针 + result_b 长度)
REPL_RES_OBJ: t.CDefine = 4 # 结构体/其它指针(读 result_a 地址)
# 会话变量类型标签(ReplVarEntry.TyTag)
REPL_TY_INT: t.CDefine = 1
REPL_TY_FLOAT: t.CDefine = 2
REPL_TY_STR: t.CDefine = 3 # i8*(str/bytes)
REPL_TY_PTR: t.CDefine = 4 # 其他指针(Bits=pointee 位宽 / TyName=结构体名)
REPL_TY_STRUCT: t.CDefine = 5 # 结构体值(按声明类型内联在槽里;≤ 槽容量)
# 会话变量存放形态(ReplVarEntry.Kind)
REPL_KIND_UNTYPED: t.CDefine = 0 # 已登记槽位但尚未定型(未赋值/无注解)
REPL_KIND_SCALAR: t.CDefine = 1 # 值内联存槽(标量/指针/≤16B 结构体值)
# 会话函数签名的返回类型 token(M3;只用于 ReplFuncEntry.RetTag)
REPL_TY_VOID: t.CDefine = 0 # 无返回值(void;不作为形参 token)
# ============================================================
# ReplVarEntry - REPL 会话变量条目(跨片段存活,表本体在 PermPool)
# ============================================================
novirtual class ReplVarEntry:
"""REPL 会话变量条目:名字 + ctx 槽偏移 + 定型信息
Name/TyName 必须是 PermPool 深拷贝——宿主的 AST 名字串属于片段 pool,
行末 reset_to 后即失效,直接存指针会读到已回收内存。
"""
Name: str # 变量名(PermPool 深拷贝,NUL 结尾)
TyName: str # 结构体名(TyTag=PTR 且 pointee 为结构体 / TyTag=STRUCT);否则 None
Offset: int # ctx 内槽偏移(REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
Kind: int # REPL_KIND_*
TyTag: int # REPL_TY_*
Bits: int # 标量位宽;TyTag=PTR 时是 pointee 位宽(0=未知)
# ============================================================
# ReplFuncEntry - REPL 会话函数条目(M3;跨片段存活,表本体在 PermPool)
#
# 语义:片段 N 里 `def f(...)` 被提升为片段模块里的**顶层 export def**(裸名导出),
# 该 DLL 加载后宿主 GetProcAddress 取到地址并写入 ctx 的函数指针槽;本条目记录
# "名字 → 符号名 + 定义片段号 + 槽偏移 + 签名 token",后续片段的调用点据此
# 从槽 load 地址并间接调用(与 M2 的会话变量槽同理:单一真相 + 间接寻址)。
#
# 签名 token(ParamTags/ParamBits 每形参 1 字节、RetTag/RetBits)与来源:
# 定义片段翻译出的 IR 里 `define <ret> @f(i8* %__repl_ctx__, <P1> %p1, ...)`
# 是 ABI 的唯一真相(注解 → LLVM 类型由 codegen 解析),宿主在注册前解析该
# define 行并**只接受**可往返重建的 token 集合:
# REPL_TY_INT(bits=1/8/16/32/64) / REPL_TY_FLOAT(bits=32/64) / REPL_TY_STR(i8*)
# 其余类型(结构体值、数组、多级指针等)⇒ 注册期明确报错,绝不静默按 i32 兜底。
# ============================================================
novirtual class ReplFuncEntry:
"""REPL 会话函数条目:名字 + 导出符号名 + ctx 函数指针槽 + 签名 token"""
Name: str # 会话函数名(= 用户写的裸名;PermPool 深拷贝,NUL 结尾)
SymName: str # 片段 DLL 里的导出符号名(PermPool 深拷贝;M3 = 裸名)
FragNo: int # 定义该函数的片段号(重定义时更新为最新片段号)
SlotOff: int # ctx 内函数指针槽偏移(REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
RetBits: int # 对齐 ReplVarEntry.Bits 的口径(VOID 时为 0)
ParamCount: int # 形参数(**不含**注入的 ctx 首形参)
ParamTags: str # PermPool 字节数组(每形参 1 字节 tag,REPL_FUNC_PARAM_MAX 个)
ParamBits: str # PermPool 字节数组(每形参 1 字节 bits,REPL_FUNC_PARAM_MAX 个)
# ============================================================
# ReplCtx - REPL 会话上下文(宿主建、翻译器读)
#
# 开关语义:trans.ReplCtx != None ⇒ REPL 模式(仅 Repl.vp 翻译片段前置位,
# 翻译后清空)。Gen 侧全部拦截点都以此字段为唯一守卫——普通编译路径下该字段
# 恒为 None,逐字走原路径。
# ============================================================
novirtual class ReplCtx:
"""REPL 模式上下文(宿主持有;翻译器只读)"""
Marker: int # REPL_CTX_MARKER(防误传非 ReplCtx 对象)
Table: rawptr[ReplVarEntry] # 会话表数组(PermPool,容量 Cap)
Count: int # 已登记条目数(= 已分配槽数)
Cap: int # 容量(REPL_VAR_CAP)
CtxParam: str # 片段函数 ctx 形参名(PermPool 深拷贝)
SlotSize: int # 槽步长(REPL_SLOT_SIZE)
# 影子条目(宿主建、PermPool):仅供"裸表达式结果"做类型分类用,不占会话槽、
# 不进表(Count 不计入),Name 固定为 "<expr>" 以便错误消息可读。
Scratch: rawptr[ReplVarEntry]
# 宿主提供的永久池:会话表条目名/结构体名等**跨片段存活**的数据必须在此池
# 深拷贝(片段的 trans.Pool 行末 reset_to 即回收,直接存其指针会悬垂)。
Perm: memhub.MemBuddy*
# 会话函数表(M3;宿主写、Gen 只读):数组容量 FuncCap,已登记 FuncCount 条。
# 条目只在"定义片段 DLL 加载成功且 GetProcAddress 命中"后由宿主追加 ⇒ 片段
# 翻译期看到的表恒为"先前片段的已就绪函数"(本片段新定义的同名函数走直接调用)。
FuncTable: rawptr[ReplFuncEntry]
FuncCount: int
FuncCap: int # 容量(REPL_FUNC_CAP)
# ============================================================
# ReplResult - ctx 头(宿主按字段读;片段按 REPL_RES_*_OFF 字节偏移写)
#
# 字段顺序/宽度即 ctx 布局:Magic(+0,i32) Version(+4,i32) Kind(+8,i32)
# Reserved0(+12,i32) A(+16,i64) B(+24,i64) VarCount(+32,i32) LastFrag(+36,i32)。
# 宿主另有"同一头的浮点视图"(把 +16 的 8 字节按 double 读回,见 Repl.vp)。
# ============================================================
novirtual class ReplResult:
"""ctx 固定头(前 64 字节中的前 40 字节;+40..63 为保留)"""
Magic: int # REPL_CTX_MAGIC('REPL')
Version: int # REPL_CTX_VERSION
Kind: int # REPL_RES_*
Reserved0: int # 保留(0)
A: t.CInt64T # result_a
B: t.CInt64T # result_b
VarCount: int # 已分配槽数(宿主写,供检视)
LastFrag: int # 最近执行的片段号(宿主写,供检视)
+462 -468
View File
@@ -1,468 +1,462 @@
import string
import lib.core.CompilerFactory as CompilerFactory
import t, c
from stdint import *
import stdio
import stdlib
import memhub
import llvmlite
import lib.core.Gen.BaseGenMixin as BaseGenMixin
import lib.core.Gen.TypeGenBase as TypeGenBase
import lib.core.Gen.TypeGenMixin as TypeGenMixin
import lib.core.Gen.TypeGenCDef as TypeGenCDef
import lib.core.Gen.TypeGenCType as TypeGenCType
import lib.core.Gen.TypeGenResolve as TypeGenResolve
import lib.core.Gen.TypeGenErr as TypeGenErr
import lib.core.Gen.StructGenMixin as StructGenMixin
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Gen.ExprGenMixin as ExprGenMixin
import lib.core.Gen.ExprGenConst as ExprGenConst
import lib.core.Gen.ExprGenFstr as ExprGenFstr
import lib.core.Gen.ExprGenOps as ExprGenOps
import lib.core.Gen.ExprGenCdef as ExprGenCdef
import lib.core.Gen.ExprGenName as ExprGenName
import lib.core.Gen.ExprGenAttr as ExprGenAttr
import lib.core.Gen.ExprGenCls as ExprGenCls
import lib.core.Gen.CallGenBase as CallGenBase
import lib.core.Gen.CallGenCBuiltin as CallGenCBuiltin
import lib.core.Gen.CallGenLeafKit as CallGenLeafKit
import lib.core.Gen.CallGenCtor as CallGenCtor
import lib.core.Gen.CallGenCall as CallGenCall
import lib.core.Gen.CallGenMixin as CallGenMixin
import lib.core.Gen.CallGenDispatch as CallGenDispatch
import lib.core.Gen.CallGenLib as CallGenLib
import lib.core.Gen.CallGenVt as CallGenVt
import lib.core.Gen.CallGenVa as CallGenVa
import lib.core.Gen.CallGenProp as CallGenProp
import lib.core.Gen.CallGenFd as CallGenFd
import lib.core.Gen.FuncGenBase as FuncGenBase
import lib.core.Gen.FuncGenDef as FuncGenDef
import lib.core.Gen.FuncGenMixin as FuncGenMixin
import lib.core.Gen.ClassGenBase as ClassGenBase
import lib.core.Gen.ClassGenMixin as ClassGenMixin
import lib.core.Gen.ClassGenEnum as ClassGenEnum
import lib.core.Gen.ClassGenGeneric as ClassGenGeneric
import lib.core.Gen.ClassGenVTable as ClassGenVTable
import lib.core.Gen.ClassGenDef as ClassGenDef
import lib.core.Gen.ImportsGenMixin as ImportsGenMixin
import lib.core.Gen.StmtGenCifMixin as StmtGenCifMixin
import lib.core.Gen.StmtGenDeclMixin as StmtGenDeclMixin
import lib.core.Gen.StmtGenMixin as StmtGenMixin
import lib.core.Gen.MatchGenMixin as MatchGenMixin
import lib.core.Gen.AssignGenMixin as AssignGenMixin
import lib.core.Gen.AsyncGenMixin as AsyncGenMixin
import lib.core.Gen.VarGenMixin as VarGenMixin
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
# ============================================================
# Generator - 生成器(双向引用组合架构)
#
# 不再 46 路多继承 Mixin;改为组合:Generator 持所有 GenMixin
# 子对象指针(永久池分配,全生命周期),每个 Mixin 持 Gen 回指。
# 跨子对象访问 = self.Gen.<Mixin 字段>.<字段/方法>(链式有风险,
# 统一用中间变量取子对象再访问)。高亮可推导(显式类型注解)。
# ============================================================
@t.NoVTable
class Generator:
"""生成器:持所有 GenMixin 子对象指针(双向引用组合)"""
# ---- 子对象指针(46 个,永久池) ----
BaseGen: BaseGenMixin.BaseGenMixin | t.CPtr
TypeGenBase: TypeGenBase.TypeGenBase | t.CPtr
TypeGen: TypeGenMixin.TypeGenMixin | t.CPtr
TypeGenCDef: TypeGenCDef.TypeGenCDef | t.CPtr
TypeGenCType: TypeGenCType.TypeGenCType | t.CPtr
TypeGenResolve: TypeGenResolve.TypeGenResolve | t.CPtr
TypeGenErr: TypeGenErr.TypeGenErr | t.CPtr
StructGen: StructGenMixin.StructGenMixin | t.CPtr
ExprGenBase: ExprGenBase.ExprGenBase | t.CPtr
ExprGen: ExprGenMixin.ExprGenMixin | t.CPtr
ExprGenConst: ExprGenConst.ExprGenConst | t.CPtr
ExprGenFstr: ExprGenFstr.ExprGenFstr | t.CPtr
ExprGenOps: ExprGenOps.ExprGenOps | t.CPtr
ExprGenCdef: ExprGenCdef.ExprGenCdef | t.CPtr
ExprGenName: ExprGenName.ExprGenName | t.CPtr
ExprGenAttr: ExprGenAttr.ExprGenAttr | t.CPtr
ExprGenCls: ExprGenCls.ExprGenCls | t.CPtr
CallGenBase: CallGenBase.CallGenBase | t.CPtr
CallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin | t.CPtr
CallGenLeafKit: CallGenLeafKit.CallGenLeafKit | t.CPtr
CallGenCtor: CallGenCtor.CallGenCtor | t.CPtr
CallGenCall: CallGenCall.CallGenCall | t.CPtr
CallGen: CallGenMixin.CallGenMixin | t.CPtr
CallGenDispatch: CallGenDispatch.CallGenDispatch | t.CPtr
CallGenLib: CallGenLib.CallGenLib | t.CPtr
CallGenVt: CallGenVt.CallGenVt | t.CPtr
CallGenVa: CallGenVa.CallGenVa | t.CPtr
CallGenProp: CallGenProp.CallGenProp | t.CPtr
CallGenFd: CallGenFd.CallGenFd | t.CPtr
FuncGenBase: FuncGenBase.FuncGenBase | t.CPtr
FuncGenDef: FuncGenDef.FuncGenDef | t.CPtr
FuncGen: FuncGenMixin.FuncGenMixin | t.CPtr
ClassGenBase: ClassGenBase.ClassGenBase | t.CPtr
ClassGen: ClassGenMixin.ClassGenMixin | t.CPtr
ClassGenEnum: ClassGenEnum.ClassGenEnum | t.CPtr
ClassGenGeneric: ClassGenGeneric.ClassGenGeneric | t.CPtr
ClassGenVTable: ClassGenVTable.ClassGenVTable | t.CPtr
ClassGenDef: ClassGenDef.ClassGenDef | t.CPtr
ImportsGen: ImportsGenMixin.ImportsGenMixin | t.CPtr
StmtGenCif: StmtGenCifMixin.StmtGenCifMixin | t.CPtr
StmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin | t.CPtr
StmtGen: StmtGenMixin.StmtGenMixin | t.CPtr
MatchGen: MatchGenMixin.MatchGenMixin | t.CPtr
AssignGen: AssignGenMixin.AssignGenMixin | t.CPtr
AsyncGen: AsyncGenMixin.AsyncGenMixin | t.CPtr
VarGen: VarGenMixin.VarGenMixin | t.CPtr
TranslatorGen: TranslatorGenMixin.TranslatorGenMixin | t.CPtr
def __init__(self, pool: memhub.MemBuddy | t.CPtr):
if self is None:
return
# ---- alloc 46 个子对象 + 回指(永久池,fail-fast) ----
SubBaseGen: BaseGenMixin.BaseGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
if SubBaseGen is None:
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
SubBaseGen.Gen = self
self.BaseGen = SubBaseGen
SubTypeGenBase: TypeGenBase.TypeGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenBase.TypeGenBase.__sizeof__())
if SubTypeGenBase is None:
stdio.printf("FAIL: TypeGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
SubTypeGenBase.Gen = self
self.TypeGenBase = SubTypeGenBase
SubTypeGen: TypeGenMixin.TypeGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
if SubTypeGen is None:
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
SubTypeGen.Gen = self
self.TypeGen = SubTypeGen
SubTypeGenCDef: TypeGenCDef.TypeGenCDef | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
if SubTypeGenCDef is None:
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
SubTypeGenCDef.Gen = self
self.TypeGenCDef = SubTypeGenCDef
SubTypeGenCType: TypeGenCType.TypeGenCType | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenCType.TypeGenCType.__sizeof__())
if SubTypeGenCType is None:
stdio.printf("FAIL: TypeGenCType alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
SubTypeGenCType.Gen = self
self.TypeGenCType = SubTypeGenCType
SubTypeGenResolve: TypeGenResolve.TypeGenResolve | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
if SubTypeGenResolve is None:
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
SubTypeGenResolve.Gen = self
self.TypeGenResolve = SubTypeGenResolve
SubTypeGenErr: TypeGenErr.TypeGenErr | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenErr.TypeGenErr.__sizeof__())
if SubTypeGenErr is None:
stdio.printf("FAIL: TypeGenErr alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
SubTypeGenErr.Gen = self
self.TypeGenErr = SubTypeGenErr
SubStructGen: StructGenMixin.StructGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StructGenMixin.StructGenMixin.__sizeof__())
if SubStructGen is None:
stdio.printf("FAIL: StructGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
SubStructGen.Gen = self
self.StructGen = SubStructGen
SubExprGenBase: ExprGenBase.ExprGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenBase.ExprGenBase.__sizeof__())
if SubExprGenBase is None:
stdio.printf("FAIL: ExprGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
SubExprGenBase.Gen = self
self.ExprGenBase = SubExprGenBase
SubExprGen: ExprGenMixin.ExprGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
if SubExprGen is None:
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
SubExprGen.Gen = self
self.ExprGen = SubExprGen
SubExprGenConst: ExprGenConst.ExprGenConst | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenConst.ExprGenConst.__sizeof__())
if SubExprGenConst is None:
stdio.printf("FAIL: ExprGenConst alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
SubExprGenConst.Gen = self
self.ExprGenConst = SubExprGenConst
SubExprGenFstr: ExprGenFstr.ExprGenFstr | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
if SubExprGenFstr is None:
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
SubExprGenFstr.Gen = self
self.ExprGenFstr = SubExprGenFstr
SubExprGenOps: ExprGenOps.ExprGenOps | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenOps.ExprGenOps.__sizeof__())
if SubExprGenOps is None:
stdio.printf("FAIL: ExprGenOps alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
SubExprGenOps.Gen = self
self.ExprGenOps = SubExprGenOps
SubExprGenCdef: ExprGenCdef.ExprGenCdef | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
if SubExprGenCdef is None:
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
SubExprGenCdef.Gen = self
self.ExprGenCdef = SubExprGenCdef
SubExprGenName: ExprGenName.ExprGenName | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenName.ExprGenName.__sizeof__())
if SubExprGenName is None:
stdio.printf("FAIL: ExprGenName alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
SubExprGenName.Gen = self
self.ExprGenName = SubExprGenName
SubExprGenAttr: ExprGenAttr.ExprGenAttr | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
if SubExprGenAttr is None:
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
SubExprGenAttr.Gen = self
self.ExprGenAttr = SubExprGenAttr
SubExprGenCls: ExprGenCls.ExprGenCls | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenCls.ExprGenCls.__sizeof__())
if SubExprGenCls is None:
stdio.printf("FAIL: ExprGenCls alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
SubExprGenCls.Gen = self
self.ExprGenCls = SubExprGenCls
SubCallGenBase: CallGenBase.CallGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenBase.CallGenBase.__sizeof__())
if SubCallGenBase is None:
stdio.printf("FAIL: CallGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
SubCallGenBase.Gen = self
self.CallGenBase = SubCallGenBase
SubCallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
if SubCallGenCBuiltin is None:
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
SubCallGenCBuiltin.Gen = self
self.CallGenCBuiltin = SubCallGenCBuiltin
SubCallGenLeafKit: CallGenLeafKit.CallGenLeafKit | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
if SubCallGenLeafKit is None:
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
SubCallGenLeafKit.Gen = self
self.CallGenLeafKit = SubCallGenLeafKit
SubCallGenCtor: CallGenCtor.CallGenCtor | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenCtor.CallGenCtor.__sizeof__())
if SubCallGenCtor is None:
stdio.printf("FAIL: CallGenCtor alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
SubCallGenCtor.Gen = self
self.CallGenCtor = SubCallGenCtor
SubCallGenCall: CallGenCall.CallGenCall | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenCall.CallGenCall.__sizeof__())
if SubCallGenCall is None:
stdio.printf("FAIL: CallGenCall alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
SubCallGenCall.Gen = self
self.CallGenCall = SubCallGenCall
SubCallGen: CallGenMixin.CallGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenMixin.CallGenMixin.__sizeof__())
if SubCallGen is None:
stdio.printf("FAIL: CallGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
SubCallGen.Gen = self
self.CallGen = SubCallGen
SubCallGenDispatch: CallGenDispatch.CallGenDispatch | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
if SubCallGenDispatch is None:
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
SubCallGenDispatch.Gen = self
self.CallGenDispatch = SubCallGenDispatch
SubCallGenLib: CallGenLib.CallGenLib | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenLib.CallGenLib.__sizeof__())
if SubCallGenLib is None:
stdio.printf("FAIL: CallGenLib alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
SubCallGenLib.Gen = self
self.CallGenLib = SubCallGenLib
SubCallGenVt: CallGenVt.CallGenVt | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenVt.CallGenVt.__sizeof__())
if SubCallGenVt is None:
stdio.printf("FAIL: CallGenVt alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
SubCallGenVt.Gen = self
self.CallGenVt = SubCallGenVt
SubCallGenVa: CallGenVa.CallGenVa | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenVa.CallGenVa.__sizeof__())
if SubCallGenVa is None:
stdio.printf("FAIL: CallGenVa alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
SubCallGenVa.Gen = self
self.CallGenVa = SubCallGenVa
SubCallGenProp: CallGenProp.CallGenProp | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenProp.CallGenProp.__sizeof__())
if SubCallGenProp is None:
stdio.printf("FAIL: CallGenProp alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
SubCallGenProp.Gen = self
self.CallGenProp = SubCallGenProp
SubCallGenFd: CallGenFd.CallGenFd | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenFd.CallGenFd.__sizeof__())
if SubCallGenFd is None:
stdio.printf("FAIL: CallGenFd alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
SubCallGenFd.Gen = self
self.CallGenFd = SubCallGenFd
SubFuncGenBase: FuncGenBase.FuncGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(FuncGenBase.FuncGenBase.__sizeof__())
if SubFuncGenBase is None:
stdio.printf("FAIL: FuncGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
SubFuncGenBase.Gen = self
self.FuncGenBase = SubFuncGenBase
SubFuncGenDef: FuncGenDef.FuncGenDef | t.CPtr = CompilerFactory.get_perm_pool().alloc(FuncGenDef.FuncGenDef.__sizeof__())
if SubFuncGenDef is None:
stdio.printf("FAIL: FuncGenDef alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
SubFuncGenDef.Gen = self
self.FuncGenDef = SubFuncGenDef
SubFuncGen: FuncGenMixin.FuncGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
if SubFuncGen is None:
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
SubFuncGen.Gen = self
self.FuncGen = SubFuncGen
SubClassGenBase: ClassGenBase.ClassGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenBase.ClassGenBase.__sizeof__())
if SubClassGenBase is None:
stdio.printf("FAIL: ClassGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
SubClassGenBase.Gen = self
self.ClassGenBase = SubClassGenBase
SubClassGen: ClassGenMixin.ClassGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
if SubClassGen is None:
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
SubClassGen.Gen = self
self.ClassGen = SubClassGen
SubClassGenEnum: ClassGenEnum.ClassGenEnum | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
if SubClassGenEnum is None:
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
SubClassGenEnum.Gen = self
self.ClassGenEnum = SubClassGenEnum
SubClassGenGeneric: ClassGenGeneric.ClassGenGeneric | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
if SubClassGenGeneric is None:
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
SubClassGenGeneric.Gen = self
self.ClassGenGeneric = SubClassGenGeneric
SubClassGenVTable: ClassGenVTable.ClassGenVTable | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
if SubClassGenVTable is None:
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
SubClassGenVTable.Gen = self
self.ClassGenVTable = SubClassGenVTable
SubClassGenDef: ClassGenDef.ClassGenDef | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenDef.ClassGenDef.__sizeof__())
if SubClassGenDef is None:
stdio.printf("FAIL: ClassGenDef alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
SubClassGenDef.Gen = self
self.ClassGenDef = SubClassGenDef
SubImportsGen: ImportsGenMixin.ImportsGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
if SubImportsGen is None:
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
SubImportsGen.Gen = self
self.ImportsGen = SubImportsGen
SubStmtGenCif: StmtGenCifMixin.StmtGenCifMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
if SubStmtGenCif is None:
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
SubStmtGenCif.Gen = self
self.StmtGenCif = SubStmtGenCif
SubStmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
if SubStmtGenDecl is None:
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
SubStmtGenDecl.Gen = self
self.StmtGenDecl = SubStmtGenDecl
SubStmtGen: StmtGenMixin.StmtGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
if SubStmtGen is None:
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
SubStmtGen.Gen = self
self.StmtGen = SubStmtGen
SubMatchGen: MatchGenMixin.MatchGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
if SubMatchGen is None:
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
SubMatchGen.Gen = self
self.MatchGen = SubMatchGen
SubAssignGen: AssignGenMixin.AssignGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
if SubAssignGen is None:
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
SubAssignGen.Gen = self
self.AssignGen = SubAssignGen
SubVarGen: VarGenMixin.VarGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(VarGenMixin.VarGenMixin.__sizeof__())
if SubVarGen is None:
stdio.printf("FAIL: VarGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
SubVarGen.Gen = self
self.VarGen = SubVarGen
SubTranslatorGen: TranslatorGenMixin.TranslatorGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
if SubTranslatorGen is None:
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
SubTranslatorGen.Gen = self
self.TranslatorGen = SubTranslatorGen
SubAsyncGen: AsyncGenMixin.AsyncGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
if SubAsyncGen is None:
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubAsyncGen, 0, AsyncGenMixin.AsyncGenMixin.__sizeof__())
SubAsyncGen.Gen = self
self.AsyncGen = SubAsyncGen
# ---- 初始化各子对象字段(Init* 方法内联调用) ----
SubBaseGen.InitBaseGen(pool)
SubTypeGenBase.InitTypeGen()
SubCallGenBase.InitCallGen()
SubStructGen.InitStructGen()
SubClassGenBase.InitClassGen()
SubExprGenBase.InitExprGen()
SubImportsGen.InitImportsGen()
SubStmtGenCif.InitStmtGen()
SubAssignGen.InitAssignGen()
# ============================================================
# 全局生成器单例(组合架构兼容层)
# ============================================================
_g_gen: Generator | t.CPtr = None
import string
import lib.core.CompilerFactory as CompilerFactory
import t, c
from stdint import *
import stdio
import stdlib
import memhub
import llvmlite
import lib.core.Gen.BaseGenMixin as BaseGenMixin
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
import lib.core.Gen.Type.TypeGenCDef as TypeGenCDef
import lib.core.Gen.Type.TypeGenCType as TypeGenCType
import lib.core.Gen.Type.TypeGenResolve as TypeGenResolve
import lib.core.Gen.Type.TypeGenErr as TypeGenErr
import lib.core.Gen.StructGenMixin as StructGenMixin
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
import lib.core.Gen.Expr.ExprGenMixin as ExprGenMixin
import lib.core.Gen.Expr.ExprGenConst as ExprGenConst
import lib.core.Gen.Expr.ExprGenFstr as ExprGenFstr
import lib.core.Gen.Expr.ExprGenOps as ExprGenOps
import lib.core.Gen.Expr.ExprGenCdef as ExprGenCdef
import lib.core.Gen.Expr.ExprGenName as ExprGenName
import lib.core.Gen.Expr.ExprGenAttr as ExprGenAttr
import lib.core.Gen.Expr.ExprGenCls as ExprGenCls
import lib.core.Gen.Call.CallGenBase as CallGenBase
import lib.core.Gen.Call.CallGenCBuiltin as CallGenCBuiltin
import lib.core.Gen.Call.CallGenLeafKit as CallGenLeafKit
import lib.core.Gen.Call.CallGenCtor as CallGenCtor
import lib.core.Gen.Call.CallGenCall as CallGenCall
import lib.core.Gen.Call.CallGenMixin as CallGenMixin
import lib.core.Gen.Call.CallGenDispatch as CallGenDispatch
import lib.core.Gen.Call.CallGenLib as CallGenLib
import lib.core.Gen.Call.CallGenVt as CallGenVt
import lib.core.Gen.Call.CallGenVa as CallGenVa
import lib.core.Gen.Call.CallGenProp as CallGenProp
import lib.core.Gen.Call.CallGenFd as CallGenFd
import lib.core.Gen.Func.FuncGenBase as FuncGenBase
import lib.core.Gen.Func.FuncGenDef as FuncGenDef
import lib.core.Gen.Func.FuncGenMixin as FuncGenMixin
import lib.core.Gen.Class.ClassGenBase as ClassGenBase
import lib.core.Gen.Class.ClassGenMixin as ClassGenMixin
import lib.core.Gen.Class.ClassGenEnum as ClassGenEnum
import lib.core.Gen.Class.ClassGenGeneric as ClassGenGeneric
import lib.core.Gen.Class.ClassGenVTable as ClassGenVTable
import lib.core.Gen.Class.ClassGenDef as ClassGenDef
import lib.core.Gen.ImportsGenMixin as ImportsGenMixin
import lib.core.Gen.Stmt.StmtGenCifMixin as StmtGenCifMixin
import lib.core.Gen.Stmt.StmtGenDeclMixin as StmtGenDeclMixin
import lib.core.Gen.Stmt.StmtGenMixin as StmtGenMixin
import lib.core.Gen.MatchGenMixin as MatchGenMixin
import lib.core.Gen.AssignGenMixin as AssignGenMixin
import lib.core.Gen.AsyncGenMixin as AsyncGenMixin
import lib.core.Gen.VarGenMixin as VarGenMixin
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
# ============================================================
# Generator - 生成器(双向引用组合架构)
# ============================================================
novirtual class Generator:
"""生成器:持所有 GenMixin 子对象指针(双向引用组合)"""
# ---- 子对象指针(46 个,永久池) ----
BaseGen: rawptr[BaseGenMixin.BaseGenMixin]
TypeGenBase: rawptr[TypeGenBase.TypeGenBase]
TypeGen: rawptr[TypeGenMixin.TypeGenMixin]
TypeGenCDef: rawptr[TypeGenCDef.TypeGenCDef]
TypeGenCType: rawptr[TypeGenCType.TypeGenCType]
TypeGenResolve: rawptr[TypeGenResolve.TypeGenResolve]
TypeGenErr: rawptr[TypeGenErr.TypeGenErr]
StructGen: rawptr[StructGenMixin.StructGenMixin]
ExprGenBase: rawptr[ExprGenBase.ExprGenBase]
ExprGen: rawptr[ExprGenMixin.ExprGenMixin]
ExprGenConst: rawptr[ExprGenConst.ExprGenConst]
ExprGenFstr: rawptr[ExprGenFstr.ExprGenFstr]
ExprGenOps: rawptr[ExprGenOps.ExprGenOps]
ExprGenCdef: rawptr[ExprGenCdef.ExprGenCdef]
ExprGenName: rawptr[ExprGenName.ExprGenName]
ExprGenAttr: rawptr[ExprGenAttr.ExprGenAttr]
ExprGenCls: rawptr[ExprGenCls.ExprGenCls]
CallGenBase: rawptr[CallGenBase.CallGenBase]
CallGenCBuiltin: rawptr[CallGenCBuiltin.CallGenCBuiltin]
CallGenLeafKit: rawptr[CallGenLeafKit.CallGenLeafKit]
CallGenCtor: rawptr[CallGenCtor.CallGenCtor]
CallGenCall: rawptr[CallGenCall.CallGenCall]
CallGen: rawptr[CallGenMixin.CallGenMixin]
CallGenDispatch: rawptr[CallGenDispatch.CallGenDispatch]
CallGenLib: rawptr[CallGenLib.CallGenLib]
CallGenVt: rawptr[CallGenVt.CallGenVt]
CallGenVa: rawptr[CallGenVa.CallGenVa]
CallGenProp: rawptr[CallGenProp.CallGenProp]
CallGenFd: rawptr[CallGenFd.CallGenFd]
FuncGenBase: rawptr[FuncGenBase.FuncGenBase]
FuncGenDef: rawptr[FuncGenDef.FuncGenDef]
FuncGen: rawptr[FuncGenMixin.FuncGenMixin]
ClassGenBase: rawptr[ClassGenBase.ClassGenBase]
ClassGen: rawptr[ClassGenMixin.ClassGenMixin]
ClassGenEnum: rawptr[ClassGenEnum.ClassGenEnum]
ClassGenGeneric: rawptr[ClassGenGeneric.ClassGenGeneric]
ClassGenVTable: rawptr[ClassGenVTable.ClassGenVTable]
ClassGenDef: rawptr[ClassGenDef.ClassGenDef]
ImportsGen: rawptr[ImportsGenMixin.ImportsGenMixin]
StmtGenCif: rawptr[StmtGenCifMixin.StmtGenCifMixin]
StmtGenDecl: rawptr[StmtGenDeclMixin.StmtGenDeclMixin]
StmtGen: rawptr[StmtGenMixin.StmtGenMixin]
MatchGen: rawptr[MatchGenMixin.MatchGenMixin]
AssignGen: rawptr[AssignGenMixin.AssignGenMixin]
AsyncGen: rawptr[AsyncGenMixin.AsyncGenMixin]
VarGen: rawptr[VarGenMixin.VarGenMixin]
TranslatorGen: rawptr[TranslatorGenMixin.TranslatorGenMixin]
def __init__(self, pool: memhub.MemBuddy*):
if self is None:
return
# ---- alloc 46 个子对象 + 回指(永久池,fail-fast) ----
SubBaseGen: rawptr[BaseGenMixin.BaseGenMixin] = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
if SubBaseGen is None:
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
SubBaseGen.Gen = self
self.BaseGen = SubBaseGen
SubTypeGenBase: rawptr[TypeGenBase.TypeGenBase] = CompilerFactory.factory.PermPool.alloc(TypeGenBase.TypeGenBase.__sizeof__())
if SubTypeGenBase is None:
stdio.printf("FAIL: TypeGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
SubTypeGenBase.Gen = self
self.TypeGenBase = SubTypeGenBase
SubTypeGen: rawptr[TypeGenMixin.TypeGenMixin] = CompilerFactory.factory.PermPool.alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
if SubTypeGen is None:
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
SubTypeGen.Gen = self
self.TypeGen = SubTypeGen
SubTypeGenCDef: rawptr[TypeGenCDef.TypeGenCDef] = CompilerFactory.factory.PermPool.alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
if SubTypeGenCDef is None:
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
SubTypeGenCDef.Gen = self
self.TypeGenCDef = SubTypeGenCDef
SubTypeGenCType: rawptr[TypeGenCType.TypeGenCType] = CompilerFactory.factory.PermPool.alloc(TypeGenCType.TypeGenCType.__sizeof__())
if SubTypeGenCType is None:
stdio.printf("FAIL: TypeGenCType alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
SubTypeGenCType.Gen = self
self.TypeGenCType = SubTypeGenCType
SubTypeGenResolve: rawptr[TypeGenResolve.TypeGenResolve] = CompilerFactory.factory.PermPool.alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
if SubTypeGenResolve is None:
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
SubTypeGenResolve.Gen = self
self.TypeGenResolve = SubTypeGenResolve
SubTypeGenErr: rawptr[TypeGenErr.TypeGenErr] = CompilerFactory.factory.PermPool.alloc(TypeGenErr.TypeGenErr.__sizeof__())
if SubTypeGenErr is None:
stdio.printf("FAIL: TypeGenErr alloc failed\n")
stdlib.exit(1)
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
SubTypeGenErr.Gen = self
self.TypeGenErr = SubTypeGenErr
SubStructGen: rawptr[StructGenMixin.StructGenMixin] = CompilerFactory.factory.PermPool.alloc(StructGenMixin.StructGenMixin.__sizeof__())
if SubStructGen is None:
stdio.printf("FAIL: StructGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
SubStructGen.Gen = self
self.StructGen = SubStructGen
SubExprGenBase: rawptr[ExprGenBase.ExprGenBase] = CompilerFactory.factory.PermPool.alloc(ExprGenBase.ExprGenBase.__sizeof__())
if SubExprGenBase is None:
stdio.printf("FAIL: ExprGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
SubExprGenBase.Gen = self
self.ExprGenBase = SubExprGenBase
SubExprGen: rawptr[ExprGenMixin.ExprGenMixin] = CompilerFactory.factory.PermPool.alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
if SubExprGen is None:
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
SubExprGen.Gen = self
self.ExprGen = SubExprGen
SubExprGenConst: rawptr[ExprGenConst.ExprGenConst] = CompilerFactory.factory.PermPool.alloc(ExprGenConst.ExprGenConst.__sizeof__())
if SubExprGenConst is None:
stdio.printf("FAIL: ExprGenConst alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
SubExprGenConst.Gen = self
self.ExprGenConst = SubExprGenConst
SubExprGenFstr: rawptr[ExprGenFstr.ExprGenFstr] = CompilerFactory.factory.PermPool.alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
if SubExprGenFstr is None:
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
SubExprGenFstr.Gen = self
self.ExprGenFstr = SubExprGenFstr
SubExprGenOps: rawptr[ExprGenOps.ExprGenOps] = CompilerFactory.factory.PermPool.alloc(ExprGenOps.ExprGenOps.__sizeof__())
if SubExprGenOps is None:
stdio.printf("FAIL: ExprGenOps alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
SubExprGenOps.Gen = self
self.ExprGenOps = SubExprGenOps
SubExprGenCdef: rawptr[ExprGenCdef.ExprGenCdef] = CompilerFactory.factory.PermPool.alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
if SubExprGenCdef is None:
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
SubExprGenCdef.Gen = self
self.ExprGenCdef = SubExprGenCdef
SubExprGenName: rawptr[ExprGenName.ExprGenName] = CompilerFactory.factory.PermPool.alloc(ExprGenName.ExprGenName.__sizeof__())
if SubExprGenName is None:
stdio.printf("FAIL: ExprGenName alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
SubExprGenName.Gen = self
self.ExprGenName = SubExprGenName
SubExprGenAttr: rawptr[ExprGenAttr.ExprGenAttr] = CompilerFactory.factory.PermPool.alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
if SubExprGenAttr is None:
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
SubExprGenAttr.Gen = self
self.ExprGenAttr = SubExprGenAttr
SubExprGenCls: rawptr[ExprGenCls.ExprGenCls] = CompilerFactory.factory.PermPool.alloc(ExprGenCls.ExprGenCls.__sizeof__())
if SubExprGenCls is None:
stdio.printf("FAIL: ExprGenCls alloc failed\n")
stdlib.exit(1)
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
SubExprGenCls.Gen = self
self.ExprGenCls = SubExprGenCls
SubCallGenBase: rawptr[CallGenBase.CallGenBase] = CompilerFactory.factory.PermPool.alloc(CallGenBase.CallGenBase.__sizeof__())
if SubCallGenBase is None:
stdio.printf("FAIL: CallGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
SubCallGenBase.Gen = self
self.CallGenBase = SubCallGenBase
SubCallGenCBuiltin: rawptr[CallGenCBuiltin.CallGenCBuiltin] = CompilerFactory.factory.PermPool.alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
if SubCallGenCBuiltin is None:
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
SubCallGenCBuiltin.Gen = self
self.CallGenCBuiltin = SubCallGenCBuiltin
SubCallGenLeafKit: rawptr[CallGenLeafKit.CallGenLeafKit] = CompilerFactory.factory.PermPool.alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
if SubCallGenLeafKit is None:
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
SubCallGenLeafKit.Gen = self
self.CallGenLeafKit = SubCallGenLeafKit
SubCallGenCtor: rawptr[CallGenCtor.CallGenCtor] = CompilerFactory.factory.PermPool.alloc(CallGenCtor.CallGenCtor.__sizeof__())
if SubCallGenCtor is None:
stdio.printf("FAIL: CallGenCtor alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
SubCallGenCtor.Gen = self
self.CallGenCtor = SubCallGenCtor
SubCallGenCall: rawptr[CallGenCall.CallGenCall] = CompilerFactory.factory.PermPool.alloc(CallGenCall.CallGenCall.__sizeof__())
if SubCallGenCall is None:
stdio.printf("FAIL: CallGenCall alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
SubCallGenCall.Gen = self
self.CallGenCall = SubCallGenCall
SubCallGen: rawptr[CallGenMixin.CallGenMixin] = CompilerFactory.factory.PermPool.alloc(CallGenMixin.CallGenMixin.__sizeof__())
if SubCallGen is None:
stdio.printf("FAIL: CallGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
SubCallGen.Gen = self
self.CallGen = SubCallGen
SubCallGenDispatch: rawptr[CallGenDispatch.CallGenDispatch] = CompilerFactory.factory.PermPool.alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
if SubCallGenDispatch is None:
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
SubCallGenDispatch.Gen = self
self.CallGenDispatch = SubCallGenDispatch
SubCallGenLib: rawptr[CallGenLib.CallGenLib] = CompilerFactory.factory.PermPool.alloc(CallGenLib.CallGenLib.__sizeof__())
if SubCallGenLib is None:
stdio.printf("FAIL: CallGenLib alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
SubCallGenLib.Gen = self
self.CallGenLib = SubCallGenLib
SubCallGenVt: rawptr[CallGenVt.CallGenVt] = CompilerFactory.factory.PermPool.alloc(CallGenVt.CallGenVt.__sizeof__())
if SubCallGenVt is None:
stdio.printf("FAIL: CallGenVt alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
SubCallGenVt.Gen = self
self.CallGenVt = SubCallGenVt
SubCallGenVa: rawptr[CallGenVa.CallGenVa] = CompilerFactory.factory.PermPool.alloc(CallGenVa.CallGenVa.__sizeof__())
if SubCallGenVa is None:
stdio.printf("FAIL: CallGenVa alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
SubCallGenVa.Gen = self
self.CallGenVa = SubCallGenVa
SubCallGenProp: rawptr[CallGenProp.CallGenProp] = CompilerFactory.factory.PermPool.alloc(CallGenProp.CallGenProp.__sizeof__())
if SubCallGenProp is None:
stdio.printf("FAIL: CallGenProp alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
SubCallGenProp.Gen = self
self.CallGenProp = SubCallGenProp
SubCallGenFd: rawptr[CallGenFd.CallGenFd] = CompilerFactory.factory.PermPool.alloc(CallGenFd.CallGenFd.__sizeof__())
if SubCallGenFd is None:
stdio.printf("FAIL: CallGenFd alloc failed\n")
stdlib.exit(1)
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
SubCallGenFd.Gen = self
self.CallGenFd = SubCallGenFd
SubFuncGenBase: rawptr[FuncGenBase.FuncGenBase] = CompilerFactory.factory.PermPool.alloc(FuncGenBase.FuncGenBase.__sizeof__())
if SubFuncGenBase is None:
stdio.printf("FAIL: FuncGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
SubFuncGenBase.Gen = self
self.FuncGenBase = SubFuncGenBase
SubFuncGenDef: rawptr[FuncGenDef.FuncGenDef] = CompilerFactory.factory.PermPool.alloc(FuncGenDef.FuncGenDef.__sizeof__())
if SubFuncGenDef is None:
stdio.printf("FAIL: FuncGenDef alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
SubFuncGenDef.Gen = self
self.FuncGenDef = SubFuncGenDef
SubFuncGen: rawptr[FuncGenMixin.FuncGenMixin] = CompilerFactory.factory.PermPool.alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
if SubFuncGen is None:
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
SubFuncGen.Gen = self
self.FuncGen = SubFuncGen
SubClassGenBase: rawptr[ClassGenBase.ClassGenBase] = CompilerFactory.factory.PermPool.alloc(ClassGenBase.ClassGenBase.__sizeof__())
if SubClassGenBase is None:
stdio.printf("FAIL: ClassGenBase alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
SubClassGenBase.Gen = self
self.ClassGenBase = SubClassGenBase
SubClassGen: rawptr[ClassGenMixin.ClassGenMixin] = CompilerFactory.factory.PermPool.alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
if SubClassGen is None:
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
SubClassGen.Gen = self
self.ClassGen = SubClassGen
SubClassGenEnum: rawptr[ClassGenEnum.ClassGenEnum] = CompilerFactory.factory.PermPool.alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
if SubClassGenEnum is None:
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
SubClassGenEnum.Gen = self
self.ClassGenEnum = SubClassGenEnum
SubClassGenGeneric: rawptr[ClassGenGeneric.ClassGenGeneric] = CompilerFactory.factory.PermPool.alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
if SubClassGenGeneric is None:
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
SubClassGenGeneric.Gen = self
self.ClassGenGeneric = SubClassGenGeneric
SubClassGenVTable: rawptr[ClassGenVTable.ClassGenVTable] = CompilerFactory.factory.PermPool.alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
if SubClassGenVTable is None:
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
SubClassGenVTable.Gen = self
self.ClassGenVTable = SubClassGenVTable
SubClassGenDef: rawptr[ClassGenDef.ClassGenDef] = CompilerFactory.factory.PermPool.alloc(ClassGenDef.ClassGenDef.__sizeof__())
if SubClassGenDef is None:
stdio.printf("FAIL: ClassGenDef alloc failed\n")
stdlib.exit(1)
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
SubClassGenDef.Gen = self
self.ClassGenDef = SubClassGenDef
SubImportsGen: rawptr[ImportsGenMixin.ImportsGenMixin] = CompilerFactory.factory.PermPool.alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
if SubImportsGen is None:
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
SubImportsGen.Gen = self
self.ImportsGen = SubImportsGen
SubStmtGenCif: rawptr[StmtGenCifMixin.StmtGenCifMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
if SubStmtGenCif is None:
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
SubStmtGenCif.Gen = self
self.StmtGenCif = SubStmtGenCif
SubStmtGenDecl: rawptr[StmtGenDeclMixin.StmtGenDeclMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
if SubStmtGenDecl is None:
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
SubStmtGenDecl.Gen = self
self.StmtGenDecl = SubStmtGenDecl
SubStmtGen: rawptr[StmtGenMixin.StmtGenMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
if SubStmtGen is None:
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
SubStmtGen.Gen = self
self.StmtGen = SubStmtGen
SubMatchGen: rawptr[MatchGenMixin.MatchGenMixin] = CompilerFactory.factory.PermPool.alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
if SubMatchGen is None:
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
SubMatchGen.Gen = self
self.MatchGen = SubMatchGen
SubAssignGen: rawptr[AssignGenMixin.AssignGenMixin] = CompilerFactory.factory.PermPool.alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
if SubAssignGen is None:
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
SubAssignGen.Gen = self
self.AssignGen = SubAssignGen
SubVarGen: rawptr[VarGenMixin.VarGenMixin] = CompilerFactory.factory.PermPool.alloc(VarGenMixin.VarGenMixin.__sizeof__())
if SubVarGen is None:
stdio.printf("FAIL: VarGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
SubVarGen.Gen = self
self.VarGen = SubVarGen
SubTranslatorGen: rawptr[TranslatorGenMixin.TranslatorGenMixin] = CompilerFactory.factory.PermPool.alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
if SubTranslatorGen is None:
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
SubTranslatorGen.Gen = self
self.TranslatorGen = SubTranslatorGen
SubAsyncGen: rawptr[AsyncGenMixin.AsyncGenMixin] = CompilerFactory.factory.PermPool.alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
if SubAsyncGen is None:
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
stdlib.exit(1)
string.memset(SubAsyncGen, 0, AsyncGenMixin.AsyncGenMixin.__sizeof__())
SubAsyncGen.Gen = self
self.AsyncGen = SubAsyncGen
# ---- 初始化各子对象字段(Init* 方法内联调用) ----
SubBaseGen.InitBaseGen(pool)
SubTypeGenBase.InitTypeGen()
SubCallGenBase.InitCallGen()
SubStructGen.InitStructGen()
SubClassGenBase.InitClassGen()
SubExprGenBase.InitExprGen()
SubImportsGen.InitImportsGen()
SubStmtGenCif.InitStmtGen()
SubAssignGen.InitAssignGen()
# ============================================================
# 全局生成器单例(组合架构兼容层)
# ============================================================
_g_gen: Generator* = None
+377 -201
View File
@@ -3,6 +3,7 @@ import t, c
from stdint import *
import ast
import memhub
import hashtable
import string
import stdlib
import viperlib
@@ -11,25 +12,16 @@ import sys
import w32.fileio as fileio
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.VLogger as VLogger
# ============================================================
# ImportsGenMixin - 导入处理 Mixin(对象化 P6-D,wiki/14-oo-refactor.md)
# ImportsGenMixin - 导入处理 Mixin
#
# 从 HandlesImports.vp 迁移:
# 全局状态(_g_reexport_sha1s/_g_reexport_funcs/_g_reexport_srcs/
# _g_reexport_count/_g_current_file)→ 本类字段
# 模块级函数(add_imported_module/is_module_imported/...)→ 本类方法
#
# 迁移原则(与 P1-P5 Mixin 一致,行为零变化):
# 1. 方法保留原函数签名(含 pool 首参),仅函数体内的全局变量改为 self.字段
# 2. 本模块内部函数调用改为 self.方法;跨模块调用(StubMerger 等)保留模块前缀
# 3. HandlesImports.vp 原函数保留为"委托":本地模块级单例 _g_gen
# (BuildPipeline/main 注入)cast 成 Generator 类型后调本类方法
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# 职责:模块导入表(imported_modules/from_imports)维护、re-export 登记、
# 相对导入解析与模块 SHA1 校验。@t.NoVTable:静态绑定(MRO 直接调用)。
# ============================================================
@@ -42,50 +34,162 @@ MAX_REEXPORT: t.CDefine = 512
# ============================================================
# ImportsGenMixin - 导入处理
# ============================================================
@t.NoVTable
class ImportsGenMixin:
novirtual class ImportsGenMixin:
"""导入处理 Mixin(P6 方法化)"""
# --- 全局 re-export 映射表(原 _g_reexport_sha1s/_g_reexport_funcs/
# _g_reexport_srcs/_g_reexport_count) ---
Gen: Generator.Generator | t.CPtr
Gen: Generator.Generator*
ReexportSha1s: bytes
ReexportFuncs: bytes
ReexportSrcs: bytes
ReexportCount: int
# --- 导入查找索引(把 O(串长) 的逐字符扫描降为 O(名字长) 哈希查找) ---
# imported_modules/_from_imports 是空格拼接的扁平串("a b c" / "n:m[:orig]"),
# 长度可达数千字符,而每个 Name 节点都会多次查询。索引与「当前串指针」绑定:
# 调用方查询的总是 add_* 刚生成的当前串,故命中索引快路径;其余来源(stub 期
# stdlib malloc 缓冲等)指针不匹配 → 回退逐字符扫描,语义完全不变。
# 索引表用 PermPool 分配(跨文件/Phase 存活),由对应 add_* 增量维护;key/value
# 均为指向该扁平串内部的指针,串在文件级 reset 前始终有效。
CurImportedModulesPtr: str
CurImportedModulesNames: hashtable.HashTable*
CurFromImportsPtr: str
CurFromImportsMods: hashtable.HashTable*
CurFromImportsOrigs: hashtable.HashTable*
# 注意:不声明 CurrentFile 字段——TypeGenMixin 已声明(Generator 展平块
# 只有一份,跨父类不去重规则下重复声明会导致字段错位)。本 Mixin 的
# set_current_file 与 CurrentFile 读取统一经 HandlesType 委托(延迟 import
# 规避 HandlesType↔HandlesImports 循环依赖)。
# _index_imported_module - 增量维护 imported_modules 查找索引
# old_ptr 为追加前的串指针(None = 首个模块),new_ptr 为追加后的新串,
# name_ptr 指向新串内的新模块名(key 直接引用串内部,无需复制)。
# 仅当 old_ptr 恰为当前索引绑定的串时复用其索引;否则放弃索引绑定并回退
# 逐字符扫描(语义不变)。
def _index_imported_module(self, old_ptr: str, new_ptr: str,
name_ptr: str) -> None:
"""增量更新 imported_modules 索引(复用旧索引或新建,失败则解除绑定)"""
if new_ptr is None or name_ptr is None:
return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None:
return
idx: hashtable.HashTable* = None
if old_ptr is None:
# 新文件/新链首:建全新索引(旧索引随旧串一起废弃)
idx = hashtable.HashTable(perm)
elif old_ptr == self.CurImportedModulesPtr and self.CurImportedModulesNames is not None:
# 正常路径:复用当前串的索引并追加(不重建,内存 O(导入数))
idx = self.CurImportedModulesNames
else:
# 旧串非当前索引串(异常来源):解除绑定,回退逐字符扫描
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
return
if idx is None:
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
return
# 同名已在(重复导入)则跳过;否则写入(key/value 均指向 new_ptr 内部)
if idx.__contains__(name_ptr) == 0:
idx.set_str(name_ptr, name_ptr)
self.CurImportedModulesPtr = new_ptr
self.CurImportedModulesNames = idx
# _index_from_import - 增量维护 from_imports 查找索引
# entry_off 为新增 entry("local:module[:orig]")在新串内相对首字节的偏移。
# 模块表 key=local_name → 指向模块名切片;orig 表 key=local_name → 指向原始名
# 切片(无别名时写入 None,以"名存在但无原始名"区别于未命中)。
# 语义对齐逐字符扫描:精确名首次命中为准(不覆盖),star 模块名以最后一条为准。
def _index_from_import(self, old_ptr: str, new_ptr: str, entry_off: sizet,
local_name: str, module_name: str,
original_name: str) -> None:
"""增量更新 from_imports 索引(复用旧索引或新建,失败则解除绑定)"""
if new_ptr is None or local_name is None or module_name is None:
return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None:
return
idx_m: hashtable.HashTable* = None
idx_o: hashtable.HashTable* = None
if old_ptr is None:
idx_m = hashtable.HashTable(perm)
idx_o = hashtable.HashTable(perm)
elif old_ptr == self.CurFromImportsPtr \
and self.CurFromImportsMods is not None \
and self.CurFromImportsOrigs is not None:
idx_m = self.CurFromImportsMods
idx_o = self.CurFromImportsOrigs
else:
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return
if idx_m is None or idx_o is None:
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return
local_len: sizet = string.strlen(local_name)
mod_len: sizet = string.strlen(module_name)
# 哈希键必须是 NUL 结尾串:扁平串内 local 段以 ':' 结尾(非 NUL),直接当键会让
# FNV 哈希到 "local:module..." 整段,与查询名 "local" 永不相等 → 索引恒 miss。
# 故用 PermPool 分配 NUL 结尾的稳定副本作键(与索引表同寿命),值仍指向扁平串
# 内模块名切片。
key_ptr: str = perm.alloc(local_len + 1)
if key_ptr is None:
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return
string.strncpy(key_ptr, local_name, local_len)
key_ptr[local_len] = '\0'
mod_ptr: str = new_ptr + entry_off + local_len + 1
if local_len == 1 and local_name[0] == '*':
# star import:模块名以最后一条为准(覆盖旧值,对齐 star_mod 语义)
idx_m.set_str(key_ptr, mod_ptr)
else:
if idx_m.__contains__(key_ptr) == 0:
idx_m.set_str(key_ptr, mod_ptr)
if idx_o.__contains__(key_ptr) == 0:
if original_name is not None and string.strcmp(original_name, local_name) != 0 \
and string.strlen(original_name) > 0:
idx_o.set_str(key_ptr, mod_ptr + mod_len + 1)
else:
# 首次出现即无别名(或别名为空)→ 该名查询返回 None(与逐字符扫描一致)
idx_o.set_str(key_ptr, None)
self.CurFromImportsPtr = new_ptr
self.CurFromImportsMods = idx_m
self.CurFromImportsOrigs = idx_o
# ============================================================
# 添加已导入模块名
# ============================================================
def add_imported_module(self, pool: memhub.MemBuddy | t.CPtr,
def add_imported_module(self, pool: memhub.MemBuddy*,
imported_modules: str,
name: str) -> str:
"""记录已导入的模块名,返回新的 imported_modules 字符串指针"""
"""记录已导入的模块名,返回新的 imported_modules 字符串指针
字符串记账走 StubMergerStore.AppendImportedModule(单一真源,与 vpi
注入期同源);本方法只额外维护查询索引。
"""
if name is None:
return imported_modules
if imported_modules is None:
nlen: t.CSizeT = string.strlen(name)
buf: str = pool.alloc(nlen + 1)
if buf is not None:
string.strcpy(buf, name)
return buf
return None
else:
old_len: t.CSizeT = string.strlen(imported_modules)
name_len: t.CSizeT = string.strlen(name)
new_len: t.CSizeT = old_len + 1 + name_len
buf2: str = pool.alloc(new_len + 1)
if buf2 is not None:
string.strcpy(buf2, imported_modules)
buf2[old_len] = ' '
string.strcpy(buf2 + old_len + 1, name)
return buf2
old_len: sizet = 0
if imported_modules is not None:
old_len = string.strlen(imported_modules)
buf: str = StubMergerStore.AppendImportedModule(pool, imported_modules, name)
if buf is None:
return imported_modules
if imported_modules is None:
# 索引:首个模块名(key 指向 buf 内部)
self._index_imported_module(None, buf, buf)
else:
# 索引:复用旧串索引并追加新名字(key 指向 buf 内的 name 段)
self._index_imported_module(imported_modules, buf, buf + old_len + 1)
return buf
# ============================================================
# 检查模块是否已导入
@@ -94,19 +198,27 @@ class ImportsGenMixin:
"""检查模块是否已导入(词边界精确匹配,避免子串误匹配)"""
if name is None or imported_modules is None:
return 0
name_len: t.CSizeT = string.strlen(name)
# [索引快路径] 传入串即当前索引绑定的串(trans 字段的常态)→ O(名字长) 哈希查;
# 指针不匹配(stub 期 malloc 缓冲等)→ 回退下方逐字符扫描,语义不变。
if imported_modules == self.CurImportedModulesPtr \
and self.CurImportedModulesNames is not None:
if self.CurImportedModulesNames.__contains__(name) != 0:
return 1
# 索引未命中:绝不据此返回 0(索引不完整会误判"未导入"),
# 落到下方原逐字符扫描按真实串内容判定(正确性优先 | 性能次之)。
name_len: sizet = string.strlen(name)
cur: str = imported_modules
total_len: t.CSizeT = string.strlen(imported_modules)
ci: t.CSizeT = 0
total_len: sizet = string.strlen(imported_modules)
ci: sizet = 0
while ci < total_len:
while ci < total_len and cur[ci] == ' ':
ci += 1
if ci >= total_len:
break
word_start: t.CSizeT = ci
word_start: sizet = ci
while ci < total_len and cur[ci] != ' ':
ci += 1
word_len: t.CSizeT = ci - word_start
word_len: sizet = ci - word_start
if word_len == name_len:
match: int = 1
for ei in range(name_len):
@@ -141,8 +253,8 @@ class ImportsGenMixin:
return 0
if StubMergerStore.LookupSha1ByModName(name) is None:
return 0
nl: t.CSizeT = string.strlen(name)
dot_pos: t.CSizeT = 0
nl: sizet = string.strlen(name)
dot_pos: sizet = 0
while dot_pos < nl:
if name[dot_pos] == '.':
pfx: str = stdlib.malloc(dot_pos + 1)
@@ -159,7 +271,7 @@ class ImportsGenMixin:
# ============================================================
# 添加 from-import 名称映射
# ============================================================
def add_from_import(self, pool: memhub.MemBuddy | t.CPtr,
def add_from_import(self, pool: memhub.MemBuddy*,
from_imports: str,
local_name: str,
module_name: str,
@@ -170,29 +282,26 @@ class ImportsGenMixin:
无别名: "local_name:module_name"
有别名: "local_name:module_name:original_name"
(original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰)
字符串记账走 StubMergerStore.AppendFromImport(单一真源,与 vpi 注入期
同源;按精确长度分配,无旧的 256 缓冲静默截断);本方法只额外维护索引。
"""
if local_name is None or module_name is None:
return from_imports
entry: str = pool.alloc(256)
if entry is None:
return from_imports
if original_name is not None and string.strcmp(original_name, local_name) != 0:
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:
old_len: t.CSizeT = string.strlen(from_imports)
entry_len: t.CSizeT = string.strlen(entry)
new_len: t.CSizeT = old_len + 1 + entry_len
buf: str = pool.alloc(new_len + 1)
if buf is not None:
string.strcpy(buf, from_imports)
buf[old_len] = ' '
string.strcpy(buf + old_len + 1, entry)
return buf
old_len: sizet = 0
if from_imports is not None:
old_len = string.strlen(from_imports)
buf: str = StubMergerStore.AppendFromImport(
pool, from_imports, local_name, module_name, original_name)
if buf is None:
return from_imports
# 索引:首条 entry_off=0;追加时 entry 从 buf[old_len+1] 开始
entry_off: sizet = 0
if from_imports is not None:
entry_off = old_len + 1
self._index_from_import(from_imports, buf, entry_off,
local_name, module_name, original_name)
return buf
# ============================================================
# 查找 from-import 名称 → 返回原始函数名(别名场景)
@@ -205,10 +314,19 @@ class ImportsGenMixin:
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
if local_name is None or from_imports is None:
return None
name_len: t.CSizeT = string.strlen(local_name)
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
# orig 表:名存在但存 None = 首次出现即无别名 → 返回 None(与扫描一致)。
if from_imports == self.CurFromImportsPtr and self.CurFromImportsOrigs is not None:
if self.CurFromImportsOrigs.__contains__(local_name) != 0:
v_o: t.CPtr = self.CurFromImportsOrigs.__getitem__(local_name)
if v_o is not None:
return rawptr(v_o)
# 索引未命中 | 值为 None(首个即无别名):绝不据此直接返回 None——
# 真实串可能仍含该名,落到下方原逐字符扫描按串内容判定(正确性优先)。
name_len: sizet = string.strlen(local_name)
cur: str = from_imports
ci: t.CSizeT = 0
total_len: t.CSizeT = string.strlen(from_imports)
ci: sizet = 0
total_len: sizet = string.strlen(from_imports)
while ci < total_len:
# 跳过前导空格
while ci < total_len and cur[ci] == ' ':
@@ -216,12 +334,12 @@ class ImportsGenMixin:
if ci >= total_len:
break
# 找到第一个 ':' 的位置
colon1: t.CSizeT = ci
colon1: sizet = 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
entry_name_len: sizet = colon1 - ci
# 跳过 star import
if entry_name_len == 1 and cur[ci] == '*':
ci = colon1
@@ -231,7 +349,7 @@ class ImportsGenMixin:
# 比较名称
if entry_name_len == name_len:
match: int = 1
ei: t.CSizeT = 0
ei: sizet = 0
while ei < name_len:
if cur[ci + ei] != local_name[ei]:
match = 0
@@ -239,17 +357,17 @@ class ImportsGenMixin:
ei += 1
if match == 1:
# 找到匹配,检查是否有第三个字段(原始名)
pos: t.CSizeT = colon1 + 1
pos: sizet = 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
orig_start: sizet = pos + 1
orig_end: sizet = 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
orig_len: sizet = orig_end - orig_start
if orig_len > 0:
orig_buf: str = cur + orig_start
# 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效)
@@ -279,10 +397,26 @@ class ImportsGenMixin:
"""
if name is None or from_imports is None:
return None
name_len: t.CSizeT = string.strlen(name)
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
# name 恰为 "*" 时与扫描一致:不做精确匹配,直接走 star 回退。
if from_imports == self.CurFromImportsPtr and self.CurFromImportsMods is not None:
name_is_star: int = 0
if string.strlen(name) == 1 and name[0] == '*':
name_is_star = 1
if name_is_star == 0:
v_f: t.CPtr = self.CurFromImportsMods.__getitem__(name)
if v_f is not None:
return rawptr(v_f)
if allow_star_fallback != 0:
sv_f: t.CPtr = self.CurFromImportsMods.__getitem__("*")
if sv_f is not None:
return rawptr(sv_f)
# 索引精确名 | star 均未命中:绝不据此直接返回 None(索引不完整会漏报
# "名已 import",_op_head NameError 即此类),落到下方原逐字符扫描按串内容判定。
name_len: sizet = string.strlen(name)
cur: str = from_imports
ci: t.CSizeT = 0
total_len: t.CSizeT = string.strlen(from_imports)
ci: sizet = 0
total_len: sizet = string.strlen(from_imports)
star_mod: str = None
while ci < total_len:
# 跳过前导空格
@@ -291,31 +425,31 @@ class ImportsGenMixin:
if ci >= total_len:
break
# 找到 ':' 的位置
colon_pos: t.CSizeT = ci
colon_pos: sizet = ci
while colon_pos < total_len and cur[colon_pos] != ':' and cur[colon_pos] != ' ':
colon_pos += 1
if colon_pos >= total_len or cur[colon_pos] != ':':
break
entry_name_len: t.CSizeT = colon_pos - ci
entry_name_len: sizet = colon_pos - ci
# 检测 star import ("*:module")
if entry_name_len == 1 and cur[ci] == '*':
mod_start: t.CSizeT = colon_pos + 1
mod_end: t.CSizeT = mod_start
mod_start: sizet = colon_pos + 1
mod_end: sizet = mod_start
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
# 比较名称
elif entry_name_len == name_len:
match: int = 1
ei: t.CSizeT = 0
ei: sizet = 0
while ei < name_len:
if cur[ci + ei] != name[ei]:
match = 0
break
ei += 1
if match == 1:
mod_start2: t.CSizeT = colon_pos + 1
mod_end2: t.CSizeT = mod_start2
mod_start2: sizet = colon_pos + 1
mod_end2: sizet = mod_start2
# 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符)
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':':
mod_end2 += 1
@@ -344,9 +478,9 @@ class ImportsGenMixin:
return -1
# 懒初始化
if self.ReexportSha1s is None:
self.ReexportSha1s = CompilerFactory.get_perm_pool().alloc(MAX_REEXPORT * 17)
self.ReexportFuncs = CompilerFactory.get_perm_pool().alloc(MAX_REEXPORT * 64)
self.ReexportSrcs = CompilerFactory.get_perm_pool().alloc(MAX_REEXPORT * 64)
self.ReexportSha1s = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 17)
self.ReexportFuncs = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 64)
self.ReexportSrcs = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 64)
if self.ReexportSha1s is None or self.ReexportFuncs is None or self.ReexportSrcs is None:
return -1
string.memset(self.ReexportSha1s, 0, MAX_REEXPORT * 17)
@@ -356,8 +490,8 @@ class ImportsGenMixin:
return -1
# 幂等检查:查找是否已存在相同映射
for i in range(self.ReexportCount):
sidx: t.CSizeT = t.CSizeT(i) * 17
fidx: t.CSizeT = t.CSizeT(i) * 64
sidx: sizet = sizet(i) * 17
fidx: sizet = sizet(i) * 64
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
if string.strcmp(self.ReexportFuncs + fidx, func_name) == 0:
# 已存在,更新 source_module(以防变化;64 字节槽,strncpy 钳制)
@@ -365,8 +499,8 @@ class ImportsGenMixin:
self.ReexportSrcs[fidx + 63] = '\0'
return 0
# 添加新条目(槽位定长 17/64 字节,strcpy 无界会越界写相邻槽,全部 strncpy 钳制)
idx2: t.CSizeT = t.CSizeT(self.ReexportCount) * 17
fidx2: t.CSizeT = t.CSizeT(self.ReexportCount) * 64
idx2: sizet = sizet(self.ReexportCount) * 17
fidx2: sizet = sizet(self.ReexportCount) * 64
string.strncpy(self.ReexportSha1s + idx2, mod_sha1, 16)
self.ReexportSha1s[idx2 + 16] = '\0'
string.strncpy(self.ReexportFuncs + fidx2, func_name, 63)
@@ -389,8 +523,8 @@ class ImportsGenMixin:
if self.ReexportSha1s is None or self.ReexportCount <= 0:
return None
for i in range(self.ReexportCount):
sidx: t.CSizeT = t.CSizeT(i) * 17
fidx: t.CSizeT = t.CSizeT(i) * 64
sidx: sizet = sizet(i) * 17
fidx: sizet = sizet(i) * 64
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
if string.strcmp(self.ReexportFuncs + fidx, func_name) == 0:
return self.ReexportSrcs + fidx
@@ -416,9 +550,9 @@ class ImportsGenMixin:
# 解析后的完整模块名;绝对导入(level<=0)直接返回 module;
# 无法解析时返回 module(回退到原始值)
# ============================================================
def _resolve_relative_module(self, pool: memhub.MemBuddy | t.CPtr,
def _resolve_relative_module(self, pool: memhub.MemBuddy*,
current_package: str,
level: t.CInt,
level: int,
module: str) -> str:
"""解析相对导入为完整模块名"""
if level <= 0:
@@ -432,12 +566,12 @@ class ImportsGenMixin:
# 从 current_package 开始,上溯 (level-1) 级
pkg: str = current_package
up: t.CInt = level - 1
up: int = level - 1
while up > 0:
pkg_len: t.CSizeT = string.strlen(pkg)
last_dot: t.CSizeT = 0
pkg_len: sizet = string.strlen(pkg)
last_dot: sizet = 0
found: int = 0
i: t.CSizeT = 0
i: sizet = 0
while i < pkg_len:
if pkg[i] == '.':
last_dot = i
@@ -466,8 +600,8 @@ class ImportsGenMixin:
# 包已上溯到空(相对导入越界):回退到 module 原始值
return module
# 拼接 pkg + "." + module
pkg_len2: t.CSizeT = string.strlen(pkg)
mod_len: t.CSizeT = string.strlen(module)
pkg_len2: sizet = string.strlen(pkg)
mod_len: sizet = string.strlen(module)
buf: str = pool.alloc(pkg_len2 + 1 + mod_len + 1)
if buf is None:
self.Gen.TypeGenErr.fatal_error(None, f"相对导入解析失败:level={level} module='{module}'", "ImportError")
@@ -489,18 +623,18 @@ class ImportsGenMixin:
# "ast/parser.vp" → "ast"
# "ast.vp" → None(顶级文件)
# ============================================================
def compute_package_from_relpath(self, pool: memhub.MemBuddy | t.CPtr,
def compute_package_from_relpath(self, pool: memhub.MemBuddy*,
rel_path: str) -> str:
"""从相对路径计算包名(目录部分,分隔符替换为 .)"""
if rel_path is None:
return None
rlen: t.CSizeT = string.strlen(rel_path)
rlen: sizet = string.strlen(rel_path)
# 找最后一个 / 或 \
last_sep: t.CSizeT = 0
last_sep: sizet = 0
found: int = 0
i: t.CSizeT = 0
i: sizet = 0
while i < rlen:
ch: t.CChar = rel_path[i]
ch: char = rel_path[i]
if ch == '/' or ch == '\\':
last_sep = i
found = 1
@@ -509,13 +643,13 @@ class ImportsGenMixin:
# 无目录分隔符 → 顶级文件,无包
return None
# 复制目录部分,将 / 和 \ 替换为 .
dir_len: t.CSizeT = last_sep
dir_len: sizet = last_sep
buf: str = pool.alloc(dir_len + 1)
if buf is None:
return None
j: t.CSizeT = 0
j: sizet = 0
while j < dir_len:
ch2: t.CChar = rel_path[j]
ch2: char = rel_path[j]
if ch2 == '/' or ch2 == '\\':
buf[j] = '.'
else:
@@ -533,6 +667,14 @@ class ImportsGenMixin:
# ============================================================
def set_current_file(self, file_path: str) -> None:
"""设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)"""
# 文件级失效:索引绑定的是上一文件 arena 内的串指针,本文件 reset 后该
# 地址可能被新分配复用(如 _merge_template_imports 的合并串)→ 必须解绑,
# 否则可能命中陈旧索引返回错误结果。本文件的索引由后续 add_* 重新建立。
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
self.Gen.TypeGenResolve.set_current_file(file_path)
# ============================================================
@@ -544,7 +686,7 @@ class ImportsGenMixin:
# File "<当前文件>", line <N>
# ImportError: <msg>
# ============================================================
def _report_import_error(self, node: ast.AST | t.CPtr, msg: str) -> None:
def _report_import_error(self, node: ast.AST*, msg: str) -> None:
"""以 Python 风格报错并退出(本地实现,避免循环依赖)"""
stdio.printf("Traceback (most recent call last):\n")
lineno: int = 0
@@ -556,7 +698,7 @@ class ImportsGenMixin:
stdio.printf(' File "%s", line %d\n', file_name, lineno)
# 显示行内内容(读源文件第 lineno 行的实际代码,便于快速定位)
if lineno > 0 and file_name is not None:
f_err: fileio.File | t.CPtr = fileio.File(file_name, fileio.MODE.R)
f_err: rawptr[fileio.File] = fileio.File(file_name, fileio.MODE.R)
if not f_err.closed:
line_buf_err: bytes = stdlib.malloc(512)
if line_buf_err is not None:
@@ -567,7 +709,7 @@ class ImportsGenMixin:
break
cur_ln += 1
if cur_ln == lineno:
ll_err: t.CSizeT = string.strlen(line_buf_err)
ll_err: sizet = string.strlen(line_buf_err)
while ll_err > 0 and (line_buf_err[ll_err - 1] == '\n' or line_buf_err[ll_err - 1] == '\r'):
ll_err -= 1
line_buf_err[ll_err] = '\0'
@@ -576,7 +718,8 @@ class ImportsGenMixin:
stdlib.free(line_buf_err)
f_err.close()
stdio.printf("ImportError: %s\n", msg)
sys.exit(1)
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印),批处理下终止
VLogger.repl_abort()
# ============================================================
# _is_local_module_file - 当前文件同目录存在 {mod_name}.vp/.py
@@ -589,18 +732,18 @@ class ImportsGenMixin:
cur: str = self.Gen.TypeGenResolve.get_current_file()
if cur is None or mod_name is None:
return 0
cur_len: t.CSizeT = string.strlen(cur)
cur_len: sizet = string.strlen(cur)
if cur_len == 0:
return 0
sep_pos: t.CSizeT = cur_len
sep_pos: sizet = cur_len
while sep_pos > 0:
sep_pos -= 1
ch: t.CChar = cur[sep_pos]
ch: char = cur[sep_pos]
if ch == '/' or ch == '\\':
sep_pos += 1
break
mod_len: t.CSizeT = string.strlen(mod_name)
path_len: t.CSizeT = sep_pos + mod_len + 5
mod_len: sizet = string.strlen(mod_name)
path_len: sizet = sep_pos + mod_len + 5
path_buf: bytes = stdlib.malloc(path_len + 1)
if path_buf is None:
return 0
@@ -609,7 +752,7 @@ class ImportsGenMixin:
path_buf[sep_pos] = '\0'
string.strcat(path_buf, mod_name)
string.strcat(path_buf, ".vp")
fex: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
fex: rawptr[fileio.File] = fileio.File(path_buf, fileio.MODE.R)
if not fex.closed:
fex.close()
stdlib.free(path_buf)
@@ -617,18 +760,11 @@ class ImportsGenMixin:
stdlib.free(path_buf)
return 0
# ============================================================
# _check_import_sha1 - 导入时校验模块 SHA1 是否已注册(fast-fail)
#
# 背景:若 includes 路径配置错误(如 project.json 指向不存在的
# ./includes),includes 扫描结果为 0,模块不会注册 SHA1。此前
# 错误延迟到实际调用 module.func() 时才报误导性的
# "跨模块调用 'bd.alloc' 找不到模块 SHA1"。
# 现在在 import 语句处立即检测并报错,快速定位配置问题。
#
# includes 路径配置错误时扫描结果为 0、模块不注册 SHA1,若延迟到实际跨模块调用才报错
# 会误导("跨模块调用 'bd.alloc' 找不到模块 SHA1"),故在 import 语句处立即检测。
# t/c 是内部库,不参与 includes 扫描,豁免检查。
# ============================================================
def _check_import_sha1(self, node: ast.AST | t.CPtr, mod_name: str) -> int:
def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int:
"""导入时校验模块 SHA1 是否已注册,未注册则立即报错退出
Args:
@@ -665,50 +801,43 @@ class ImportsGenMixin:
# translate_import - 处理 import 语句,更新 trans._imported_modules
# (迁移自 HandlesImports.ImportsHandle.HandleImport)
# ============================================================
def translate_import(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""处理 import 语句,返回 0"""
imp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(node)
def translate_import(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""处理 import 语句,返回 0
记账走 StubMergerStore.NotePlainImport(**该约定的唯一实现**):与 vpi
注入期(VpiReader 顶层遍历)同口径,包含
· `import X as Y` → from_imports 追加 "Y:X"(别名反查模块真名的唯一
入口,见 StubMergerStore.LookupModNameByAlias);
· `import a.b.c` → imported_modules 追加顶层包名 "a"。
两侧各自只保留特有副作用(此处:导入 SHA1 校验)。
注:NotePlainImport 为纯记账,不再续接 imported/from 两个查询索引 ⇒ 这两
个串上的查找走 is_module_imported / lookup_from_import 的逐字符扫描回退
(与 vpi 注入期上下文同款,正确性不受影响)。
"""
imp: ast.Import* = ast.Import*(node)
if imp is None:
return 0
names: list[ast.AST | t.CPtr] | t.CPtr = imp.names
names: list[ast.AST*]* = imp.names
if names is None:
return 0
nn: t.CSizeT = len(names)
nn: sizet = len(names)
pool_val: memhub.MemBuddy* = trans.Pool
ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx(
pool_val, trans._imported_modules, trans._from_imports)
if ctx is None:
return 0
for ni in range(nn):
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni))
alias: ast.Alias* = ast.Alias*(names.get(ni))
if alias is not None and alias.name is not None:
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
# declare_only=1/2 是 struct 预注册/import 扫描阶段:此时 SHA1 表
# 尚未包含后续才追加的模块(如 App 源文件),检查会造成误报
# 尚未包含后续才追加的模块(如 App 源文件),检查会造成误报。
if trans._declare_only == 0:
self._check_import_sha1(node, alias.name)
pool_val: memhub.MemBuddy | t.CPtr = trans.Pool
im_val: str = trans._imported_modules
trans._imported_modules = self.add_imported_module(
pool_val, im_val, alias.name)
# 别名也加入导入模块列表(用于模块限定构造器检查 Module.Class())
if alias.asname is not None:
trans._imported_modules = self.add_imported_module(
pool_val, trans._imported_modules, alias.asname)
# 别名也加入 from_imports(asname:fullname),
# 使 HT.func() 跨模块调用能通过 from_imports 查找别名→完整模块名→SHA1
# 否则 _lookup_module_sha1("HT") 找不到(mod_arr 只存文件名 HandlesTranslator)
trans._from_imports = self.add_from_import(
pool_val, 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'
trans._imported_modules = self.add_imported_module(
pool_val, trans._imported_modules, pkg_buf)
StubMergerStore.NotePlainImport(pool_val, ctx, alias.name, alias.asname)
trans._imported_modules = ctx.ImportedModules
trans._from_imports = ctx.FromImports
return 0
# ============================================================
@@ -719,45 +848,53 @@ class ImportsGenMixin:
# 完整模块名(如 __types → llvmlite.__types),确保 .deps.txt
# 记录的模块名与 SHA1 映射表一致。
# ============================================================
def translate_import_from_module(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_import_from_module(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""处理 from X import Y 的模块部分,更新 trans._imported_modules"""
impf: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(node)
impf: ast.ImportFrom* = ast.ImportFrom*(node)
if impf is None:
return 0
# 解析模块名:相对导入需补充包前缀
resolved: str = self._resolve_relative_module(
trans.Pool, trans.CurrentPackage,
resolved: str = self._resolve_relative_module(trans.Pool, trans.CurrentPackage,
impf.level, impf.module)
if resolved is not None:
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
if trans._declare_only == 0:
# from . import Y(impf.module is None):resolved 是包目录名
# (lib.core.Gen.Call 等无 __init__.vp 的目录包也允许这样导入,
# 见 CallGenCall.vp L12 from . import CallGenBase),目录本身不是
# 模块文件、无 SHA1 → 跳过包级校验;真实目标子模块(pkg.Y)由
# 下方 L759-762 逐个校验(LookupSha1ByModName / 本地兄弟文件)。
if trans._declare_only == 0 and impf.module is not None:
self._check_import_sha1(node, resolved)
trans._imported_modules = self.add_imported_module(
trans.Pool, trans._imported_modules, resolved)
trans._imported_modules = self.add_imported_module(trans.Pool, trans._imported_modules, resolved)
# from . import Y(impf.module is None):resolved 是包名(vpsdk),
# 各 name 是包的子模块 vpsdk.Y。须把完整子模块名也加入
# _imported_modules,否则别名 "syscall" 精确匹配不到包名 "vpsdk"
# → is_module_imported 返回 0 → syscall._syscall1 / syscall.LOAD_SO
# 跨模块解析失败(vpsdk/dynlib.vp 根因)。
# 各 name 是包的子模块 vpsdk.Y。须把完整子模块名也加入 _imported_modules,
# 否则别名 "syscall" 精确匹配不到包名 "vpsdk" → is_module_imported 返回 0
# → syscall._syscall1 / syscall.LOAD_SO 跨模块解析失败。
if impf.module is None:
names_m: list[ast.AST | t.CPtr] | t.CPtr = impf.names
names_m: list[ast.AST*]* = impf.names
if names_m is not None:
nn_m: t.CSizeT = len(names_m)
nn_m: sizet = len(names_m)
for ni_m in range(nn_m):
alias_m: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names_m.get(ni_m))
alias_m: ast.Alias* = ast.Alias*(names_m.get(ni_m))
if alias_m is not None and alias_m.name is not None:
rl_m: t.CSizeT = string.strlen(resolved)
rn_m: t.CSizeT = string.strlen(alias_m.name)
rl_m: sizet = string.strlen(resolved)
rn_m: sizet = string.strlen(alias_m.name)
jb_m: str = trans.Pool.alloc(rl_m + rn_m + 2)
if jb_m is not None:
string.strcpy(jb_m, resolved)
jb_m[rl_m] = '.'
string.strcpy(jb_m + rl_m + 1, alias_m.name)
if trans._declare_only == 0:
self._check_import_sha1(node, jb_m)
trans._imported_modules = self.add_imported_module(
trans.Pool, trans._imported_modules, jb_m)
# from . import Y 的 Y 不一定是包子模块文件:可能是包
# __init__.vp 中定义的符号(类/常量,如 JsonValue),无独立
# 文件、不注册 SHA1。仅当完整名已注册 SHA1(真子模块)或是
# 本地文件时才校验+登记;否则跳过,符号映射由
# translate_import_from_names 的 from-import / re-export 处理。
if (StubMergerStore.LookupSha1ByModName(jb_m) is not None
or self._is_local_module_file(alias_m.name) != 0):
if trans._declare_only == 0:
self._check_import_sha1(node, jb_m)
trans._imported_modules = self.add_imported_module(trans.Pool, trans._imported_modules, jb_m)
return 0
# ============================================================
@@ -767,22 +904,53 @@ class ImportsGenMixin:
# 同样使用解析后的完整模块名,确保 from-import 映射
# (如 LLVMType:llvmlite.__types)能正确查到 SHA1。
# ============================================================
def translate_import_from_names(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
# _CheckStaticFromImport - static 符号 from-import 拦截
#
# `from mod import name` 命中源模块登记的 static 符号 → fail-fast(与
# ExprGenAttr 的 `mod.name` 取值拦截、CallGenMixin 的 `mod.fn()` 调用拦截同源,
# 覆盖 from-import 形态)。用源码原名判表——定义侧登记用的也是裸名。
# 独立成函数:内联进 translate_import_from_names 会干扰其类型推断(同款处理
# 见 ExprGenAttr._CheckStaticCrossModule 与 CallGenMixin._CheckStaticCallCrossModule)。
# ============================================================
def _CheckStaticFromImport(self, trans: HT.Translator*,
fi_target: str,
orig_name: str,
node: ast.AST*) -> int:
if trans is None or fi_target is None or orig_name is None:
return 0
if trans.ModuleSha1 is None:
return 0
# 快速短路:项目内无 static 符号时零开销(不查 sha1、不扫表)
if self.Gen.CallGenBase.StaticCount <= 0:
return 0
sf_sha1: str = self.Gen.StructGen._resolve_module_sha1(fi_target, trans._from_imports)
if sf_sha1 is None:
return 0
if string.strncmp(sf_sha1, trans.ModuleSha1, 16) == 0:
return 0
if self.Gen.CallGenBase.IsStaticSymbol(sf_sha1, orig_name) == 0:
return 0
self.Gen.TypeGenErr.fatal_error(node,
f"'{fi_target}.{orig_name}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
"NameError")
return 0
def translate_import_from_names(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""处理 from X import Y 的名称部分,更新 trans._from_imports"""
impf: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(node)
impf: ast.ImportFrom* = ast.ImportFrom*(node)
if impf is None:
return 0
# 解析模块名:相对导入需补充包前缀
resolved: str = self._resolve_relative_module(
trans.Pool, trans.CurrentPackage,
resolved: str = self._resolve_relative_module(trans.Pool, trans.CurrentPackage,
impf.level, impf.module)
if resolved is not None:
names: list[ast.AST | t.CPtr] | t.CPtr = impf.names
names: list[ast.AST*]* = impf.names
if names is not None:
nn: t.CSizeT = len(names)
nn: sizet = len(names)
for ni in range(nn):
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni))
alias: ast.Alias* = ast.Alias*(names.get(ni))
if alias is not None and alias.name is not None:
local_name: str = alias.name
orig_name: str = None
@@ -790,25 +958,27 @@ class ImportsGenMixin:
local_name = alias.asname
orig_name = alias.name
# from . import Y(impf.module is None):_resolve_relative_module
# 返回的是包名(如 vpsdk),但 Y 是包的子模块 vpsdk.Y
# (vpsdk/syscall.vp)。此前直接映射 local_name → 包名,
# 导致 syscall.LOAD_SO 跨模块 CDefine / syscall._syscall1
# 跨模块调用在包里找不到(vpsdk/dynlib.vp `from . import
# syscall as syscall` 根因)。
# 返回的是包名(如 vpsdk),但 Y 是包的子模块 vpsdk.Y。须映射
# local_name → 完整子模块名,否则 syscall.LOAD_SO 跨模块 CDefine /
# syscall._syscall1 跨模块调用在包里找不到。
fi_target: str = resolved
if impf.module is None and resolved is not None:
fl_t: t.CSizeT = string.strlen(resolved)
fn_t: t.CSizeT = string.strlen(alias.name)
fl_t: sizet = string.strlen(resolved)
fn_t: sizet = string.strlen(alias.name)
jb_t: str = trans.Pool.alloc(fl_t + fn_t + 2)
if jb_t is not None:
string.strcpy(jb_t, resolved)
jb_t[fl_t] = '.'
string.strcpy(jb_t + fl_t + 1, alias.name)
fi_target = jb_t
trans._from_imports = self.add_from_import(
trans.Pool, trans._from_imports,
trans._from_imports = self.add_from_import(trans.Pool, trans._from_imports,
local_name, fi_target, orig_name)
# 命名空间隔离:from-import 的名称标记为可见结构体
# static 符号(禁止跨模块使用):from mod import name 命中源
# 模块登记的 static 符号 → fail-fast。逻辑收敛在
# _CheckStaticFromImport 内(不内联——内联新增分支会干扰本函数
# 的类型推断,与 ExprGenAttr/CallGenMixin 同款处理)。
self._CheckStaticFromImport(trans, fi_target, alias.name, node)
# 命名空间隔离:from-import 的名称标记为可见结构体
self.Gen.StructGen.add_visible_struct(trans.Pool, local_name)
# 注册 re-export 映射:当前模块 re-export 了 local_name,
# 源模块为 fi_target(from . import Y 时是子模块名 vpsdk.Y,
@@ -825,4 +995,10 @@ class ImportsGenMixin:
self.ReexportFuncs = None
self.ReexportSrcs = None
self.ReexportCount = 0
# 导入查找索引字段(索引表由 add_* 增量创建,PermPool 分配)
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return 0
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -9,78 +9,69 @@ import stdlib
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
import lib.Projectrans.Config as Config
import lib.core.Gen.StmtGenCifMixin as StmtGenCifMixin
import lib.core.Gen.Stmt.StmtGenCifMixin as StmtGenCifMixin
import lib.core.Gen.GenApi as GenApi
# ============================================================
# StmtGenDeclMixin - StmtGen 拆分 2/3:声明/枚举/nonlocal 访问
#
# 按功能域从 StmtGenMixin.vp 拆出(500~1500 行/文件架构重构,试点):
# translate_global/nonlocal/expr_stmt/pre_scan、break/continue、
# nonlocal 闭包 env 访问、枚举注册表。继承 StmtGenCifMixin。
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# 常量(与 HandlesEnum 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# 常量
# ============================================================
ENUM_MAX: t.CDefine = 64
ENUM_MEMBER_MAX: t.CDefine = 128
define ENUM_MAX = 64
define ENUM_MEMBER_MAX = 128
# ============================================================
# StmtGenDeclMixin - 声明/枚举/nonlocal(中层级)
# ============================================================
@t.NoVTable
class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
"""声明/枚举/nonlocal 访问(StmtGen 拆分 2/3,继承 Cif)"""
def translate_global(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_global(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 global 语句:记录 global 变量名"""
gn: ast.Global | t.CPtr = (ast.Global | t.CPtr)(node)
gn: rawptr[ast.Global] = rawptr[ast.Global](node)
if gn is None:
return 0
names: list[ast.AST | t.CPtr] | t.CPtr = gn.names
names: list[ast.AST*]* = gn.names
if names is None:
return 0
n: t.CSizeT = len(names)
n: sizet = len(names)
for i in range(n):
nm_node: ast.AST | t.CPtr = names.get(i)
nm_node: ast.AST* = names.get(i)
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node)
nm: ast.Name* = ast.Name*(nm_node)
if nm.id is not None:
HT.add_global_name(trans, nm.id)
GenApi.add_global_name(trans, nm.id)
return 0
def translate_nonlocal(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_nonlocal(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 nonlocal 语句:记录 nonlocal 变量名及其类型
类型信息通过查找父作用域的 alloca 获取(用于 env 指针数组方案的类型恢复)。
"""
nl: ast.Nonlocal | t.CPtr = (ast.Nonlocal | t.CPtr)(node)
nl: rawptr[ast.Nonlocal] = rawptr[ast.Nonlocal](node)
if nl is None:
return 0
names: list[ast.AST | t.CPtr] | t.CPtr = nl.names
names: list[ast.AST*]* = nl.names
if names is None:
return 0
n: t.CSizeT = len(names)
n: sizet = len(names)
for i in range(n):
nm_node: ast.AST | t.CPtr = names.get(i)
nm_node: ast.AST* = names.get(i)
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node)
nm: ast.Name* = ast.Name*(nm_node)
if nm.id is not None:
# 查找父作用域中的变量 alloca,获取类型信息
var_ty: llvmlite.LLVMType | t.CPtr = None
var_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, nm.id)
var_ty: llvmlite.LLVMType* = None
var_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if var_alloca is not None and var_alloca.Ty is not None:
var_ty = var_alloca.Ty.Pointee
# Void 类型视为无效(Pointee 访问异常时可能返回 Void),
@@ -92,24 +83,32 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal 变量 '{nm.id}' 类型无效(Void/无法解析)", "TypeError")
case _:
pass
HT.add_nonlocal_name(trans, nm.id, var_ty)
GenApi.add_nonlocal_name(trans, nm.id, var_ty)
return 0
def translate_expr_stmt(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_expr_stmt(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译表达式语句(printf 调用等)"""
ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node)
ex: rawptr[ast.Expr] = rawptr[ast.Expr](node)
if ex is None:
return 0
call_node: ast.AST | t.CPtr = ex.value
call_node: ast.AST* = ex.value
if call_node is None:
return 0
ck: int = call_node.kind()
if ck != ast.ASTKind.Call:
# REPL 模式(M2):片段函数本体内的裸表达式语句(如 1+2 / x / x + 1)
# 求值后写入 ctx 结果区,由宿主回显。调用表达式仍走下方原路径
# (执行 + 丢弃返回值,同 print/printf 语义)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ "表达式语句除调用外一律忽略"
# 的原行为逐字不变。
if trans is not None and trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
self.Gen.VarGen.repl_expr_result(trans, call_node)
return 0
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(call_node)
func_node: ast.AST | t.CPtr = cl.func
cl: ast.Call* = ast.Call*(call_node)
func_node: ast.AST* = cl.func
if func_node is None:
return 0
@@ -120,20 +119,37 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
# printf / print 走特殊路径(print 是类型感知格式化,不映射到 printf)
if string.strcmp(func_name, "printf") == 0:
self.Gen.CallGenLeafKit.translate_printf_call(
trans.Pool, trans._cur_builder, trans.Module, cl, trans)
self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
elif string.strcmp(func_name, "print") == 0:
self.Gen.CallGenLeafKit.translate_print_call(
trans.Pool, trans._cur_builder, trans.Module, cl, trans)
self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
else:
# 通用函数调用(返回值丢弃)
self.Gen.CallGen.translate_call(
trans.Pool, trans._cur_builder, trans.Module, call_node,
trans._funcs, trans._func_count, trans)
# M3:裸名调用**会话函数**(跨片段)时额外把返回值写进 ctx 结果区回显
# (M2 的限制条目"调用表达式的结果不回显"对会话函数放开——用户在
# `>>> f(41)` 后期望看到 `=> 42`)。结果区的类型分类直接用会话函数表里
# 记录的返回类型 token(宿主从定义片段 IR 解析而来,是定义端 ABI 真相),
# 不用影子条目推断 ⇒ 与调用点签名同源。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 原"执行 + 丢弃返回值"逐字不变;
# 仅裸名(Name)且表中登记的会话函数走本分支(printf/print 等在上方已分流)。
if trans is not None and trans.ReplCtx is not None \
and func_node.kind() == ast.ASTKind.Name \
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
sfn_ent: rawptr[GenTypes.ReplFuncEntry] = \
self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
sfn_v: llvmlite.Value* = self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node,
trans._funcs, trans._func_count, trans)
if sfn_v is not None:
ctx_v: llvmlite.Value* = self.Gen.VarGen.repl_ctx_ptr(trans, trans.ReplCtx, call_node)
if ctx_v is not None:
self.Gen.VarGen.repl_emit_result(trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits,
call_node)
return 0
self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
return 0
def pre_scan_allocas(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def pre_scan_allocas(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""预扫描语句中的 AnnAssign,提前创建 alloca
递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部
@@ -150,87 +166,87 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
if k == ast.ASTKind.AnnAssign:
return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
if k == ast.ASTKind.If:
ifn: ast.If | t.CPtr = (ast.If | t.CPtr)(node)
ifn: rawptr[ast.If] = rawptr[ast.If](node)
if ifn.children is not None:
bn_pre_i: t.CSizeT = len(ifn.children)
bn_pre_i: sizet = len(ifn.children)
for bi_pre_i in range(bn_pre_i):
self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
if ifn.orelse is not None:
bn_pre_e: t.CSizeT = len(ifn.orelse)
bn_pre_e: sizet = len(ifn.orelse)
for bi_pre_e in range(bn_pre_e):
self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
return 0
if k == ast.ASTKind.For:
for_n: ast.For | t.CPtr = (ast.For | t.CPtr)(node)
for_n: ast.For* = ast.For*(node)
if for_n.children is not None:
bn_pre_f: t.CSizeT = len(for_n.children)
bn_pre_f: sizet = len(for_n.children)
for bi_pre_f in range(bn_pre_f):
self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
if for_n.orelse is not None:
bn_pre_fe: t.CSizeT = len(for_n.orelse)
bn_pre_fe: sizet = len(for_n.orelse)
for bi_pre_fe in range(bn_pre_fe):
self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
return 0
if k == ast.ASTKind.While:
wh_n: ast.While | t.CPtr = (ast.While | t.CPtr)(node)
wh_n: rawptr[ast.While] = rawptr[ast.While](node)
if wh_n.children is not None:
bn_pre_w: t.CSizeT = len(wh_n.children)
bn_pre_w: sizet = len(wh_n.children)
for bi_pre_w in range(bn_pre_w):
self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
if wh_n.orelse is not None:
bn_pre_we: t.CSizeT = len(wh_n.orelse)
bn_pre_we: sizet = len(wh_n.orelse)
for bi_pre_we in range(bn_pre_we):
self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
return 0
if k == ast.ASTKind.Try:
tr_n: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node)
tr_n: ast.Try* = ast.Try*(node)
if tr_n.children is not None:
bn_pre_t: t.CSizeT = len(tr_n.children)
bn_pre_t: sizet = len(tr_n.children)
for bi_pre_t in range(bn_pre_t):
self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
if tr_n.handlers is not None:
bn_pre_th: t.CSizeT = len(tr_n.handlers)
bn_pre_th: sizet = len(tr_n.handlers)
for bi_pre_th in range(bn_pre_th):
eh_pre: ast.AST | t.CPtr = tr_n.handlers.get(bi_pre_th)
eh_pre: ast.AST* = tr_n.handlers.get(bi_pre_th)
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
ehc_pre: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(eh_pre)
ehc_pre: ast.ExceptHandler* = ast.ExceptHandler*(eh_pre)
if ehc_pre.children is not None:
bn_pre_ehb: t.CSizeT = len(ehc_pre.children)
bn_pre_ehb: sizet = len(ehc_pre.children)
for bi_pre_ehb in range(bn_pre_ehb):
self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
if tr_n.orelse is not None:
bn_pre_to: t.CSizeT = len(tr_n.orelse)
bn_pre_to: sizet = len(tr_n.orelse)
for bi_pre_to in range(bn_pre_to):
self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
if tr_n.finalbody is not None:
bn_pre_tf: t.CSizeT = len(tr_n.finalbody)
bn_pre_tf: sizet = len(tr_n.finalbody)
for bi_pre_tf in range(bn_pre_tf):
self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
return 0
if k == ast.ASTKind.With:
wth: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
wth: ast.With* = ast.With*(node)
if wth.children is not None:
bn_pre_wb: t.CSizeT = len(wth.children)
bn_pre_wb: sizet = len(wth.children)
for bi_pre_wb in range(bn_pre_wb):
self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
return 0
if k == ast.ASTKind.Match:
mt_n: ast.Match | t.CPtr = (ast.Match | t.CPtr)(node)
mt_n: ast.Match* = ast.Match*(node)
if mt_n.cases is not None:
bn_pre_mc: t.CSizeT = len(mt_n.cases)
bn_pre_mc: sizet = len(mt_n.cases)
for bi_pre_mc in range(bn_pre_mc):
mc_pre: ast.AST | t.CPtr = mt_n.cases.get(bi_pre_mc)
mc_pre: ast.AST* = mt_n.cases.get(bi_pre_mc)
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
mcc_pre: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(mc_pre)
mcc_pre: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](mc_pre)
if mcc_pre.children is not None:
bn_pre_mcb: t.CSizeT = len(mcc_pre.children)
bn_pre_mcb: sizet = len(mcc_pre.children)
for bi_pre_mcb in range(bn_pre_mcb):
self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
return 0
return 0
def pre_scan_scope_decls(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def pre_scan_scope_decls(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""预扫描 Global/Nonlocal 声明,提前注册作用域名称"""
if node is None:
return 0
@@ -241,11 +257,11 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
return self.translate_nonlocal(trans, node)
return 0
def translate_break(self, trans: HT.Translator | t.CPtr) -> int:
def translate_break(self, trans: HT.Translator*) -> int:
"""翻译 break 语句:跳转到循环 end 块"""
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
func: llvmlite.Function | t.CPtr = trans._cur_func
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
func: llvmlite.Function* = trans._cur_func
pool: memhub.MemBuddy* = trans.Pool
if builder is None or func is None:
return 0
@@ -260,15 +276,15 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
trans._label_counter = cnt + 1
name_buf: str = pool.alloc(32)
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
dead_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
dead_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
llvmlite.position_at_end(builder, dead_bb)
return 0
def translate_continue(self, trans: HT.Translator | t.CPtr) -> int:
def translate_continue(self, trans: HT.Translator*) -> int:
"""翻译 continue 语句:跳转到循环 cond/incr 块"""
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
func: llvmlite.Function | t.CPtr = trans._cur_func
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
func: llvmlite.Function* = trans._cur_func
pool: memhub.MemBuddy* = trans.Pool
if builder is None or func is None:
return 0
@@ -283,11 +299,11 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
trans._label_counter = cnt + 1
name_buf: str = pool.alloc(32)
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
dead_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
dead_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
llvmlite.position_at_end(builder, dead_bb)
return 0
def get_stmt_kind_name(self, node: ast.AST | t.CPtr) -> str:
def get_stmt_kind_name(self, node: ast.AST*) -> str:
"""获取语句类型名"""
if node is None:
return None
@@ -297,7 +313,7 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
# 簇 D:nonlocal/闭包 env 访问(迁移自 HandlesNonlocal.vp)
# ============================================================
def _is_void_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
def _is_void_type(self, ty: llvmlite.LLVMType*) -> int:
"""判断是否为 Void 类型,是则返回 1,否则 0"""
if ty is None:
return 0
@@ -307,113 +323,112 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
case _:
return 0
def get_env_ptr_var(self, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
def get_env_ptr_var(self, trans: HT.Translator*) -> llvmlite.Value*:
"""获取或创建 _env_ptr 变量(存储闭包 env 指针)"""
env_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, "_env_ptr")
env_var: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
if env_var is not None:
return env_var
# 创建 alloca 存储 env
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i8_ptr_ty)
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
alloca: llvmlite.Value* = llvmlite.build_alloca(builder, i8_ptr_ty)
if alloca is None:
return None
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", alloca)
return alloca
def _load_raw_var_ptr(self, trans: HT.Translator | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
def _load_raw_var_ptr(self, trans: HT.Translator*,
name: str) -> llvmlite.Value*:
"""从闭包 env 加载 nonlocal 变量的原始指针(i8*)
返回 i8* 类型的 Value,调用方需 bitcast 为具体类型指针后 load/store。
"""
idx: int = HT.get_nonlocal_index(trans, name)
idx: int = GenApi.get_nonlocal_index(trans, name)
if idx < 0:
return None
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty)
# 1. 加载 env_ptr
env_alloca: llvmlite.Value | t.CPtr = self.get_env_ptr_var(trans)
env_alloca: llvmlite.Value* = self.get_env_ptr_var(trans)
if env_alloca is None:
return None
env_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
env_ptr: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
if env_ptr is None:
return None
# 2. GEP to offset (idx * 8) — env 存储指针数组,每槽 8 字节
offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, idx * 8)
addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
offset_val: llvmlite.Value* = llvmlite.const_int32(pool, idx * 8)
addr: llvmlite.Value* = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
if addr is None:
return None
# 3. Bitcast to i8** and load the raw pointer
ptr_addr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
ptr_addr: llvmlite.Value* = llvmlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
if ptr_addr is None:
return None
return llvmlite.build_load(builder, i8_ptr_ty, ptr_addr)
def load_nonlocal_var(self, trans: HT.Translator | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
def load_nonlocal_var(self, trans: HT.Translator*,
name: str) -> llvmlite.Value*:
"""从闭包 env 加载 nonlocal 变量值
通过 HT.get_nonlocal_type 获取变量类型,bitcast 原始指针后 load。
"""
raw_ptr: llvmlite.Value | t.CPtr = self._load_raw_var_ptr(trans, name)
raw_ptr: llvmlite.Value* = self._load_raw_var_ptr(trans, name)
if raw_ptr is None:
return None
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
# 获取变量类型(从注册时存储的类型信息)
var_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, name)
var_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
if var_ty is None or self._is_void_type(var_ty) != 0:
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "load void, void*" 非法 IR
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
# bitcast raw i8* to var_ty*
var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty)
var_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
var_ptr: llvmlite.Value* = llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
if var_ptr is None:
return None
return llvmlite.build_load(builder, var_ty, var_ptr)
def get_nonlocal_var_ptr(self, trans: HT.Translator | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
def get_nonlocal_var_ptr(self, trans: HT.Translator*,
name: str) -> llvmlite.Value*:
"""获取 nonlocal 变量的存储指针(var_ty*),用于 store 操作
返回的指针指向父函数的 alloca,store 操作对父函数可见。
"""
raw_ptr: llvmlite.Value | t.CPtr = self._load_raw_var_ptr(trans, name)
raw_ptr: llvmlite.Value* = self._load_raw_var_ptr(trans, name)
if raw_ptr is None:
return None
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
# 获取变量类型
var_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, name)
var_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
if var_ty is None or self._is_void_type(var_ty) != 0:
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "bitcast ... to void*" 非法 IR
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
# bitcast raw i8* to var_ty*
var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty)
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
return llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
# ============================================================
# 簇 E:枚举注册表(迁移自 HandlesEnum.vp)
# ============================================================
def init_enum_table(self, pool: memhub.MemBuddy | t.CPtr) -> int:
def init_enum_table(self, pool: memhub.MemBuddy*) -> int:
"""初始化枚举注册表,返回 1 成功
表用 stdlib 分配(跨 Phase1 1b 的 mark/reset_to 存活——枚举表是
@@ -423,77 +438,72 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
if self.EnumTable is not None:
return 1
entry_size: t.CSizeT = GenTypes.EnumEntry.__sizeof__()
self.EnumTable = CompilerFactory.get_perm_pool().alloc(entry_size * ENUM_MAX)
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
self.EnumTable = CompilerFactory.factory.PermPool.alloc(entry_size * ENUM_MAX)
if self.EnumTable is None:
return 0
string.memset(self.EnumTable, 0, entry_size * ENUM_MAX)
self.EnumCount = 0
return 1
def _get_enum_entry(self, i: int) -> GenTypes.EnumEntry | t.CPtr:
def _get_enum_entry(self, i: int) -> rawptr[GenTypes.EnumEntry]:
"""获取第 i 个枚举条目"""
if self.EnumTable is None or i < 0 or i >= ENUM_MAX:
return None
entry_size: t.CSizeT = GenTypes.EnumEntry.__sizeof__()
addr: t.CUInt64T = t.CUInt64T(self.EnumTable) + i * entry_size
return (GenTypes.EnumEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
addr: uint64 = uint64(self.EnumTable) + i * entry_size
return rawptr[GenTypes.EnumEntry](addr)
def _get_enum_member(self, enum_entry: GenTypes.EnumEntry | t.CPtr,
i: int) -> GenTypes.EnumMember | t.CPtr:
def _get_enum_member(self, enum_entry: rawptr[GenTypes.EnumEntry],
i: int) -> rawptr[GenTypes.EnumMember]:
"""获取枚举中第 i 个成员条目"""
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX:
return None
member_size: t.CSizeT = GenTypes.EnumMember.__sizeof__()
addr: t.CUInt64T = t.CUInt64T(enum_entry.Members) + i * member_size
return (GenTypes.EnumMember | t.CPtr)(t.CVoid(addr, t.CPtr))
member_size: sizet = GenTypes.EnumMember.__sizeof__()
addr: uint64 = uint64(enum_entry.Members) + i * member_size
return rawptr[GenTypes.EnumMember](addr)
def register_enum(self, pool: memhub.MemBuddy | t.CPtr,
def register_enum(self, pool: memhub.MemBuddy*,
name: str,
base_ty: llvmlite.LLVMType | t.CPtr) -> GenTypes.EnumEntry | t.CPtr:
base_ty: llvmlite.LLVMType*) -> rawptr[GenTypes.EnumEntry]:
"""注册枚举类型,返回 EnumEntry 指针"""
if self.init_enum_table(pool) == 0:
return None
# 检查是否已注册
existing: GenTypes.EnumEntry | t.CPtr = self.find_enum(name)
existing: rawptr[GenTypes.EnumEntry] = self.find_enum(name)
if existing is not None:
return existing
if self.EnumCount >= ENUM_MAX:
fb_et: str = 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")
VLogger.error(f"table full, cannot register {name}", "ENUM")
return None
entry: GenTypes.EnumEntry | t.CPtr = self._get_enum_entry(self.EnumCount)
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(self.EnumCount)
if entry is None:
return None
# 分配成员数组(永久池:跨 mark/reset 存活,对齐 EnumTable 永久池化)
member_size: t.CSizeT = GenTypes.EnumMember.__sizeof__()
entry.Members = CompilerFactory.get_perm_pool().alloc(member_size * ENUM_MEMBER_MAX)
member_size: sizet = GenTypes.EnumMember.__sizeof__()
entry.Members = CompilerFactory.factory.PermPool.alloc(member_size * ENUM_MEMBER_MAX)
if entry.Members is None:
return None
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
# 复制类名(永久池深拷贝)
name_len: t.CSizeT = string.strlen(name)
name_buf: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
name_len: sizet = string.strlen(name)
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if name_buf is not None:
string.strcpy(name_buf, name)
entry.Name = name_buf
# BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1(Phase2 mark2 后)
# 时 reset 回收悬空 → Phase B 枚举访问 ConstInt(悬空 BaseTy) 生成 void
# 值(DecoratorMarkerTest `store void 2` llc 报错根因)。Phase1 1a
# (StdlibAlloc=0,mark1 前)保持 pool 存活,无需拷贝。
# 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接
# self.StdlibAlloc(NoVTable 按本 Mixin 字段布局编译),走模块级委托。
# BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1 时 reset 回收悬空 → Phase B
# 枚举访问 ConstInt(悬空 BaseTy) 生成 void 值(llc 报错)。Phase1 1a(mark1 前)
# 保持 pool 存活,无需拷贝。
# 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接 self.StdlibAlloc
# (NoVTable 按本 Mixin 字段布局编译),走模块级委托。
if self.Gen.StructGen.get_stdlib_alloc() != 0:
base_ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
CompilerFactory.get_perm_pool(), base_ty)
base_ty_copy: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
if base_ty_copy is not None:
entry.BaseTy = base_ty_copy
else:
@@ -505,11 +515,11 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
self.EnumCount += 1
return entry
def add_enum_member(self, pool: memhub.MemBuddy | t.CPtr,
enum_entry: GenTypes.EnumEntry | t.CPtr,
def add_enum_member(self, pool: memhub.MemBuddy*,
enum_entry: rawptr[GenTypes.EnumEntry],
member_name: str,
member_val: t.CInt64T,
member_ty: llvmlite.LLVMType | t.CPtr) -> int:
member_val: int64,
member_ty: llvmlite.LLVMType*) -> int:
"""向枚举添加成员,返回成员索引(-1 失败)"""
if enum_entry is None or member_name is None or member_ty is None:
return -1
@@ -517,27 +527,23 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
# 去重:同名成员已存在则跳过(跨模块同名枚举合并注册 / 同模块
# 重复处理幂等——如 w32.fileio 与 fat32_types 都有 FRESULT)
for ei_d in range(enum_entry.MemberCount):
ex_d: GenTypes.EnumMember | t.CPtr = self._get_enum_member(
enum_entry, ei_d)
ex_d: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, ei_d)
if ex_d is not None and ex_d.Name is not None:
if string.strcmp(ex_d.Name, member_name) == 0:
return ei_d
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
fb_em: str = 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")
VLogger.error(f"member table full for {enum_entry.Name}", "ENUM")
return -1
idx: int = enum_entry.MemberCount
me: GenTypes.EnumMember | t.CPtr = self._get_enum_member(enum_entry, idx)
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, idx)
if me is None:
return -1
# 复制成员名(永久池深拷贝:成员表跨 mark/reset 存活)
name_len: t.CSizeT = string.strlen(member_name)
name_buf: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
name_len: sizet = string.strlen(member_name)
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if name_buf is not None:
string.strcpy(name_buf, member_name)
me.Name = name_buf
@@ -548,27 +554,27 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
enum_entry.MemberCount = idx + 1
return idx
def find_enum(self, name: str) -> GenTypes.EnumEntry | t.CPtr:
def find_enum(self, name: str) -> rawptr[GenTypes.EnumEntry]:
"""按枚举类名查找,返回 EnumEntry 或 None"""
if name is None or self.EnumTable is None:
return None
for i in range(self.EnumCount):
entry: GenTypes.EnumEntry | t.CPtr = self._get_enum_entry(i)
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(i)
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
return None
def lookup_enum_member(self, enum_name: str,
member_name: str) -> GenTypes.EnumMember | t.CPtr:
member_name: str) -> rawptr[GenTypes.EnumMember]:
"""按枚举类名和成员名查找,返回 EnumMember 或 None"""
if enum_name is None or member_name is None:
return None
entry: GenTypes.EnumEntry | t.CPtr = self.find_enum(enum_name)
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
if entry is None:
return None
for mi in range(entry.MemberCount):
me: GenTypes.EnumMember | t.CPtr = self._get_enum_member(entry, mi)
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(entry, mi)
if me is not None and me.Name is not None:
if string.strcmp(me.Name, member_name) == 0:
return me
@@ -582,9 +588,9 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
return 1
return 0
def get_enum_base_type(self, enum_name: str) -> llvmlite.LLVMType | t.CPtr:
def get_enum_base_type(self, enum_name: str) -> llvmlite.LLVMType*:
"""按枚举类名获取基准类型"""
entry: GenTypes.EnumEntry | t.CPtr = self.find_enum(enum_name)
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
if entry is not None:
return entry.BaseTy
return None
@@ -1,6 +1,6 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.CallGenFd as CallGenFd
import lib.core.Gen.Call.CallGenFd as CallGenFd
import t, c
from stdint import *
import ast
@@ -11,49 +11,44 @@ import stdlib
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
import lib.Projectrans.Config as Config
import lib.core.Gen.StmtGenDeclMixin as StmtGenDeclMixin
import lib.core.Gen.Stmt.StmtGenDeclMixin as StmtGenDeclMixin
import lib.core.Gen.DebugInfoGen as DebugInfoGen
# ============================================================
# StmtGenMixin - 语句/流程翻译 Mixin(P6 对象化重构 A 组,拆分后顶层)
#
# 按功能域拆分(500~1500 行/文件架构重构,试点):
# 顶层保留语句分派中枢簇 A(translate_stmt/try/raise/with/delete/
# _eh_ret_default/_with_ctx_call);global/nonlocal/枚举/CIf 已拆至
# StmtGenDeclMixin / StmtGenCifMixin(本类继承链 1/3、2/3)。
# 外部引用(Generator.vp import + HandlesBody 委托)零改动。
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# StmtGenMixin - 语句分派中枢(顶层)
# ============================================================
@t.NoVTable
class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
"""语句/流程翻译 Mixin(拆分后顶层,继承 Decl)"""
def translate_stmt(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_stmt(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译单条语句,返回新增的变量数(深度守卫包装器,原逻辑见 _translate_stmt_impl)"""
if trans is not None:
# --traceback: statement-level DWARF location (no-op outside a
# DI-attached define or when DI is disabled). 此后本语句生成的每条
# 指令自动带 ", !dbg !N",保证函数体内每个 call 都有合法 !dbg。
if node is not None and trans._cur_builder is not None:
DebugInfoGen.SetStatement(trans.Pool, trans._cur_builder,
node.lineno, node.col_offset + 1)
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
self.Gen.TypeGenErr.fatal_error(
node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
self.Gen.TypeGenErr.fatal_error(node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
trans.TranslateDepth += 1
mret: int = self._translate_stmt_impl(trans, node)
if trans is not None:
trans.TranslateDepth -= 1
return mret
def _translate_stmt_impl(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def _translate_stmt_impl(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译单条语句,返回新增的变量数"""
if node is None:
return 0
@@ -109,68 +104,61 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
self.Gen.TypeGenErr.fatal_error(node, f"未知语句类型 kind={k}", "SyntaxError")
return 0
def translate_delete(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_delete(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 del 语句,返回 0"""
if trans is None or node is None:
return 0
dnode: ast.Delete | t.CPtr = (ast.Delete | t.CPtr)(node)
dnode: rawptr[ast.Delete] = rawptr[ast.Delete](node)
if dnode is None or dnode.targets is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
builder: llvmlite.IRBuilder* = trans._cur_builder
if pool is None or mod is None or builder is None:
return 0
tn: t.CSizeT = len(dnode.targets)
tn: sizet = len(dnode.targets)
for ti in range(tn):
tgt: ast.AST | t.CPtr = dnode.targets.get(ti)
tgt: ast.AST* = dnode.targets.get(ti)
if tgt is None:
continue
# del obj.attr → property deleter
if tgt.kind() == ast.ASTKind.Attribute:
del_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(tgt)
del_at: ast.Attribute* = ast.Attribute*(tgt)
if del_at is not None:
self.Gen.CallGenProp._try_property_deleter(
pool, builder, mod, del_at, trans)
# del obj[key] → __delitem__(obj, key)
# (对齐 TPC HandlesDelete.py L14-25;此前 Subscript 目标被静默
# 忽略,HashTableTest Test 11 的 del ht3[del_buf] 未执行 → len 不变)
self.Gen.CallGenProp._try_property_deleter(pool, builder, mod, del_at, trans)
# del obj[key] → __delitem__(obj, key)(对齐 TPC HandlesDelete.py)。
# 不能静默忽略 Subscript 目标——否则 del 不生效(len 不变)。
elif tgt.kind() == ast.ASTKind.Subscript:
dl_sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(tgt)
dl_sub: ast.Subscript* = ast.Subscript*(tgt)
if dl_sub is not None and dl_sub.value is not None and dl_sub.slice is not None:
dl_obj: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, dl_sub.value, None, 0, trans)
dl_idx: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, dl_sub.slice, None, 0, trans)
dl_obj: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, dl_sub.value, None, 0, trans)
dl_idx: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, dl_sub.slice, None, 0, trans)
if dl_obj is not None and dl_idx is not None:
dl_st: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type_from_value(
dl_obj)
dl_st: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(dl_obj)
if dl_st is not None:
dl_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, dl_st)
if dl_cls is not None:
dl_lookup: str = pool.alloc(128)
if dl_lookup is not None:
viperlib.snprintf(dl_lookup, 128, "%s.__delitem__", dl_cls)
dl_fn: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module(
mod, dl_lookup)
dl_fn: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, dl_lookup)
if dl_fn is not None:
dl_ret: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(dl_fn)
dl_ret: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(dl_fn)
dl_fn_name: str = llvmlite.function_get_name(dl_fn)
if dl_ret is not None and dl_fn_name is not None:
# self 参数(容器)按 __delitem__ 首参类型对齐
dl_self: llvmlite.Value | t.CPtr = dl_obj
dl_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(dl_fn)
dl_self: llvmlite.Value* = dl_obj
dl_first: llvmlite.Param* = llvmlite.function_get_param_head(dl_fn)
if dl_first is not None:
dl_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(dl_first)
dl_self_ty: llvmlite.LLVMType* = llvmlite.param_get_ty(dl_first)
if dl_self_ty is not None:
dl_self = llvmlite.build_bitcast(builder, dl_obj, dl_self_ty)
if dl_self is not None:
# 参数链表: self → key(独立 null 结尾)
dl_self.Next = dl_idx
dl_idx.Next = None
llvmlite.build_call(
builder, dl_fn_name, dl_self, 2, dl_ret, 0)
llvmlite.build_call(builder, dl_fn_name, dl_self, 2, dl_ret, 0)
return 0
# ============================================================
@@ -188,34 +176,34 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# v1 未实现 finalbody(TestFBProject 未使用;finally+return 交互复杂,
# 后续按 TPC HandlesTry.py L188-214 的 pending_ret 方案扩展)。
# ============================================================
def translate_try(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_try(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 try/except 语句,返回 0"""
if trans is None or node is None:
return 0
tr: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node)
tr: ast.Try* = ast.Try*(node)
if tr is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
func: llvmlite.Function | t.CPtr = trans._cur_func
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
func: llvmlite.Function* = trans._cur_func
if pool is None or builder is None or func is None:
return 0
if llvmlite.builder_cur_block_is_terminated(builder) != 0:
return 0
# ---- 1. 异常编码/消息 alloca(当前 try 上下文)----
i32_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_t)
i32_ty_t: llvmlite.LLVMType* = llvmlite.Int32(pool)
i8_ty_t: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_t: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_t)
if i32_ty_t is None or i8_ptr_ty_t is None:
return 0
exception_code: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i32_ty_t)
eh_message: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i8_ptr_ty_t)
exception_code: llvmlite.Value* = llvmlite.build_alloca(builder, i32_ty_t)
eh_message: llvmlite.Value* = llvmlite.build_alloca(builder, i8_ptr_ty_t)
if exception_code is None or eh_message is None:
return 0
init_code_t: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
init_msg_t: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty_t, "null")
init_code_t: llvmlite.Value* = llvmlite.const_int32(pool, 0)
init_msg_t: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty_t, "null")
if init_code_t is not None:
llvmlite.build_store(builder, init_code_t, exception_code)
if init_msg_t is not None:
@@ -228,43 +216,43 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
if bb_buf_t is None:
return 0
viperlib.snprintf(bb_buf_t, 64, "try.dispatch.%d", lc_t)
dispatch_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
dispatch_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, bb_buf_t)
viperlib.snprintf(bb_buf_t, 64, "try.after.%d", lc_t)
after_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
after_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, bb_buf_t)
viperlib.snprintf(bb_buf_t, 64, "try.unhandled.%d", lc_t)
unhandled_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
unhandled_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, bb_buf_t)
viperlib.snprintf(bb_buf_t, 64, "try.body.%d", lc_t)
try_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
try_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, bb_buf_t)
if dispatch_bb is None or after_bb is None or unhandled_bb is None or try_bb is None:
return 0
else_bb: llvmlite.BasicBlock | t.CPtr = None
else_bb: rawptr[llvmlite.BasicBlock] = None
if tr.orelse is not None and len(tr.orelse) > 0:
viperlib.snprintf(bb_buf_t, 64, "try.else.%d", lc_t)
else_bb = llvmlite.create_block(pool, func, bb_buf_t)
n_handlers_t: t.CSizeT = 0
n_handlers_t: sizet = 0
if tr.handlers is not None:
n_handlers_t = len(tr.handlers)
# 注意:list[ptr](pool, N) 第二参数是 elem_size 不是容量!
# 指针元素必须显式传 8(T.__sizeof__ 对联合类型不可靠),
# 否则 append 按 N 字节步长错位存取 → 读出垃圾指针(崩溃)。
except_bbs: list[llvmlite.BasicBlock | t.CPtr] | t.CPtr = \
list[llvmlite.BasicBlock | t.CPtr](pool, 8)
match_bbs: list[llvmlite.BasicBlock | t.CPtr] | t.CPtr = \
list[llvmlite.BasicBlock | t.CPtr](pool, 8)
except_bbs: list[rawptr[llvmlite.BasicBlock]] | t.CPtr = \
list[rawptr[llvmlite.BasicBlock]](pool, 8)
match_bbs: list[rawptr[llvmlite.BasicBlock]] | t.CPtr = \
list[rawptr[llvmlite.BasicBlock]](pool, 8)
if except_bbs is None or match_bbs is None:
return 0
for i_t in range(n_handlers_t):
viperlib.snprintf(bb_buf_t, 64, "try.except.%d.%d", lc_t, i_t)
eb_t: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
eb_t: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, bb_buf_t)
viperlib.snprintf(bb_buf_t, 64, "try.match.%d.%d", lc_t, i_t)
mb_t: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
mb_t: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, bb_buf_t)
except_bbs.append(eb_t)
match_bbs.append(mb_t)
# ---- 3. 保存外层 try 上下文,设置本 try 上下文 ----
old_dispatch_t: llvmlite.BasicBlock | t.CPtr = trans._eh_dispatch_bb
old_code_t: llvmlite.Value | t.CPtr = trans._eh_code_ptr
old_msg_t: llvmlite.Value | t.CPtr = trans._eh_msg_ptr
old_dispatch_t: rawptr[llvmlite.BasicBlock] = trans._eh_dispatch_bb
old_code_t: llvmlite.Value* = trans._eh_code_ptr
old_msg_t: llvmlite.Value* = trans._eh_msg_ptr
trans._eh_dispatch_bb = dispatch_bb
trans._eh_code_ptr = exception_code
trans._eh_msg_ptr = eh_message
@@ -273,9 +261,9 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
llvmlite.build_br(builder, try_bb)
llvmlite.position_at_end(builder, try_bb)
if tr.children is not None:
tb_n: t.CSizeT = len(tr.children)
tb_n: sizet = len(tr.children)
for tbi in range(tb_n):
stmt_t: ast.AST | t.CPtr = tr.children.get(tbi)
stmt_t: ast.AST* = tr.children.get(tbi)
if stmt_t is not None:
self.translate_stmt(trans, stmt_t)
# 正常走完 try 体:清异常码,进 else(若有)或 After
@@ -293,39 +281,34 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# 无 except(非法 try,防御):直接吞掉
llvmlite.build_br(builder, unhandled_bb)
else:
eh_code_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i32_ty_t, exception_code)
next_check_t: llvmlite.BasicBlock | t.CPtr = None
eh_code_val: llvmlite.Value* = llvmlite.build_load(builder, i32_ty_t, exception_code)
next_check_t: rawptr[llvmlite.BasicBlock] = None
for i_t in range(n_handlers_t):
hdr_t: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(
tr.handlers.get(i_t))
match_bb_t: llvmlite.BasicBlock | t.CPtr = match_bbs.get(i_t)
hdr_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
match_bb_t: rawptr[llvmlite.BasicBlock] = match_bbs.get(i_t)
if i_t > 0 and next_check_t is not None:
llvmlite.position_at_end(builder, next_check_t)
eh_code_val = llvmlite.build_load(
builder, i32_ty_t, exception_code)
eh_code_val = llvmlite.build_load(builder, i32_ty_t, exception_code)
if hdr_t is None or hdr_t.type is None:
# 裸 except:无条件匹配
llvmlite.build_br(builder, match_bb_t)
else:
# 展开匹配编码集(handler.type 自身 + 全部子孙异常类)
hname_t: str = None
htype_t: ast.AST | t.CPtr = hdr_t.type
htype_t: ast.AST* = hdr_t.type
if htype_t.kind() == ast.ASTKind.Name:
hname_t = ((ast.Name | t.CPtr)(htype_t)).id
hname_t = (ast.Name*(htype_t)).id
elif htype_t.kind() == ast.ASTKind.Attribute:
hname_t = ((ast.Attribute | t.CPtr)(htype_t)).attr
# 匹配编码缓冲:必须是 t.CInt*(i32 步长)——collect_descendant_codes
# 按 out_codes[i](4 字节)写入;若用 t.CSizeT*(8 字节)读,
# 多码时读出 (code[i+1]<<32)|code[i] 的 64 位拼接垃圾(单码时
# 高位恰为 0 掩盖;Test 2/3/8 父类多码捕获失败的根因)。
codes_buf_t: t.CInt | t.CPtr = pool.alloc(64 * 4)
hname_t = (ast.Attribute*(htype_t)).attr
# 匹配编码缓冲:必须是 int*(i32 步长)——collect_descendant_codes 按
# out_codes[i](4 字节)写入;用 sizet*(8 字节)读会读出 64 位拼接垃圾
# (多码时 code[i+1]<<32|code[i];单码时高位恰为 0 掩盖)。
codes_buf_t: int | t.CPtr = pool.alloc(64 * 4)
if codes_buf_t is None:
return 0
n_codes_t: int = 0
if hname_t is not None and self.Gen.ClassGenBase.is_exception_name(hname_t) != 0:
n_codes_t = self.Gen.ClassGenBase.collect_descendant_codes(
hname_t, codes_buf_t, 64)
n_codes_t = self.Gen.ClassGenBase.collect_descendant_codes(hname_t, codes_buf_t, 64)
else:
c0_t: int = 1
if hname_t is not None:
@@ -334,24 +317,20 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
c0_t = 1
codes_buf_t[0] = c0_t
n_codes_t = 1
match_cond_t: llvmlite.Value | t.CPtr = None
match_cond_t: llvmlite.Value* = None
for ci_t in range(n_codes_t):
code_val_t: llvmlite.Value | t.CPtr = llvmlite.const_int32(
pool, codes_buf_t[ci_t])
is_match_t: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_EQ, eh_code_val, code_val_t)
code_val_t: llvmlite.Value* = llvmlite.const_int32(pool, codes_buf_t[ci_t])
is_match_t: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, eh_code_val, code_val_t)
if match_cond_t is None:
match_cond_t = is_match_t
elif is_match_t is not None:
match_cond_t = llvmlite.build_or(
builder, match_cond_t, is_match_t)
match_cond_t = llvmlite.build_or(builder, match_cond_t, is_match_t)
if i_t < n_handlers_t - 1:
viperlib.snprintf(bb_buf_t, 64, "try.check.%d.%d", lc_t, i_t)
next_check_t = llvmlite.create_block(pool, func, bb_buf_t)
else:
next_check_t = unhandled_bb
llvmlite.build_cond_br(
builder, match_cond_t, match_bb_t, next_check_t)
llvmlite.build_cond_br(builder, match_cond_t, match_bb_t, next_check_t)
# MatchBB:绑定 handler 变量(e = 消息指针),跳 ExceptBB
llvmlite.position_at_end(builder, match_bb_t)
if hdr_t is not None and hdr_t.name is not None and hdr_t.name[0] != '\0':
@@ -359,30 +338,26 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# 重复 define_var 不 shadow——多 try 共用 `as e` 时各 MatchBB
# 各自 store 消息,各 ExceptBB 读同一入口 alloca,避免
# 非支配 alloca 触发 "does not dominate all uses")。
h_ent_t: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, hdr_t.name)
h_ent_t: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, hdr_t.name)
if h_ent_t is not None and h_ent_t.Alloca is not None:
h_alloca_t: llvmlite.Value | t.CPtr = h_ent_t.Alloca
h_alloca_t: llvmlite.Value* = h_ent_t.Alloca
else:
h_alloca_t = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_t)
if h_alloca_t is not None:
self.Gen.VarGen.define_var(trans.SymTab, hdr_t.name, h_alloca_t)
if h_alloca_t is not None:
h_msg_t: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i8_ptr_ty_t, eh_message)
h_msg_t: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty_t, eh_message)
if h_msg_t is not None:
llvmlite.build_store(builder, h_msg_t, h_alloca_t)
except_bb_t: llvmlite.BasicBlock | t.CPtr = except_bbs.get(i_t)
except_bb_t: rawptr[llvmlite.BasicBlock] = except_bbs.get(i_t)
llvmlite.build_br(builder, except_bb_t)
# ---- 6. UnhandledBB:无 except 匹配 → 向上传播或吞掉 ----
llvmlite.position_at_end(builder, unhandled_bb)
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \
and trans._eh_code_out_param is not None:
un_msg_t: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i8_ptr_ty_t, eh_message)
un_code_t: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, i32_ty_t, exception_code)
un_msg_t: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty_t, eh_message)
un_code_t: llvmlite.Value* = llvmlite.build_load(builder, i32_ty_t, exception_code)
if un_msg_t is not None:
llvmlite.build_store(builder, un_msg_t, trans._eh_msg_out_param)
if un_code_t is not None:
@@ -395,14 +370,13 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# ---- 7. Except 体 ----
for i_t in range(n_handlers_t):
hdr2_t: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(
tr.handlers.get(i_t))
except_bb2_t: llvmlite.BasicBlock | t.CPtr = except_bbs.get(i_t)
hdr2_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
except_bb2_t: rawptr[llvmlite.BasicBlock] = except_bbs.get(i_t)
llvmlite.position_at_end(builder, except_bb2_t)
if hdr2_t is not None and hdr2_t.children is not None:
hn2_t: t.CSizeT = len(hdr2_t.children)
hn2_t: sizet = len(hdr2_t.children)
for hi2 in range(hn2_t):
stmt2_t: ast.AST | t.CPtr = hdr2_t.children.get(hi2)
stmt2_t: ast.AST* = hdr2_t.children.get(hi2)
if stmt2_t is not None:
self.translate_stmt(trans, stmt2_t)
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
@@ -412,9 +386,9 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
if else_bb is not None:
llvmlite.position_at_end(builder, else_bb)
if tr.orelse is not None:
en_t: t.CSizeT = len(tr.orelse)
en_t: sizet = len(tr.orelse)
for ei in range(en_t):
stmt3_t: ast.AST | t.CPtr = tr.orelse.get(ei)
stmt3_t: ast.AST* = tr.orelse.get(ei)
if stmt3_t is not None:
self.translate_stmt(trans, stmt3_t)
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
@@ -427,8 +401,8 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
trans._eh_msg_ptr = old_msg_t
return 0
def translate_raise(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_raise(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 raise 语句,返回 0
对齐 TPC HandlesRaise.py 的 try-except 语义(v1 迁移):
@@ -442,12 +416,12 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
"""
if trans is None or node is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
mod: llvmlite.LLVMModule* = trans.Module
if pool is None or builder is None or mod is None:
return 0
rn: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(node)
rn: rawptr[ast.Raise] = rawptr[ast.Raise](node)
if rn is None or rn.exc is None:
# bare raise(无 exc):v1 无活动异常可再抛,仅返回默认值
self._eh_ret_default(trans, pool, builder)
@@ -458,61 +432,57 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
ek: int = rn.exc.kind()
is_stop_iter: int = 0
if ek == ast.ASTKind.Name:
enm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn.exc)
enm: ast.Name* = ast.Name*(rn.exc)
if enm.id is not None and string.strcmp(enm.id, "StopIteration") == 0:
is_stop_iter = 1
elif ek == ast.ASTKind.Call:
ecl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn.exc)
ecl: ast.Call* = ast.Call*(rn.exc)
if ecl.func is not None and ecl.func.kind() == ast.ASTKind.Name:
ecn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl.func)
ecn: ast.Name* = ast.Name*(ecl.func)
if ecn.id is not None and string.strcmp(ecn.id, "StopIteration") == 0:
is_stop_iter = 1
if is_stop_iter != 0:
if trans._stop_iter_flag_ptr is not None:
i1_ty_r: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
i1_ty_r: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_r is not None:
one_i1_r: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
pool, i1_ty_r, 1, "1")
one_i1_r: llvmlite.Value* = llvmlite.ConstInt(pool, i1_ty_r, 1, "1")
if one_i1_r is not None:
llvmlite.build_store(
builder, one_i1_r, trans._stop_iter_flag_ptr)
llvmlite.build_store(builder, one_i1_r, trans._stop_iter_flag_ptr)
self._eh_ret_default(trans, pool, builder)
return 0
# ---- 非 StopIteration:计算异常编码与消息 ----
exc_code: int = 1
exc_msg: llvmlite.Value | t.CPtr = None
exc_msg: llvmlite.Value* = None
if ek == ast.ASTKind.Constant:
cst_r: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rn.exc)
cst_r: ast.Constant* = ast.Constant*(rn.exc)
if cst_r is not None and cst_r.const_kind == ast.CONST_INT:
exc_code = cst_r.int_val
elif ek == ast.ASTKind.Name:
enm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn.exc)
enm2: ast.Name* = ast.Name*(rn.exc)
if enm2.id is not None:
exc_code = self.Gen.ClassGenBase.lookup_exception_code(enm2.id)
if exc_code == 0:
exc_code = 1
elif ek == ast.ASTKind.Call:
ecl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn.exc)
ecl2: ast.Call* = ast.Call*(rn.exc)
if ecl2.func is not None and ecl2.func.kind() == ast.ASTKind.Name:
ecn2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl2.func)
ecn2: ast.Name* = ast.Name*(ecl2.func)
if ecn2.id is not None:
exc_code = self.Gen.ClassGenBase.lookup_exception_code(ecn2.id)
if exc_code == 0:
exc_code = 1
if ecl2.args is not None and len(ecl2.args) > 0:
exc_msg = self.Gen.ExprGen.translate_value(
builder, pool, mod, ecl2.args.get(0), None, 0, trans)
exc_msg = self.Gen.ExprGen.translate_value(builder, pool, mod, ecl2.args.get(0), None, 0, trans)
# ---- 异常分发 ----
# 1. try 内:写入 try 的 eh_code/eh_message alloca,分支 DispatchBB
if trans._eh_dispatch_bb is not None and trans._eh_code_ptr is not None:
code_val_r: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, exc_code)
code_val_r: llvmlite.Value* = llvmlite.const_int32(pool, exc_code)
if code_val_r is not None:
llvmlite.build_store(builder, code_val_r, trans._eh_code_ptr)
if exc_msg is None:
exc_msg = llvmlite.ConstNull(
pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null")
exc_msg = llvmlite.ConstNull(pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null")
if exc_msg is not None and trans._eh_msg_ptr is not None:
llvmlite.build_store(builder, exc_msg, trans._eh_msg_ptr)
llvmlite.build_br(builder, trans._eh_dispatch_bb)
@@ -520,12 +490,11 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# 2. 函数有 EH 输出参数:写入 out 参数(异常向调用方传播)
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \
and trans._eh_code_out_param is not None:
code_val_r2: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, exc_code)
code_val_r2: llvmlite.Value* = llvmlite.const_int32(pool, exc_code)
if code_val_r2 is not None:
llvmlite.build_store(builder, code_val_r2, trans._eh_code_out_param)
if exc_msg is None:
exc_msg = llvmlite.ConstNull(
pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null")
exc_msg = llvmlite.ConstNull(pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null")
if exc_msg is not None:
llvmlite.build_store(builder, exc_msg, trans._eh_msg_out_param)
self._eh_ret_default(trans, pool, builder)
@@ -541,33 +510,33 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# (void→ret_void,ptr→null,float→0.0,其余→零值)。当前块已
# 终结(前一条是 raise/return)时跳过,避免在终止块后插指令。
# ============================================================
def _eh_ret_default(self, trans: HT.Translator | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr) -> int:
def _eh_ret_default(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*) -> int:
"""异常路径返回当前函数默认零值,返回 0"""
if trans is None or builder is None:
return 0
if llvmlite.builder_cur_block_is_terminated(builder) != 0:
return 0
cur_func: llvmlite.Function | t.CPtr = trans._cur_func
cur_func: llvmlite.Function* = trans._cur_func
if cur_func is None:
return 0
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func)
ret_ty: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(cur_func)
if ret_ty is None:
return 0
match ret_ty:
case llvmlite.LLVMType.Void():
llvmlite.build_ret_void(builder)
case llvmlite.LLVMType.Ptr(_pe):
null_v: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null")
null_v: llvmlite.Value* = llvmlite.ConstNull(pool, ret_ty, "null")
if null_v is not None:
llvmlite.build_ret(builder, null_v)
case llvmlite.LLVMType.Float(_fb):
zero_f: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, ret_ty, 0.0)
zero_f: llvmlite.Value* = llvmlite.ConstFloat(pool, ret_ty, 0.0)
if zero_f is not None:
llvmlite.build_ret(builder, zero_f)
case _:
zero_v: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, ret_ty)
zero_v: llvmlite.Value* = llvmlite.ConstZero(pool, ret_ty)
if zero_v is not None:
llvmlite.build_ret(builder, zero_v)
return 0
@@ -588,64 +557,61 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# 退出后恢复,避免内层覆盖导致外层 __exit__ 解析到错误对象。
# 对齐 TPC HandlesWith 语义(不含 __provides__ provider 机制)。
# ============================================================
def translate_with(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
def translate_with(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 with 语句,返回 0"""
if trans is None or node is None:
return 0
wt: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
wt: ast.With* = ast.With*(node)
if wt is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
mod: llvmlite.LLVMModule* = trans.Module
if pool is None or builder is None or mod is None:
return 0
items: list[ast.AST | t.CPtr] | t.CPtr = wt.items
items: list[ast.AST*]* = wt.items
if items is None:
return 0
n: t.CSizeT = len(items)
n: sizet = len(items)
# 保存外层 __with_ctx(嵌套 with 时退出后恢复)
old_ctx_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, "__with_ctx")
old_ctx_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "__with_ctx")
# 记录每个 item 的类名(__exit__ 调用时设置 __with_ctx 类型用)
ctx_class_buf: t.CSizeT | t.CPtr = pool.alloc(8 * n)
ctx_class_buf: sizet* = pool.alloc(8 * n)
if ctx_class_buf is None:
return 0
string.memset(ctx_class_buf, 0, 8 * n)
# 1. 全部 item:构造 context 对象 + 调用 __enter__ + asname 绑定
for i in range(n):
item_node: ast.AST | t.CPtr = items.get(i)
item_node: ast.AST* = items.get(i)
if item_node is None:
continue
if item_node.kind() != ast.ASTKind.WithItem:
continue
item: ast.WithItem | t.CPtr = (ast.WithItem | t.CPtr)(item_node)
item: rawptr[ast.WithItem] = rawptr[ast.WithItem](item_node)
if item is None or item.context_expr is None:
continue
# 翻译 context_expr(对象构造,如 MemPool(arena, 4096))
ctx_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, item.context_expr,
trans._funcs, trans._func_count, trans)
ctx_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, item.context_expr, trans._funcs, trans._func_count, trans)
if ctx_val is None:
continue
# 提取类名(注解类名,Attribute 时带模块前缀,如 "memhub.MemPool"——
# 与 AnnAssign 的 extract_class_name_from_annotation 一致,跨模块
# 方法调用靠 AnnotClassName 回退按模块名查 SHA1)
cls_name: str = None
ce: ast.AST | t.CPtr = item.context_expr
ce: ast.AST* = item.context_expr
if ce.kind() == ast.ASTKind.Call:
cfn: ast.AST | t.CPtr = ((ast.Call | t.CPtr)(ce)).func
cfn: ast.AST* = (ast.Call*(ce)).func
if cfn is not None:
if cfn.kind() == ast.ASTKind.Attribute:
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cfn)
ca: ast.Attribute* = ast.Attribute*(cfn)
if ca.value is not None and ca.value.kind() == ast.ASTKind.Name:
cmn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ca.value)
cmn: ast.Name* = ast.Name*(ca.value)
if cmn.id is not None:
ml2: t.CSizeT = string.strlen(cmn.id) + string.strlen(ca.attr) + 2
ml2: sizet = string.strlen(cmn.id) + string.strlen(ca.attr) + 2
mbuf: str = pool.alloc(ml2)
if mbuf is not None:
viperlib.snprintf(mbuf, ml2, "%s.%s", cmn.id, ca.attr)
@@ -653,39 +619,34 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
if cls_name is None:
cls_name = ca.attr
elif cfn.kind() == ast.ASTKind.Name:
cls_name = ((ast.Name | t.CPtr)(cfn)).id
cls_name = (ast.Name*(cfn)).id
# context 对象存入 __with_ctx(方法调用解析 self 用)
ctx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
builder, ctx_val.Ty)
ctx_alloca: llvmlite.Value* = llvmlite.build_alloca(builder, ctx_val.Ty)
if ctx_alloca is None:
continue
llvmlite.build_store(builder, ctx_val, ctx_alloca)
self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", ctx_alloca)
if cls_name is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, "__with_ctx", cls_name)
ctx_class_buf[i] = t.CSizeT(cls_name)
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_name)
ctx_class_buf[i] = sizet(cls_name)
# 调用 __enter__()
enter_val: llvmlite.Value | t.CPtr = self._with_ctx_call(
trans, pool, builder, mod, "__enter__")
enter_val: llvmlite.Value* = self._with_ctx_call(trans, pool, builder, mod, "__enter__")
# asname 绑定(__enter__ 返回值,缺失时退回 context 对象)
if item.optional_vars is not None:
ov: ast.AST | t.CPtr = item.optional_vars
ov: ast.AST* = item.optional_vars
if ov.kind() == ast.ASTKind.Name:
asname: str = ((ast.Name | t.CPtr)(ov)).id
asname: str = (ast.Name*(ov)).id
if asname is not None:
bind_val: llvmlite.Value | t.CPtr = enter_val
bind_val: llvmlite.Value* = enter_val
if bind_val is None:
bind_val = ctx_val
if bind_val is not None:
asn_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
builder, bind_val.Ty)
asn_alloca: llvmlite.Value* = llvmlite.build_alloca(builder, bind_val.Ty)
if asn_alloca is not None:
llvmlite.build_store(builder, bind_val, asn_alloca)
self.Gen.VarGen.define_var(trans.SymTab, asname, asn_alloca)
if cls_name is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, asname, cls_name)
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, asname, cls_name)
# FakeDuck: __provides__ 元数据 → 生成 _push_provider 调用
# (对齐 TPC HandlesWith.py L138-159;经 gen 单例委托 CallGenMixin。
# 类名去模块前缀后查 __provides__ 表,如 memhub.MemBuddy → MemBuddy)
@@ -695,15 +656,14 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
if fd_dot is not None:
fd_cls_bare = fd_dot + 1
if fd_cls_bare is not None:
self.Gen.CallGenFd._emit_provider_push(
trans, pool, builder, mod, fd_cls_bare, ctx_val)
self.Gen.CallGenFd._emit_provider_push(trans, pool, builder, mod, fd_cls_bare, ctx_val)
# 2. 翻译 body
body: list[ast.AST | t.CPtr] | t.CPtr = wt.children
body: list[ast.AST*]* = wt.children
if body is not None:
bn: t.CSizeT = len(body)
bn: sizet = len(body)
for bi in range(bn):
st: ast.AST | t.CPtr = body.get(bi)
st: ast.AST* = body.get(bi)
if st is not None:
self.translate_stmt(trans, st)
@@ -711,23 +671,21 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# (对齐 TPC HandlesWith.py L167-174;逆序弹栈,经 gen 单例委托)
for pi_pop in range(n):
if ctx_class_buf[pi_pop] != 0:
cls_pop: str = (str | t.CPtr)(t.CVoid(ctx_class_buf[pi_pop], t.CPtr))
cls_pop: str = rawptr(ctx_class_buf[pi_pop])
fd_cls_bare_p: str = cls_pop
if cls_pop is not None:
fd_dot_p: str = string.strrchr(cls_pop, '.')
if fd_dot_p is not None:
fd_cls_bare_p = fd_dot_p + 1
if fd_cls_bare_p is not None:
self.Gen.CallGenFd._emit_provider_pop(
trans, pool, builder, mod, fd_cls_bare_p)
self.Gen.CallGenFd._emit_provider_pop(trans, pool, builder, mod, fd_cls_bare_p)
# 3. 逆序调用 __exit__()(Python with 语义:后进先出)
for i in range(n):
ci: int = n - 1 - i
if ctx_class_buf[ci] != 0:
cls_ci: str = (str | t.CPtr)(t.CVoid(ctx_class_buf[ci], t.CPtr))
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, "__with_ctx", cls_ci)
cls_ci: str = rawptr(ctx_class_buf[ci])
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_ci)
self._with_ctx_call(trans, pool, builder, mod, "__exit__")
# 4. 恢复外层 __with_ctx
@@ -735,24 +693,17 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", old_ctx_alloca)
return 0
def _with_ctx_call(self, trans: HT.Translator | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
method_name: str) -> llvmlite.Value | t.CPtr:
def _with_ctx_call(self, trans: HT.Translator*,
pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
method_name: str) -> llvmlite.Value*:
"""构造并翻译 __with_ctx.<method_name>() 调用(__enter__/__exit__)"""
if pool is None or method_name is None:
return None
name_node: ast.AST | t.CPtr = ast.Name(pool, "__with_ctx", ast.ASTCtx.Load)
if name_node is None:
return None
attr_node: ast.AST | t.CPtr = ast.Attribute(
pool, name_node, method_name, ast.ASTCtx.Load)
if attr_node is None:
return None
call_node: ast.AST | t.CPtr = ast.Call(pool, attr_node, None, None)
if call_node is None:
return None
return self.Gen.ExprGen.translate_value(
builder, pool, mod, call_node,
trans._funcs, trans._func_count, trans)
if pool is None or method_name is None: return None
name_node: ast.AST* = ast.Name(pool, "__with_ctx", ast.ASTCtx.Load)
if name_node is None: return None
attr_node: ast.AST* = ast.Attribute(pool, name_node, method_name, ast.ASTCtx.Load)
if attr_node is None: return None
call_node: ast.AST* = ast.Call(pool, attr_node, None, None)
if call_node is None: return None
return self.Gen.ExprGen.translate_value(builder, pool, mod, call_node, trans._funcs, trans._func_count, trans)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+298
View File
@@ -0,0 +1,298 @@
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import hashtable
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
# TypeSpec 模块导入别名用 TSpec(模块内类名也叫 TypeSpec,别名同名会被解析器
# 当成"类 TypeSpec 的静态方法",导致跨模块调用被 mangled 成 <sha1>.TypeSpec.<fn>
# 并隐式补 self 实参 → undefined symbol)。
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# TypeGenBase - 类型表基础设施:字段 + InitTypeGen + trace/临时目录/generic 上下文
# ============================================================
novirtual class TypeGenBase:
"""字段 + InitTypeGen + trace/临时目录/generic 上下文"""
# --- 仿 Python Traceback 调用栈(原 _g_trace_frames/_g_trace_count) ---
Gen: Generator.Generator*
TraceFrames: rawptr[GenTypes.TraceFrame] # malloc 数组(TypeGenMixin.TRACE_MAX 槽)
TraceCount: int
# --- 全局泛型特化上下文(原 _g_generic_tp_names/_g_generic_type_args) ---
GenericTpNames: list[str]*
GenericTypeArgs: list[str]*
# 嵌套安全栈:set_generic_context 压入外层值,clear_generic_context 弹出
# 恢复(原单槽赋值:嵌套特化/泛型函数翻译退出时把外层上下文清成 None →
# 外层方法体后续的 T 替换丢失 → T.__sizeof__() NameError,VectorGenericTest
# 特化 Vector[int].__init__ 回归根因)。
GenericCtxStack: sizet* # i64* 固定数组:每层 2 个槽(tp_names, type_args 指针)
GenericCtxDepth: int
# --- 全局 CDefine 编译期常量表(原 _g_cdefine_*) ---
CDefineNames: list[str]*
CDefineValues: list[str]*
CDefineDoubleFlags: list[int] | t.CPtr
CDefineStrFlags: list[int] | t.CPtr # 1=字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2")
CDefineIsDouble: int
CDefineIsStr: int
CDefineFound: int
# --- CDefine 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
# 与 CDefineNames 同池分配、同 clear_cdefine_constants 复位;CDefineNames 只
# 追加不重排 ⇒ 存槽下标稳定;Built == len(CDefineNames) 才可信,不可信/未命中
# 一律回退线性扫描(正确性保证)。
CDefineNameIdx: sizet*
CDefineNameIdxCap: int
CDefineNameIdxBuilt: int
# --- 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) ---
# 缓存 reset_to(mark2)/clear_cdefine_constants 不清除 → 跨文件复用,
# CONST_INT 递归查找只做一次。
XmodCdefNames: list[str]*
XmodCdefValues: list[str]*
XmodCdefDoubleFlags: list[int] | t.CPtr
XmodCdefStrFlags: list[int] | t.CPtr
# --- 当前模块 SHA1(原 _g_current_module_sha1) ---
CurrentModuleSha1: str
# --- 全局 CTypedef 表(原 _g_ctypedef_*) ---
CTypedefNames: list[str]*
CTypedefTypeStrs: list[str]*
CTypedefFound: int
# CTypedef 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)。
# 与 CTypedefNames 同池分配、同 clear_ctypedefs 复位;CTypedefNames 只追加
# 不重排(同名注册原地更新类型串)⇒ 存槽下标稳定;Built == len(CTypedefNames)
# 才可信,不可信/未命中一律回退线性扫描(正确性保证)。
CTypedefNameIdx: sizet*
CTypedefNameIdxCap: int
CTypedefNameIdxBuilt: int
CTypedefResolveDepth: int
CTypedefVisiting: list[str]*
# builtins 源扫描标记:注解解析"最后匹配"兜底(_lookup_builtins_typedef)
# 每文件最多读一次 includes/builtins.vp;随 clear_ctypedefs 复位
BuiltinsTypedefScanned: int
# builtins 最后匹配诊断("变量类型注解解析失败"详细报错用):
# LastBuiltinsTypedefName = 最后一次请求的名称;LastBuiltinsTypedefResult =
# 展开后的类型表达式(未命中 = None)。随 clear_ctypedefs 复位。
# 两个被指串由以下自有缓冲承载(stdlib.malloc,clear_ctypedefs 释放):
# 调用方常传临时缓冲(VpiReader._resolve_single_vpi_type_str 的段缓冲
# malloc 后立刻 free),直接存指针会悬垂 → _build_ann_fail_msg 的
# snprintf 读已释放内存(ASan heap-use-after-free)。
LastBuiltinsTypedefName: str
LastBuiltinsTypedefResult: str
LastBuiltinsTypedefNameBuf: bytes
LastBuiltinsTypedefResultBuf: bytes
# builtins import 别名缓冲(stdlib.malloc,格式 "alias\0target\0" 连续段):
# _lookup_builtins_typedef 扫描 builtins.vp import 行填充;库 typedef 模块限定
# 引用按别名还原真实模块名(__stdint.size_t → stdint.size_t)。
# 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空;malloc 跨 pool
# 生命周期存活,clear_ctypedefs 释放置空。
ImportAliasBuf: bytes
# --- 库 typedef 按模块一次性门禁(_lookup_library_typedef 性能优化) ---
# 库模块 typedef 缓存(PermPool 永久分配,跨文件/Phase 存活):
# LibTypedefScannedMods: key=库模块名 → 已扫描标记(命中=该模块源已读并入缓存);
# LibTypedefTypeMap: key="模块名|typedef 名" → 源中原始类型表达式。
# 每次查询都读 64KB 库源全文扫描会使单文件 translate 耗时 7–12s;库源单次编译内
# 不变,故缓存命中结果与逐次读源等价。
LibTypedefScannedMods: hashtable.HashTable*
LibTypedefTypeMap: hashtable.HashTable*
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
ConstEvalOk: int
# --- 全局当前文件名(原 _g_current_filename) ---
CurrentFile: str
# --- TypeSpec 驻留表(统一类型描述符,Debugger/_Type.md 第 6 节) ---
# 惰性创建(ensure_spec_arena);由类型子系统自身持有,避免模块级全局表。
# 实例与内部数组均 PermPool 分配 ⇒ 跨文件/跨 mark 存活。
Specs: TSpec.TypeSpecArena*
def InitTypeGen(self) -> int:
"""初始化类型 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
if self is None: return 1
self.TraceFrames = None
self.TraceCount = 0
self.GenericTpNames = None
self.GenericTypeArgs = None
self.GenericCtxStack = None
self.GenericCtxDepth = 0
self.CDefineNames = None
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
self.CDefineFound = 0
self.CDefineNameIdx = None
self.CDefineNameIdxCap = 0
self.CDefineNameIdxBuilt = -1
self.XmodCdefNames = None
self.XmodCdefValues = None
self.XmodCdefDoubleFlags = None
self.XmodCdefStrFlags = None
self.CurrentModuleSha1 = None
self.CTypedefNames = None
self.CTypedefTypeStrs = None
self.CTypedefFound = 0
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
self.CTypedefResolveDepth = 0
self.CTypedefVisiting = None
self.BuiltinsTypedefScanned = 0
self.LastBuiltinsTypedefName = None
self.LastBuiltinsTypedefResult = None
self.LastBuiltinsTypedefNameBuf = None
self.LastBuiltinsTypedefResultBuf = None
self.ImportAliasBuf = None
# 库 typedef 持久缓存字段(不随 clear_ctypedefs 复位 → 跨文件/Phase 存活)
self.LibTypedefScannedMods = None
self.LibTypedefTypeMap = None
self.ConstEvalOk = 0
self.CurrentFile = None
self.Specs = None
return 0
# ============================================================
# ensure_spec_arena - 惰性创建 TypeSpec 驻留表(幂等)
#
# 用 PermPool 分配 arena 本体;表内部缓冲由 TSpec.SpecArenaInit 分配
# (同样 PermPool)。失败返回 None(调用方按 fail-fast 处理,不静默退化)。
# ============================================================
def ensure_spec_arena(self) -> TSpec.TypeSpecArena*:
"""确保 TypeSpec 驻留表已创建,返回 arena;失败返回 None"""
if self.Specs is not None: return self.Specs
size: sizet = TSpec.TypeSpecArena.__sizeof__()
arena: TSpec.TypeSpecArena* = TSpec.TypeSpecArena*(CompilerFactory.factory.PermPool.alloc(size))
if arena is None: return None
string.memset(arena, 0, size)
if TSpec.SpecArenaInit(arena, 0) != 0: return None
self.Specs = arena
return arena
# ============================================================
# P2 迁移方法(函数体来自 HandlesType.vp,全局变量改 self.字段,
# 同模块函数调用改 self.方法;签名与 HandlesType 原函数一致)
# ============================================================
def _ensure_trace_init(self) -> int:
"""确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)"""
if self.TraceFrames is not None: return 1
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
self.TraceFrames = CompilerFactory.factory.PermPool.alloc(entry_size * TypeGenMixin.TRACE_MAX)
if self.TraceFrames is None: return 0
string.memset(self.TraceFrames, 0, entry_size * TypeGenMixin.TRACE_MAX)
return 1
def _get_trace_frame(self, i: int) -> rawptr[GenTypes.TraceFrame]:
"""获取第 i 个 TraceFrame 槽位"""
if self.TraceFrames is None or i < 0 or i >= TypeGenMixin.TRACE_MAX: return None
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
addr: uint64 = uint64(self.TraceFrames) + i * entry_size
return rawptr[GenTypes.TraceFrame](addr)
def push_trace(self, file_name: str, lineno: int, col_start: int = 0, col_end: int = 0) -> None:
"""压入一个翻译上下文帧(进入新的翻译作用域时调用)"""
if self._ensure_trace_init() == 0: return
if self.TraceCount >= TypeGenMixin.TRACE_MAX: return
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
if frame is None: return
frame.file_name = file_name
frame.lineno = lineno
frame.col_start = col_start
frame.col_end = col_end
self.TraceCount += 1
def pop_trace(self) -> None:
"""弹出栈顶帧(退出翻译作用域时调用)"""
if self.TraceCount <= 0: return
self.TraceCount -= 1
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
def clear_trace(self) -> None:
"""清空调用栈(每个文件翻译开始前调用)"""
for i in range(self.TraceCount):
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(i)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
self.TraceCount = 0
def set_temp_dir(self, temp_dir: str) -> None:
"""设置全局 temp_dir(统一写入 CallGenBase.TempDir,跨子对象经 Gen 指针)"""
self.Gen.CallGenBase.SetTempDir(temp_dir)
def get_temp_dir(self) -> str:
"""获取全局 temp_dir(从 CallGenBase 读取)"""
return self.Gen.CallGenBase.TempDir
def set_current_module_sha1(self, sha1: str) -> None:
"""设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)"""
self.CurrentModuleSha1 = sha1
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*,
tree: ast.AST*, tag: str):
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
pass
def set_generic_context(self, tp_names: list[str]*,
type_args: list[str]*):
"""设置泛型特化上下文(进入泛型类方法体翻译时调用),嵌套安全
先压栈当前(外层)上下文,再设置新值。clear_generic_context 弹出恢复,
避免嵌套特化/泛型函数翻译退出时清空外层上下文(T.__sizeof__() 回归根因)。
"""
if self.GenericCtxStack is None:
self.GenericCtxStack = stdlib.malloc(8 * 2 * 32)
if self.GenericCtxStack is None:
self.GenericTpNames = tp_names
self.GenericTypeArgs = type_args
return
self.GenericCtxDepth = 0
if self.GenericCtxDepth < 32:
self.GenericCtxStack[self.GenericCtxDepth * 2] = sizet(self.GenericTpNames)
self.GenericCtxStack[self.GenericCtxDepth * 2 + 1] = sizet(self.GenericTypeArgs)
self.GenericCtxDepth += 1
self.GenericTpNames = tp_names
self.GenericTypeArgs = type_args
def clear_generic_context(self):
"""清除泛型特化上下文(离开泛型类方法体翻译时调用),恢复外层而非置 None"""
if self.GenericCtxDepth > 0:
self.GenericCtxDepth -= 1
self.GenericTpNames = (list[str]*)(self.GenericCtxStack[self.GenericCtxDepth * 2])
self.GenericTypeArgs = (list[str]*)(self.GenericCtxStack[self.GenericCtxDepth * 2 + 1])
else:
self.GenericTpNames = None
self.GenericTypeArgs = None
def resolve_generic_type_name(self, name: str) -> str:
"""如果 name 是泛型类型参数,返回对应的具体类型名;否则返回 name 本身
用于表达式中的 T 替换(如 T.__sizeof__() → int.__sizeof__())
"""
if name is None:
return name
rv_d: str = name
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
rg_n: sizet = len(self.GenericTpNames)
rg_i: sizet
for rg_i in range(rg_n):
rg_tpn: str = self.GenericTpNames.get(rg_i)
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
rg_ta: str = self.GenericTypeArgs.get(rg_i)
if rg_ta is not None:
rv_d = rg_ta
break
return rv_d
+550
View File
@@ -0,0 +1,550 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
# ============================================================
# CDefine 名索引(名 → 槽下标,开放寻址 | 线性探测)
#
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
# 槽存槽下标 + 1(0=空槽)。CDefineNames 只追加不重排 ⇒ 槽下标稳定;
# Built == len(CDefineNames) 才可信。
# ============================================================
define CDEFINE_IDX_CAP = 4096
# 索引哈希常量(32 位 FNV-1a)
define CDEF_IDX_FNV_OFFSET = 2166136261
define CDEF_IDX_FNV_PRIME = 16777619
define CDEF_IDX_FNV_MASK = 4294967295
# ============================================================
# TypeGenCDef - TypeGenMixin 拆分(阶段 D):CDefine 编译期常量表(register/lookup/is/set/clear)
# ============================================================
novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
"""CDefine 编译期常量表(register/lookup/is/set/clear)"""
# ============================================================
# CDefine 名索引(开放寻址;槽存 index+1,0=空槽)
# ============================================================
def _cdef_idx_hash(self, name: str) -> uint64:
"""名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
h: uint64 = uint64(CDEF_IDX_FNV_OFFSET)
if name is None:
return h
i: sizet = 0
while name[i] != '\0':
h = (h ^ uint64(int(name[i]))) * uint64(CDEF_IDX_FNV_PRIME)
h = h & uint64(CDEF_IDX_FNV_MASK)
i += 1
return h
def _cdef_idx_slot(self, si: sizet) -> sizet*:
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
return (sizet*)(uint64(self.CDefineNameIdx) + uint64(si) * 8)
def _cdef_idx_init(self, pool: memhub.MemBuddy*) -> None:
"""创建/清零名索引(与 CDefineNames 同池);失败则标记不可信"""
idx: sizet* = pool.alloc(sizet(CDEFINE_IDX_CAP) * 8)
if idx is None:
self.CDefineNameIdx = None
self.CDefineNameIdxCap = 0
self.CDefineNameIdxBuilt = -1
return
string.memset(idx, 0, sizet(CDEFINE_IDX_CAP) * 8)
self.CDefineNameIdx = idx
self.CDefineNameIdxCap = CDEFINE_IDX_CAP
self.CDefineNameIdxBuilt = 0
def _cdef_idx_find(self, name: str) -> int:
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None:
return -1
if self.CDefineNameIdxBuilt != int(len(self.CDefineNames)):
return -1
h: uint64 = self._cdef_idx_hash(name)
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CDefineNameIdxCap:
slot: sizet* = self._cdef_idx_slot(si)
sv: sizet = slot[0]
if sv == 0:
return -1
se: int = int(sv) - 1
nm: str = self.CDefineNames.get(sizet(se))
if nm is not None and string.strcmp(nm, name) == 0:
return se
si = (si + 1) & mask
i += 1
return -1
def _cdef_idx_insert(self, slot_index: int, name: str) -> int:
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None:
return 1
h: uint64 = self._cdef_idx_hash(name)
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CDefineNameIdxCap:
slot: sizet* = self._cdef_idx_slot(si)
if slot[0] == 0:
slot[0] = sizet(slot_index + 1)
return 0
si = (si + 1) & mask
i += 1
return 1
def _cdef_idx_sync_append(self, name: str) -> None:
"""CDefineNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
n: int = int(len(self.CDefineNames))
if self.CDefineNameIdxBuilt == n - 1 and self._cdef_idx_insert(n - 1, name) == 0:
self.CDefineNameIdxBuilt = n
else:
self.CDefineNameIdxBuilt = -1
# ============================================================
# CDefine 表本体的池归一
#
# 表指针挂在**进程级单例** _g_gen.TypeGenCDef 上,不在任何 pool 的管辖
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
# (Phase1 1a/1b、Phase2 PhaseA、StubMerger 扫描、Repl 每片段都按 mark/reset
# 轮转)。表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None`
# 守卫失效 ⇒ 下一次 register_cdefine_* 的 append 写进已回收内存(堆损坏;
# 检测模式下该区被 free 填成 0xCD ⇒ append 的接收者 = 0xcdcdcdcdcdcdcdcd)。
# 故表本体(四个 list + 名索引)一律落 PermPool(进程级永久池,从不 reset),
# 与"文件级/片段级 reset"彻底解耦。
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
# ============================================================
def _cdefine_table_pool(self,
pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
"""CDefine 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
if CompilerFactory.factory is not None \
and CompilerFactory.factory.PermPool is not None:
return CompilerFactory.factory.PermPool
return pool
def register_cdefine_constant(self, pool: memhub.MemBuddy*,
name: str, value: int64) -> None:
"""注册 CDefine 编译期常量到本地表
值以十进制字符串存储(64 位 %lld)。大整数 CDefine(如
sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断
成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。
0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。
名称深拷贝到 pool(与 register_xmod_cdefine 对 key 深拷贝同一教训):
调用方传入的 name 可能指向临时缓冲——VpiReader 注入 .vpi 时 Name.id 是
零拷贝指向 vpi_buf(见 ParseVpiIntoModule 的 token 零拷贝注释),而
vpi_buf 在注入函数末尾即 stdlib.free。只存指针会让 CDefineNames 持有
悬空名:之后 lookup 的 strcmp 若命中被回收/复用内存里恰好出现的同名文本,
就会返回别的条目的值(Phase1.SHA1_LEN=17 被读成 0 的根因)。
"""
if name is None:
return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
# 落到 None 或未初始化地址(原实现无检查 ⇒ append 写进 NULL/0xCD 区崩)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表分配失败(表本体池不可用或耗尽)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self._cdef_idx_init(tp)
# 表就绪守卫:表本体缺失/半初始化(建表失败或外部置空)⇒ fail-fast,
# 不写垃圾(append 之前必有非空断言)
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
return
name_copy: str = pool.alloc(string.strlen(name) + 1)
if name_copy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 名称缓冲区分配失败", "MemoryError")
return
string.strcpy(name_copy, name)
self.CDefineNames.append(name_copy)
self._cdef_idx_sync_append(name_copy)
val_buf: str = pool.alloc(32)
if val_buf is not None:
viperlib.snprintf(val_buf, 32, "%lld", value)
self.CDefineValues.append(val_buf)
else:
self.CDefineValues.append("0")
# fail-fast:CDefine 值缓冲区分配失败必须终止编译
# (与浮点/字符串分支口径一致,不再静默写 "0" 污染常量表)
if val_buf is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 值缓冲区分配失败", "MemoryError")
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(0)
def register_cdefine_double(self, pool: memhub.MemBuddy*,
name: str, value: double) -> None:
"""注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...)
值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。
名称同样深拷贝到 pool(理由见 register_cdefine_constant:注入 .vpi 时
Name.id 指向的 vpi_buf 会被释放,只存指针会留下悬空名)。
"""
if name is None:
return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表分配失败(表本体池不可用或耗尽)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self._cdef_idx_init(tp)
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
return
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
if name_copy_d is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 浮点 '{name}' 名称缓冲区分配失败", "MemoryError")
return
string.strcpy(name_copy_d, name)
self.CDefineNames.append(name_copy_d)
self._cdef_idx_sync_append(name_copy_d)
val_buf: str = pool.alloc(32)
if val_buf is not None:
# 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的
# %g 浮点路径有 bug(%.17g 输出错误值,如 pi 显示 3.1397985 而非 3.1415926,
# 位模式 %a 正确),会导致 CDefine 表存错文本 → lookup 生成错 LLVM 常量。
stdio.snprintf(val_buf, 32, "%.17g", value)
self.CDefineValues.append(val_buf)
else:
self.CDefineValues.append("0")
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(1)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(0)
# fail-fast:CDefine 值缓冲区分配失败必须终止编译,
if val_buf is None:
VLogger.error("CDefine 浮点值缓冲区分配失败", "CDEF-DBG")
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点值缓冲区分配失败", "MemoryError")
def register_cdefine_string(self, pool: memhub.MemBuddy*,
name: str, value: str) -> None:
"""注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2")
字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写,
与浮点 CDefine 的恢复同理)。名称同样深拷贝(理由见
register_cdefine_constant:注入 .vpi 的 Name.id 指向会被释放的 vpi_buf)。
"""
if pool is None or name is None:
return
if value is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值缺失: {name}", "InternalCompilerError")
return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表分配失败(表本体池不可用或耗尽)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self._cdef_idx_init(tp)
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
return
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
if ncopy_s is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串 '{name}' 名称缓冲区分配失败", "MemoryError")
return
string.strcpy(ncopy_s, name)
self.CDefineNames.append(ncopy_s)
self._cdef_idx_sync_append(ncopy_s)
vlen: sizet = string.strlen(value)
vcopy: str = pool.alloc(vlen + 1)
if vcopy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值分配失败: {name}", "MemoryError")
return
string.strcpy(vcopy, value)
self.CDefineValues.append(vcopy)
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(1)
# 同步设置"最近注册/查表结果为字符串"标志(is_cdefine_str() 读取)。
# 跨模块字符串 CDefine 读回只调本函数 + 尾部 set_cdefine_found;不设标志会让
# 属性路径 is_cdefine_str()==0 → 退化为整数分支返回 0 → IR 生成 inttoptr i32 0
# → 运行期按 NULL 字符串处理崩溃。
self.CDefineIsStr = 1
self.CDefineFound = 1
# ============================================================
# 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用)
# ============================================================
def register_xmod_cdefine(self, name: str, value_str: str,
is_double: int, is_str: int) -> None:
"""注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(跨文件复用)
Args:
name: CDefine 常量名(如 CONST_INT)
value_str: 值文本(十进制/浮点/字符串)
is_double: 1=浮点值
is_str: 1=字符串值
"""
if name is None or value_str is None:
return
# 去重:同名已缓存则跳过(首次查找结果为准,CDefine 全局唯一约定)
if self.XmodCdefNames is not None:
x_n: sizet = len(self.XmodCdefNames)
x_i: sizet
for x_i in range(x_n):
x_nm: str = self.XmodCdefNames.get(x_i)
if x_nm is not None and string.strcmp(x_nm, name) == 0:
return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if self.XmodCdefNames is None:
self.XmodCdefNames = list[str](perm, 64)
self.XmodCdefValues = list[str](perm, 64)
self.XmodCdefDoubleFlags = list[int](perm, 64)
self.XmodCdefStrFlags = list[int](perm, 64)
# key/value 深拷贝到 perm_pool:入参(pool.alloc 临时缓冲)在
# reset_to(mark2) 后悬空,append 只存指针 → 必须深拷贝跨 reset 存活
klen: sizet = string.strlen(name)
kcopy: str = perm.alloc(klen + 1)
if kcopy is None:
return
string.strcpy(kcopy, name)
vlen: sizet = string.strlen(value_str)
vcopy: str = perm.alloc(vlen + 1)
if vcopy is None:
return
string.strcpy(vcopy, value_str)
self.XmodCdefNames.append(kcopy)
self.XmodCdefValues.append(vcopy)
if self.XmodCdefDoubleFlags is not None:
self.XmodCdefDoubleFlags.append(is_double)
if self.XmodCdefStrFlags is not None:
self.XmodCdefStrFlags.append(is_str)
def lookup_xmod_cdefine(self, name: str) -> str:
"""查找跨模块 CDefine 缓存,命中设置 found/is_double/is_str 标志
Returns:
值文本(命中),未命中返回 None
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if name is None or self.XmodCdefNames is None:
return None
n: sizet = len(self.XmodCdefNames)
i: sizet
for i in range(n):
nm: str = self.XmodCdefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.XmodCdefValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1:
self.CDefineIsStr = 1
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
return val_str
return None
def lookup_cdefine_constant(self, name: str) -> int64:
"""查找 CDefine 编译期常量
返回找到的值(64 位,大整数 CDefine 如 sys.maxsize 全精度)。
由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1),
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
调用方应在调用后检查 _g_cdefine_found。
浮点 CDefine 项返回整数部分(与 _g_cdefine_is_double 标志配合)。
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return 0
n: sizet = len(self.CDefineNames)
i: sizet
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0(= 原全表扫描
# 结果);索引不可用时回退下方原全表线性扫描。
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) \
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0:
return 0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
# 字符串 CDefine(如 ZLIB_VERSION="1.3.2"):整数查询路径
# 标记 is_str,调用方改用 lookup_cdefine_string 取字符串。
self.CDefineIsStr = 1
return 0
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
return string.atoll(val_str)
return string.atoll(val_str)
return 0
def lookup_cdefine_double(self, name: str) -> double:
"""查找 CDefine 编译期常量,返回 double 值(浮点/整数统一)
与 lookup_cdefine_constant 共享 _g_cdefine_found / _g_cdefine_is_double
(found=1 且 is_double=1 → 浮点 CDefine,应返回浮点常量)。
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return 0.0
n: sizet = len(self.CDefineNames)
i: sizet
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0.0(= 原全表扫描
# 结果);索引不可用时回退下方原全表线性扫描。
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) \
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0:
return 0.0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
nm: str = self.CDefineNames.get(i)
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
self.CDefineIsStr = 1
return 0.0
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
ret_d: double = string.atof(val_str)
return ret_d
ret_i2: double = double(string.atoll(val_str))
return ret_i2
return 0.0
def lookup_cdefine_string(self, name: str) -> str:
"""查找 CDefine 编译期字符串值(found=1 且 is_str=1 时有效)
返回的字符串指针位于 pool 内(当前模块生命周期内有效),
调用方应尽快用于 create_global_string 拷贝到 LLVM 常量。
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return None
n: sizet = len(self.CDefineNames)
i: sizet
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 None(= 原全表扫描
# 结果);索引不可用时回退下方原全表线性扫描。
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) \
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0:
return None
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
self.CDefineIsStr = 1
return val_str
return val_str
return None
def is_cdefine_str(self) -> int:
"""检查最近一次 CDefine 查找命中的是否是字符串常量"""
return self.CDefineIsStr
def is_cdefine_double(self) -> int:
"""检查最近一次 CDefine 查找命中的是否是浮点常量"""
return self.CDefineIsDouble
def is_cdefine_found(self) -> int:
"""检查上次 lookup_cdefine_constant 是否找到值"""
return self.CDefineFound
def set_cdefine_found(self, val: int) -> None:
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
self.CDefineFound = val
def clear_cdefine_constants(self) -> None:
"""清空 CDefine 常量表(模块切换时调用)
表本体自首次创建起常驻 PermPool(见 _cdefine_table_pool)⇒ 此处只
**清空内容**、不丢弃表指针:
- 丢指针会退回"下一次 register_cdefine_* 按**调用方 pool** 重建",
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
初始化内存的根因);
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
register 首次 append 即写回第 0 槽。
"""
if self.CDefineNames is not None:
self.CDefineNames.clear()
if self.CDefineValues is not None:
self.CDefineValues.clear()
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.clear()
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.clear()
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
# 与"新建表后首次 register"的索引状态逐字一致)
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
string.memset(self.CDefineNameIdx, 0,
sizet(self.CDefineNameIdxCap) * 8)
self.CDefineNameIdxBuilt = 0
else:
self.CDefineNameIdx = None
self.CDefineNameIdxCap = 0
self.CDefineNameIdxBuilt = -1
self.CDefineIsStr = 0
+767
View File
@@ -0,0 +1,767 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# CTypedef 名索引(名 → 槽下标,开放寻址 | 线性探测)
#
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
# 槽存槽下标 + 1(0=空槽)。CTypedefNames 只追加不重排(同名注册原地更新类型串)
# ⇒ 槽下标稳定;Built == len(CTypedefNames) 才可信。
# ============================================================
define CTYPEDEF_IDX_CAP = 4096
# 索引哈希常量(32 位 FNV-1a,与 StructGenMixin 范式一致)
define CTYPEDEF_IDX_FNV_OFFSET = 2166136261
define CTYPEDEF_IDX_FNV_PRIME = 16777619
define CTYPEDEF_IDX_FNV_MASK = 4294967295
def _ctypedef_idx_hash(name: str) -> uint64:
"""CTypedef 名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
h: uint64 = uint64(CTYPEDEF_IDX_FNV_OFFSET)
if name is None: return h
i: sizet = 0
while name[i] != '\0':
h = (h ^ uint64(int(name[i]))) * uint64(CTYPEDEF_IDX_FNV_PRIME)
h = h & uint64(CTYPEDEF_IDX_FNV_MASK)
i += 1
return h
# ============================================================
# TypeGenCType - CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)
#
# 继承链仅 1 级(TPV self 调用仅支持 1 级父类);跨 Mixin 调用统一走 HandlesType 委托。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class TypeGenCType(TypeGenBase.TypeGenBase):
"""CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)"""
# ============================================================
# 本类**不**重复声明 CTypedef* 字段:它们由父类 TypeGenBase 的**类体**声明
# (TypeGenBase.vp:73-84),跨模块可见性随父类声明一并提供(继承前缀)。
# 判据:字段只在其**归属类**的类体声明一次;子类若再声明同名继承字段,该名字
# 会变成子类"自有字段",自有表与继承前缀出现重名条目 ⇒ `base + 表内下标`
# 的继承字段偏移整体错位(现场:TypeGenResolve.vp 的
# `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 落到 i32 槽 →
# "指针隐式转整数已禁止" fail-fast;正确槽是同名继承字段的 i8* 槽)。
# ============================================================
# ============================================================
# CTypedef 名索引(开放寻址;槽存 index+1,0=空槽)
# ============================================================
def _ctypedef_idx_slot(self, si: sizet) -> sizet*:
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
return (sizet*)(uint64(self.CTypedefNameIdx) + uint64(si) * 8)
def _ctypedef_idx_init(self, pool: memhub.MemBuddy*) -> None:
"""创建/清零名索引(与 CTypedefNames 同池);失败则标记不可信"""
idx: sizet* = pool.alloc(sizet(CTYPEDEF_IDX_CAP) * 8)
if idx is None:
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
return
string.memset(idx, 0, sizet(CTYPEDEF_IDX_CAP) * 8)
self.CTypedefNameIdx = idx
self.CTypedefNameIdxCap = CTYPEDEF_IDX_CAP
self.CTypedefNameIdxBuilt = 0
def _ctypedef_idx_find(self, name: str) -> int:
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
if self.CTypedefNameIdx is None or self.CTypedefNameIdxCap <= 0 or name is None:
return -1
if self.CTypedefNameIdxBuilt != int(len(self.CTypedefNames)):
return -1
h: uint64 = _ctypedef_idx_hash(name)
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CTypedefNameIdxCap:
slot: sizet* = self._ctypedef_idx_slot(si)
sv: sizet = slot[0]
if sv == 0:
return -1
se: int = int(sv) - 1
nm: str = self.CTypedefNames.get(sizet(se))
if nm is not None and string.strcmp(nm, name) == 0:
return se
si = (si + 1) & mask
i += 1
return -1
def _ctypedef_idx_insert(self, slot_index: int, name: str) -> int:
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
if self.CTypedefNameIdx is None or self.CTypedefNameIdxCap <= 0 or name is None:
return 1
h: uint64 = _ctypedef_idx_hash(name)
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CTypedefNameIdxCap:
slot: sizet* = self._ctypedef_idx_slot(si)
if slot[0] == 0:
slot[0] = sizet(slot_index + 1)
return 0
si = (si + 1) & mask
i += 1
return 1
def _ctypedef_idx_sync_append(self, name: str) -> None:
"""CTypedefNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
n: int = int(len(self.CTypedefNames))
if self.CTypedefNameIdxBuilt == n - 1 and self._ctypedef_idx_insert(n - 1, name) == 0:
self.CTypedefNameIdxBuilt = n
else:
self.CTypedefNameIdxBuilt = -1
# ============================================================
# CTypedef 表本体的池归一
#
# 表指针挂在**进程级单例** _g_gen.TypeGenCType 上,不在任何 pool 的管辖
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
# (Phase1 1a/1b、Phase2 PhaseA、Repl 每片段/每模块都按 mark/reset 轮转)。
# 表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None` 守卫失效
# ⇒ 下一次 register_ctypedef 的 append 写进已回收内存(堆损坏;检测模式下
# 该区被 free 填成 0xCD ⇒ append 收到 rdi=0xcdcdcdcdcdcdcdcd 后崩)。
# 故表本体(表对象 + 名索引 + visiting 表)一律落 PermPool(进程级永久池,
# 从不 reset),与"文件级/片段级 reset"彻底解耦。
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
# ============================================================
def _ctypedef_table_pool(self,
pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
"""CTypedef 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
if CompilerFactory.factory is not None \
and CompilerFactory.factory.PermPool is not None:
return CompilerFactory.factory.PermPool
return pool
def register_ctypedef(self, pool: memhub.MemBuddy*,
name: str, type_str: str) -> None:
"""注册 CTypedef 到本地表
Args:
pool: 内存池
name: CTypedef 名称(如 "HANDLE")
type_str: 类型表达式字符串(如 "VOIDPTR" 或 "t.CPtr")
"""
if name is None or type_str is None or pool is None:
return
# rawptr 是编译器内置语法(TPC/TPV 双端),不可重定义:
# 忽略任何对 rawptr 的 typedef 注册,防止覆盖内置裸指针语义
# (rawptr[T] 固定 = 指向原始类型 T 的裸指针)。
if string.strcmp(name, "rawptr") == 0:
return
# 复制 name/type_str 到 pool:调用方可能传入零拷贝 AST 字符串
# (如 .vpi 读取缓冲区),缓冲区随后被 free 会导致表项悬空、
# lookup_ctypedef strcmp 失配、typedef 退化为 i8*(testcheck
# SetConsoleOutputCP 的 UINT 根因)。表须持有自己的字符串副本。
name_len: sizet = string.strlen(name)
name_copy: str = pool.alloc(name_len + 1)
if name_copy is None:
return
string.strcpy(name_copy, name)
type_len: sizet = string.strlen(type_str)
type_copy: str = pool.alloc(type_len + 1)
if type_copy is None:
return
string.strcpy(type_copy, type_str)
if self.CTypedefNames is None:
tp: memhub.MemBuddy* = self._ctypedef_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
ts: list[str]* = list[str](tp, 64)
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
# 落到 None 或未初始化地址(原实现无检查 ⇒ 写进 0xCDCD 区崩)
if tn is None or ts is None:
VLogger.error("CTypedef 表分配失败(表本体池不可用或耗尽)", "TypeGen")
return
self.CTypedefNames = tn
self.CTypedefTypeStrs = ts
# 名索引随表创建(同池 = PermPool;分配失败则标记不可信,查找回退线性扫描)
self._ctypedef_idx_init(tp)
# 表就绪守卫:表本体缺失(建表失败/被外部置空)⇒ fail-fast,不写垃圾
if self.CTypedefNames is None or self.CTypedefTypeStrs is None:
VLogger.error("CTypedef 表未就绪(CTypedefNames/CTypedefTypeStrs 为空)", "TypeGen")
return
# 去重:若已存在同名 CTypedef,更新其类型字符串
exist_n: sizet = len(self.CTypedefNames)
ei: sizet
for ei in range(exist_n):
en: str = self.CTypedefNames.get(ei)
if en is not None and string.strcmp(en, name_copy) == 0:
# 已存在,更新类型字符串
self.CTypedefTypeStrs.set(ei, type_copy)
return
self.CTypedefNames.append(name_copy)
self.CTypedefTypeStrs.append(type_copy)
# 追加后同步名索引(槽下标 = 追加前 len = 新 len-1)
self._ctypedef_idx_sync_append(name_copy)
def store_builtins_diag(self, which: int, text: str) -> str:
"""把 builtins 诊断串复制进自有缓冲并返回该缓冲(which: 0=Name, 1=Result)
调用方(VpiReader._resolve_single_vpi_type_str)传的往往是
stdlib.malloc 的临时段缓冲,返回前即 free——直接存指针会让
_build_ann_fail_msg 的 snprintf 读已释放内存(ASan
heap-use-after-free)。诊断字段必须持有自己的副本。
text 为 None 时返回 None("未命中"标记)。
"""
if text is None:
return None
# 诊断串按需 realloc 到精确长度:固定 TYPEDEF_DIAG_BUF(256) + strncpy(buf, text, 255)
# 会静默截断超长诊断串 ⇒ 报错文本丢掉关键名字(归因信息失效)。
# 缓冲仍为自有字段(生命周期不变:仍在 reset/析构时统一 stdlib.free)。
need: sizet = string.strlen(text) + 1
if which == 0:
if self.LastBuiltinsTypedefNameBuf is None:
self.LastBuiltinsTypedefNameBuf = stdlib.malloc(need)
else:
self.LastBuiltinsTypedefNameBuf = stdlib.realloc(self.LastBuiltinsTypedefNameBuf, need)
buf: str = self.LastBuiltinsTypedefNameBuf
else:
if self.LastBuiltinsTypedefResultBuf is None:
self.LastBuiltinsTypedefResultBuf = stdlib.malloc(need)
else:
self.LastBuiltinsTypedefResultBuf = stdlib.realloc(self.LastBuiltinsTypedefResultBuf, need)
buf = self.LastBuiltinsTypedefResultBuf
if buf is None:
return None
string.strcpy(buf, text)
return buf
def lookup_ctypedef(self, name: str) -> str:
"""查找 CTypedef,返回类型表达式字符串
返回找到的类型表达式字符串,未找到返回 None。
调用方应在调用后检查 _g_ctypedef_found 标志区分"未找到"和"空字符串"。
"""
self.CTypedefFound = 0
if name is None:
return None
if self.CTypedefNames is None:
return None
n: sizet = len(self.CTypedefNames)
i: sizet
# 名索引快速路径:可信(Built==n)时直接定位唯一候选槽下标。CTypedef 表
# 注册时按名去重(register_ctypedef 已存在则原地更新类型串、不追加)⇒ 名
# 唯一且注册后不再改写,索引完整覆盖全表 ⇒ 未命中即"确无",直接返回 None
# (= 原全表扫描结果);索引不可用/不可信 → 回退下方原全表扫描。
if self.CTypedefNameIdxBuilt == int(n) \
and self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
ti: int = self._ctypedef_idx_find(name)
if ti < 0:
return None
ts_hit: str = self.CTypedefTypeStrs.get(sizet(ti))
self.CTypedefFound = 1
return ts_hit
for i in range(n):
nm: str = self.CTypedefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
ts: str = self.CTypedefTypeStrs.get(i)
self.CTypedefFound = 1
return ts
return None
def is_ctypedef_found(self) -> int:
"""检查上次 lookup_ctypedef 是否找到值"""
return self.CTypedefFound
def lookup_ctypedef_span(self, text: str, start: sizet, end: sizet) -> str:
"""按名字切片查 CTypedef(零分配,不复制子串)
与 lookup_ctypedef 同表;命中置 CTypedefFound=1 并返回类型表达式串,
未命中返回 None(CTypedefFound=0)。用于符号性独立真源的别名链展开。
"""
self.CTypedefFound = 0
if text is None or end <= start:
return None
if self.CTypedefNames is None:
return None
want: sizet = end - start
n: sizet = len(self.CTypedefNames)
i: sizet
for i in range(n):
nm: str = self.CTypedefNames.get(i)
if nm is None:
continue
# 快速路径:命中要求 nm[0]==text[start](逐字节比较的 k=0 项),
# 首字节不同者无需 strlen 即可判定不命中(不影响任何原命中项)。
if nm[0] != text[start] or string.strlen(nm) != want:
continue
hit: int = 1
k: sizet = 0
while k < want:
if nm[k] != text[start + k]:
hit = 0
break
k += 1
if hit != 0:
ts: str = self.CTypedefTypeStrs.get(i)
self.CTypedefFound = 1
return ts
return None
# 符号性真源(无符号判定的读入口)
#
# 实现:精确名白名单(TSpec.SpecSignFromTypeNameSpan)+ 别名链逐层展开;
# 不表态时返回 TS_SIGN_UNKNOWN(由调用方决定是否回退旧路径)。
# 不用 strstr 子串扫描 + 单层展开:会误判 BYTEPTR(含 BYTE)、sha1 前缀名(含
# uint32),且多层别名(WCHAR→WORD→uint16)漏判。
def spec_sign_of_annot(self, text: str, depth: int = 0) -> int:
"""按注解文本判定其整数值符号性(独立真源),未覆盖返回 TS_SIGN_UNKNOWN"""
if text is None or depth > 16:
return TSpec.TS_SIGN_UNKNOWN
return self._spec_sign_of_span(text, 0, string.strlen(text), depth)
def _spec_sign_of_span(self, text: str, start: sizet, end: sizet,
depth: int) -> int:
"""顶层 '|' 分段,返回首个能确定的符号性(`X | t.CPtr` 取 X)"""
seg_start: sizet = start
bracket_depth: int = 0
i: sizet = start
while i <= end:
is_split: int = 0
if i == end:
is_split = 1
elif text[i] == '|' and bracket_depth == 0:
is_split = 1
if is_split != 0:
r: int = self._spec_sign_of_segment(text, seg_start, i, depth)
if r != TSpec.TS_SIGN_UNKNOWN:
return r
seg_start = i + 1
elif text[i] == '[' or text[i] == '(':
bracket_depth += 1
elif text[i] == ']' or text[i] == ')':
if bracket_depth > 0:
bracket_depth -= 1
i += 1
return TSpec.TS_SIGN_UNKNOWN
def _spec_sign_of_segment(self, text: str, start: sizet, end: sizet,
depth: int) -> int:
"""单个片段:去空白/括号 → 指针后缀 → 下标取元素 → 末段名 → 别名链展开"""
s: sizet = start
e: sizet = end
while s < e:
if text[s] != ' ' and text[s] != '\t' and text[s] != '\n':
break
s += 1
while e > s:
if text[e - 1] != ' ' and text[e - 1] != '\t' and text[e - 1] != '\n':
break
e -= 1
# 去外层成对括号(如 "(uint32)")
while e > s + 1:
if text[s] != '(' or text[e - 1] != ')':
break
s += 1
e -= 1
while s < e:
if text[s] != ' ' and text[s] != '\t':
break
s += 1
while e > s:
if text[e - 1] != ' ' and text[e - 1] != '\t':
break
e -= 1
if e <= s:
return TSpec.TS_SIGN_UNKNOWN
# C 指针后缀 '*' → 指针,不表态(指针语义由 PtrLevel 表达)
if text[e - 1] == '*':
return TSpec.TS_SIGN_UNKNOWN
# 泛型/数组/rawptr 下标:IDENT[INNER, ...] → 取 INNER(元素决定值符号性)
bracket: sizet = 0
has_bracket: int = 0
k: sizet = s
while k < e:
if text[k] == '[':
bracket = k
has_bracket = 1
break
k += 1
if has_bracket != 0:
inner_s: sizet = bracket + 1
inner_e: sizet = inner_s
d: int = 0
while inner_e < e:
if text[inner_e] == '[' or text[inner_e] == '(':
d += 1
elif text[inner_e] == ']':
if d == 0:
break
d -= 1
elif text[inner_e] == ')':
if d > 0:
d -= 1
elif text[inner_e] == ',':
if d == 0:
break
inner_e += 1
return self._spec_sign_of_span(text, inner_s, inner_e, depth)
# 去模块限定前缀:取最后一个 '.' 之后的名字
name_s: sizet = s
w: sizet = s
while w < e:
if text[w] == '.':
name_s = w + 1
w += 1
exact: int = TSpec.SpecSignFromTypeNameSpan(text, name_s, e)
if exact != TSpec.TS_SIGN_UNKNOWN:
return exact
# 精确名未覆盖 → 沿 CTypedef 链展开(depth 限制防环)
if depth >= 16:
return TSpec.TS_SIGN_UNKNOWN
ts: str = self.lookup_ctypedef_span(text, name_s, e)
if ts is None or self.CTypedefFound == 0:
return TSpec.TS_SIGN_UNKNOWN
return self._spec_sign_of_span(ts, 0, string.strlen(ts), depth + 1)
def clear_ctypedefs(self) -> None:
"""清空 CTypedef 表(模块切换时调用)
表本体自首次创建起常驻 PermPool(见 _ctypedef_table_pool)⇒ 此处只
**清空内容**、不丢弃表指针:
- 丢指针会退回"下一次 register_ctypedef 按**调用方 pool** 重建",
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
初始化内存的根因);
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
register 首次 append 即写回第 0 槽。
"""
if self.CTypedefNames is not None:
self.CTypedefNames.clear()
if self.CTypedefTypeStrs is not None:
self.CTypedefTypeStrs.clear()
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
# 与"新建表后首次 register"的索引状态逐字一致)
if self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
string.memset(self.CTypedefNameIdx, 0,
sizet(self.CTypedefNameIdxCap) * 8)
self.CTypedefNameIdxBuilt = 0
else:
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
self.CTypedefResolveDepth = 0
if self.CTypedefVisiting is not None:
self.CTypedefVisiting.clear()
# builtins 源扫描标记随表清空复位:每文件最多读一次 includes/builtins.vp
# (_lookup_builtins_typedef 的"最后匹配"兜底,见 TypeGenResolve)
self.BuiltinsTypedefScanned = 0
self.LastBuiltinsTypedefName = None
self.LastBuiltinsTypedefResult = None
# 别名缓冲 / 诊断缓冲均为 stdlib.malloc 分配,跨 pool 生命周期;
# 此处释放防泄漏(诊断缓冲按需再分配)
if self.LastBuiltinsTypedefNameBuf is not None:
stdlib.free(self.LastBuiltinsTypedefNameBuf)
self.LastBuiltinsTypedefNameBuf = None
if self.LastBuiltinsTypedefResultBuf is not None:
stdlib.free(self.LastBuiltinsTypedefResultBuf)
self.LastBuiltinsTypedefResultBuf = None
if self.ImportAliasBuf is not None:
stdlib.free(self.ImportAliasBuf)
self.ImportAliasBuf = None
def _ctypedef_push_visiting(self, pool: memhub.MemBuddy*, name: str) -> int:
"""将 CTypedef 名加入正在解析列表(循环检测)
Returns:
0=成功加入(无循环),1=检测到循环
"""
if name is None:
return 0
if self.CTypedefVisiting is None:
# 与 CTypedef 表同池(PermPool):clear_ctypedefs 只清空、指针不丢,
# 避免这张表也落在会被 reset_to 回收的调用方池上
vp: memhub.MemBuddy* = self._ctypedef_table_pool(pool)
self.CTypedefVisiting = list[str](vp, 32)
if self.CTypedefVisiting is None:
return 0
vn: sizet = len(self.CTypedefVisiting)
vi: sizet
for vi in range(vn):
vs: str = self.CTypedefVisiting.get(vi)
if vs is not None and string.strcmp(vs, name) == 0:
return 1 # 循环检测
self.CTypedefVisiting.append(name)
return 0
def _ctypedef_pop_visiting(self) -> None:
"""从正在解析列表弹出最后一个 CTypedef 名"""
if self.CTypedefVisiting is None:
return
vn: sizet = len(self.CTypedefVisiting)
if vn > 0:
self.CTypedefVisiting.pop()
def _is_ctypedef_annotation(self, annotation: ast.AST*) -> int:
"""检测类型注解是否为 t.CTypedef
Args:
annotation: 类型注解 AST 节点
Returns:
1=是 t.CTypedef,0=否
"""
if annotation is None:
return 0
k_v: int = annotation.kind()
if k_v != ast.ASTKind.Attribute:
return 0
at: ast.Attribute* = ast.Attribute*(annotation)
# 字段访问改用 ast 模块访问器(直接访问 at.attr 可能按 stub 类型生成错误 GEP,
# 读垃圾地址 → strcmp 崩溃)。
at_attr_v: str = ast.attribute_get_attr(at)
if at_attr_v is None:
return 0
# 限长比较替代 strcmp:attr 是零拷贝引用 src_buf 的 token 字符串,可能无 NUL
# 结尾("CTypedef" 后紧跟空格/其他字符),strcmp 会读穿到页边界 → 崩溃。
# strncmp 只读 8 字节,再校验第 9 字节为终止/分隔符(防 "CTypedefXXX" 误匹配)。
if string.strncmp(at_attr_v, "CTypedef", 8) != 0:
return 0
sep_ch_d: char = at_attr_v[8]
if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' \
and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|':
return 0
at_val_v: ast.AST* = ast.attribute_get_value(at)
if at_val_v is None or at_val_v.kind() != ast.ASTKind.Name:
return 0
mod_nm: ast.Name* = ast.Name*(at_val_v)
mod_id_v: str = ast.name_get_id(mod_nm)
if mod_id_v is None:
return 0
# 同样限长比较("t" 无 NUL 结尾时 strcmp 读穿)
if string.strncmp(mod_id_v, "t", 1) != 0:
return 0
sep_ch_m: char = mod_id_v[1]
if sep_ch_m != '\0' and sep_ch_m != ' ' and sep_ch_m != '\n' \
and sep_ch_m != '\r' and sep_ch_m != '\t' and sep_ch_m != '|':
return 0
return 1
def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy*,
tree: ast.AST*) -> int:
"""扫描 AST 顶层节点,注册所有 NAME: t.CTypedef = Type 声明
在 _translate_module_level 中、模块级变量预注册之前调用,
确保 CTypedef 名称在类型解析时可用。
Args:
pool: 内存池
tree: 模块 AST 根节点
Returns:
注册的 CTypedef 数量
"""
if pool is None or tree is None:
return 0
ch: list[ast.AST*]* = tree.children
if ch is None:
return 0
n: sizet = len(ch)
count: int = 0
i: sizet
for i in range(n):
child: ast.AST* = ch.get(i)
if child is None:
continue
if child.kind() != ast.ASTKind.AnnAssign:
continue
aa: ast.AnnAssign* = ast.AnnAssign*(child)
if aa.annotation is None:
continue
# 检测注解是否为 t.CTypedef
if self._is_ctypedef_annotation(aa.annotation) == 0:
continue
# 提取目标名称(必须是 Name 节点)
if aa.target is None or aa.target.kind() != ast.ASTKind.Name:
continue
tgt: ast.Name* = ast.Name*(aa.target)
tgt_id_v: str = ast.name_get_id(tgt)
if tgt_id_v is None:
continue
# 提取类型表达式字符串(从 value AST 节点)
if aa.value is None:
continue
type_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, aa.value)
if type_str is None:
continue
# 注册 CTypedef
self.register_ctypedef(pool, tgt_id_v, type_str)
count += 1
return count
def register_ctypedef_from_source(self, pool: memhub.MemBuddy*,
src: str) -> int:
"""从源码文本逐行扫描注册 CTypedef(`typedef N = V` 或 `N: t.CTypedef = V`)
stdint.vp 崩溃根因修复:AST 字符串字段(attr/id)token 零拷贝指向
src_buf,跨阶段内容不稳定(悬空),ScanCTypedefDeclarations 读
attr 会 0xC0000005。本函数在 src_buf 有效期内(Phase1 1b translate
前)用纯文本匹配注册,不依赖 AST 节点内容。
"""
if pool is None or src is None:
return 0
# 文件边界防护:clear_ctypedefs 只在 translate 前调用(Phase1Main
# L800),文件级 reset_to(mark1) 回收本文件翻译期 pool 分配后,上一
# 文件的 CTypedefNames 表指针悬空(非 None,list 本身也 pool 分配)。
# 本函数去重遍历(register_ctypedef 内 strcmp)读已回收内存 →
# 0xC0000005(1.log: stdint.vp 翻译时 strcmp 读地址 0x4)。先清表
# 重建,杜绝悬空遍历。翻译期 Handle 按需注册不受影响(L800 会再清)。
self.clear_ctypedefs()
src_len: sizet = string.strlen(src)
count: int = 0
line_start: sizet = 0
i: sizet = 0
# 行解析缓冲("NAME\0VALUE\0"),整文件复用(register_ctypedef 内部自会复制)
td_buf: str = pool.alloc(320)
while i <= src_len:
if i == src_len or src[i] == '\n':
line_len: sizet = i - line_start
if line_len > 0 and td_buf is not None:
# 两种等价形态统一解析:语法糖 `typedef N = V`(builtins.vp 现用
# 形态)与注解 `N: t.CTypedef = V`(stdint.vp 等库模块)。
if self.Gen.TypeGenResolve._parse_typedef_line(td_buf, src, line_start, line_len) != 0:
name_buf: str = td_buf
ty_buf: str = td_buf + string.strlen(td_buf) + 1
self.register_ctypedef(pool, name_buf, ty_buf)
count += 1
line_start = i + 1
i += 1
return count
def _eval_const_int_expr(self, node: ast.AST*,
pool: memhub.MemBuddy* = None,
from_imports: str = None,
imported_modules: str = None,
depth: int = 0) -> int:
"""递归计算常量整数表达式
pool/from_imports/imported_modules:跨模块 CDefine 查找上下文
(t.CArray count 引用依赖模块的 CDefine,如 SHA512_BLOCK_LEN 定义在
hashlib/__sha512.vp,本地 CDefine 表每模块清空,需跨模块回退查找)。
depth:递归深度守卫(防御 AST 损坏成自引用环导致的无限递归——
本函数被 resolve_annotation_type 的 t.CArray count 解析调用,其内部
BinOp 自递归不经过 resolve_annotation_type 的 depth 守卫)。
"""
if node is None:
self.ConstEvalOk = 0
return 0
# 递归深度守卫:BinOp 左右子树成环时无限递归直至栈耗尽。
# fail-fast 终止(fatal_type_error → sys.exit(1)),不静默回落。
if depth > 64:
self.Gen.TypeGenErr.fatal_type_error(node, "常量表达式递归过深(AST 环或嵌套超过 64 层)")
k: int = node.kind()
# Constant: 整数字面量
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_INT:
self.ConstEvalOk = 1
return cn.int_val
self.ConstEvalOk = 0
return 0
# Name: CDefine 常量名(本地表优先,未命中走跨模块查找)
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is None:
self.ConstEvalOk = 0
return 0
val: int = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# 本地表未命中:跨模块 CDefine 查找
# 用 HandlesExpr 模块级委托(self._lookup_cross_module_cdefine 是
# ExprGenMixin 方法,TypeGenMixin 直接调用会生成未定义引用)
if (from_imports is not None or imported_modules is not None) \
and pool is not None:
x_val: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, nm.id, from_imports, imported_modules)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
self.ConstEvalOk = 1
return x_val
self.ConstEvalOk = 0
return 0
self.ConstEvalOk = 1
return val
# Attribute: 模块限定常量名,如 hashlib.MD5_DIGEST_LEN
# (t.CArray[uint8, hashlib.MD5_DIGEST_LEN] 的 count 是 Attribute,
# 复用跨模块 CDefine 查找:*:模块 → 源文件提取;re-export 常量由
# _lookup_cross_module_cdefine 的 from-import 递归子模块兜底)
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
if at.attr is None:
self.ConstEvalOk = 0
return 0
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
at_mod: ast.Name* = ast.Name*(at.value)
if at_mod.id is not None:
if (from_imports is not None or imported_modules is not None) \
and pool is not None:
mod_len: sizet = string.strlen(at_mod.id)
fi_buf: str = pool.alloc(mod_len + 3)
if fi_buf is not None:
fi_buf[0] = '*'
fi_buf[1] = ':'
for mi in range(mod_len):
fi_buf[2 + mi] = at_mod.id[mi]
fi_buf[2 + mod_len] = '\0'
av: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, fi_buf, imported_modules)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
self.ConstEvalOk = 1
return av
self.ConstEvalOk = 0
return 0
# BinOp: 算术/位运算
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
# 递归计算左操作数
lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules, depth + 1)
if self.ConstEvalOk == 0:
return 0
# 递归计算右操作数
rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules, depth + 1)
if self.ConstEvalOk == 0:
return 0
# 根据运算符执行计算
if bop.op == ast.OpKind.Add:
self.ConstEvalOk = 1
return lv + rv
if bop.op == ast.OpKind.Sub:
self.ConstEvalOk = 1
return lv - rv
if bop.op == ast.OpKind.Mult:
self.ConstEvalOk = 1
return lv * rv
if bop.op == ast.OpKind.Div:
# 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355)
if rv == 0:
self.ConstEvalOk = 0
return 0
self.ConstEvalOk = 1
return lv // rv
if bop.op == ast.OpKind.FloorDiv:
if rv == 0:
self.ConstEvalOk = 0
return 0
self.ConstEvalOk = 1
return lv // rv
if bop.op == ast.OpKind.BitOr:
self.ConstEvalOk = 1
return lv | rv
if bop.op == ast.OpKind.BitAnd:
self.ConstEvalOk = 1
return lv & rv
# 不支持的运算符
self.ConstEvalOk = 0
return 0
# 不支持的节点类型
self.ConstEvalOk = 0
return 0
+492
View File
@@ -0,0 +1,492 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import sys
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# TypeGenErr - 错误上报/源行读取/类型推断(fatal_error/infer_* 等)
# ============================================================
novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
"""错误上报/源行读取/类型推断(fatal_error/infer_* 等)"""
def _read_source_line(self, file_path: str, lineno: int) -> str:
"""从源文件读取第 lineno 行内容"""
if file_path is None or lineno <= 0: return None
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
if f.closed: return None
# 按文件实际大小分配缓冲区:固定 256KB 会截断大源文件,末尾行号读不到 →
# 误报"文件不存在或行号超出范围"(如 CallGenMixin.vp 555KB/9236 行的第
# 8390 行)。64MB 上限防御异常超大文件整体读取。
fsize: LONGLONG = f.size()
if fsize <= 0 or fsize > 67108864:
f.close()
return None
src_size: sizet = sizet(fsize)
buf: bytes = stdlib.malloc(src_size + 1)
if buf is None:
f.close()
return None
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
f.close()
if bytes_read <= 0:
stdlib.free(buf)
return None
# 逐行查找第 lineno 行
cur_line: int = 1
line_start: sizet = 0
for i in range(bytes_read):
if buf[i] == '\n':
if cur_line == lineno:
line_len: sizet = i - line_start
if line_len > 0 and buf[line_start + line_len - 1] == '\r':
line_len -= 1
line_buf: bytes = stdlib.malloc(line_len + 1)
if line_buf is not None:
string.strncpy(line_buf, buf + line_start, line_len)
line_buf[line_len] = '\0'
stdlib.free(buf)
return line_buf
stdlib.free(buf)
return None
cur_line += 1
line_start = i + 1
stdlib.free(buf)
return None
def read_source_line(self, file_path: str, lineno: int) -> str:
"""公开包装:从源文件读取第 lineno 行内容(供 traceback 展示)"""
return self._read_source_line(file_path, lineno)
def dump_parse_traceback(self, file_path: str) -> None:
"""解析失败时以 Python 风格输出 traceback(仿 fatal_error)"""
err_line: int = ast.get_parse_err_line()
err_col: int = ast.get_parse_err_col()
err_msg: str = ast.get_parse_err_msg()
stdio.printf("Traceback (most recent call last, compile-time error):\n")
if file_path is None or file_path[0] == 0:
file_path = "<unknown>"
if err_line > 0:
stdio.printf(' File "%s", line %d\n', file_path, err_line)
src: str = self._read_source_line(file_path, err_line)
if src is not None:
stdio.printf(" %s\n", src)
# 列标记:在出错 token 起始列下方标 ^(parser 的 col_offset 是 0-based,
# 前缀 4 空格 + err_col 个空格后落点恰好是该 token 首字符)
if err_col > 0:
stdio.printf(" ")
for _ in range(err_col):
stdio.printf(" ")
stdio.printf("^\n")
stdlib.free(src)
else:
stdio.printf(' File "%s"\n', file_path)
if err_msg is None or err_msg[0] == 0:
err_msg = "语法错误"
stdio.printf("SyntaxError: %s\n", err_msg)
def fatal_error(self, node: ast.AST*, msg: str,
err_type: str = "SyntaxError",
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(支持多帧、错误类型、列范围)
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
"""
stdio.printf("Traceback (most recent call last, compile-time error):\n")
# 输出调用栈帧(从底到顶)
for i in range(self.Gen.TypeGenBase.TraceCount):
frame: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(i)
if frame is not None and frame.file_name is not None:
if frame.lineno > 0:
stdio.printf(' File "%s", line %d\n', frame.file_name, frame.lineno)
src: str = self._read_source_line(frame.file_name, frame.lineno)
if src is not None:
stdio.printf(" %s\n", src)
stdlib.free(src)
else:
stdio.printf(' File "%s"\n', frame.file_name)
# 输出当前帧(node 对应的位置)
lineno: int = 0
if node is not None:
lineno = node.lineno
file_name: str = self.Gen.TypeGenResolve.get_current_file()
if file_name is None:
file_name = "<unknown>"
# 行号兜底:克隆/构造节点 lineno 可能为 0(如泛型特化 _clone_arguments 的
# ast.Arg 构造器硬编码 0,astaux.vp L139)。此时回退到 trace 栈最后一个
# 有效帧(文件+行号)作为出错位置,避免输出误导性的 "line 0"。
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
while ti_b >= 0:
tfb: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(ti_b)
if tfb is not None and tfb.file_name is not None and tfb.lineno > 0:
file_name = tfb.file_name
lineno = tfb.lineno
break
ti_b -= 1
# 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。
# 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信
# (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。
src_line: str = None
line_ok: int = 1
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
src_line = self._read_source_line(file_name, lineno)
if src_line is None:
line_ok = 0
if line_ok == 0:
stdio.printf(' File "%s", line %d (!! 无法读取该行源码:文件不存在或行号超出范围,错误可能来自被复用的 AST 树,请以错误消息为准)\n',
file_name, lineno)
else:
stdio.printf(' File "%s", line %d\n', file_name, lineno)
if src_line is not None:
stdio.printf(" %s\n", src_line)
# 列范围标记:在源代码行下方用 ^ 标记出错范围
if col_start > 0 and col_end >= col_start:
stdio.printf(" ")
j: int
for j in range(col_end):
if j >= col_start - 1 and j < col_end:
stdio.printf("^")
else:
stdio.printf(" ")
stdio.printf("\n")
stdlib.free(src_line)
# 输出错误类型 + 消息
if err_type is None or err_type[0] == 0:
err_type = "SyntaxError"
stdio.printf("%s: %s\n", err_type, msg)
# 退出前强制刷出:stdout 重定向到文件时是全缓冲,直接终止进程会把整段
# traceback 丢掉(实测 2.log 只剩一行 VLogger 输出)。
stdio.fflush(None)
# 终止点收敛:REPL 会话内经 VLogger.repl_abort longjmp 回 REPL 循环
# (Traceback 已打印,进程存活);批处理下该出口即 sys.exit(1),逐字不变。
VLogger.repl_abort()
def fatal_type_error(self, node: ast.AST*, msg: str,
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(TypeError 版本)"""
self.fatal_error(node, msg, "TypeError", col_start, col_end)
def try_extract_callable_ret(self, pool: memhub.MemBuddy*,
annot: ast.AST*,
imported_modules: str,
from_imports: str,
trans: HT.Translator*) -> llvmlite.LLVMType*:
"""提取函数指针注解(t.Callable[[A,B], R] / callable[[A,B], R])的返回类型
LLVMType;非 Callable 注解或无签名形态([]、[[]])返回 None"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return None
sub_cr: ast.Subscript* = ast.Subscript*(annot)
# 构造子判据走 TypeGenResolve 单一真源(Attribute 形态 t.Callable 与
# 裸名形态 callable / Callable 等价)
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0:
return None
# slice 必须是 Tuple(elts=[args_list, ret_node])
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple:
return None
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
if tp_cr.elts is None or len(tp_cr.elts) != 2:
return None
ret_node_cr: ast.AST* = tp_cr.elts.get(1)
if ret_node_cr is None:
return None
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
def has_decorator_marker(self, node: ast.AST*,
marker_name: str,
depth: int = 0) -> int:
"""检测类型注解中是否包含指定的装饰器标记"""
if node is None or marker_name is None:
return 0
# 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数,
# vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813),
# BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。
if depth > 128:
self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)")
k: int = node.kind()
# Attribute 节点: t.CExtern, t.State, t.CExport, t.CInline
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
if at.attr is not None:
if string.strcmp(at.attr, marker_name) == 0:
return 1
return 0
# BinOp 节点: int | t.CExtern(递归检测左右子节点)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0:
return 1
if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0:
return 1
return 0
return 0
def has_pointee_volatile_marker(self, node: ast.AST*) -> int:
"""检测指针注解 rawptr[...] 的下标实参是否含 t.CVolatile 标记,1=是 / 0=否
语义:rawptr[t.CVolatile | T] 等价 C 的 volatile T*,即"指针所指对象按
volatile 访问"——解引用(p[i] / p.field / c.Deref(p))的 load/store 带
LLVM volatile。与 has_decorator_marker 的区别:后者检测变量/字段**自身**
的可见性限定(如 `locked: t.CVolatile | t.CInt`),对 Subscript 节点返回 0;
本函数专检 rawptr 的 pointee 侧标记(下标实参子树),两者互不串味。
标注形态(vast.parse 实证):
rawptr[t.CVolatile | t.CInt]
→ Subscript(value=Name('rawptr'),
slice=BinOp(BitOr, Attribute(t,CVolatile), Attribute(t,CInt)))
多级指针 rawptr[rawptr[t.CVolatile|T]] 的 slice 是 Subscript,
has_decorator_marker 不递归 Subscript ⇒ 返回 0(明确不支持,见限制清单)。
"""
if node is None or node.kind() != ast.ASTKind.Subscript:
return 0
sub: ast.Subscript* = ast.Subscript*(node)
if sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return 0
rp_nm: ast.Name* = ast.Name*(sub.value)
if rp_nm.id is None or string.strcmp(rp_nm.id, "rawptr") != 0:
return 0
if sub.slice is None:
return 0
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
def is_const_object_marker(self, node: ast.AST*,
var_ty: llvmlite.LLVMType*) -> int:
"""变量/字段本身是否 **const 对象**(1=是 / 0=否)。
判据与 TPC 的 BaseGen._IsConstObject **逐条一致**(见 Debugger/type-descriptors.md):
① 注解子树含 t.CConst(has_decorator_marker,与 TPC 的 _AnnotationHasQualifier 同口径);
② 声明类型的 LLVM 形态**不是指针**(TPC 用 PtrCount==0 表达同一件事)。
`t.CConst` 在本语言里承载「指向的数据 const」(对齐 TPC 的 DataConst 语义),
对非指针类型该限定退化为「该对象自身 const」⇒ 登记为 const 变量/字段;
对指针类型(`p: t.CConst | T | t.CPtr`)限定只作用于 pointee ⇒ 指针变量自身
仍可重新赋值,不登记。
"""
if self.has_decorator_marker(node, "CConst", 0) == 0:
return 0
if var_ty is None:
return 0
if var_ty.kind() == llvmlite.LLVMType.Ptr:
return 0
return 1
def is_pass_body(self, body: list[ast.AST*]*) -> int:
"""检测函数体是否为 pass(空体或仅含 pass 语句)"""
if body is None:
return 1
bn: sizet = len(body)
if bn == 0:
return 1
if bn == 1:
stmt: ast.AST* = body.get(0)
if stmt is not None and stmt.kind() == ast.ASTKind.Pass:
return 1
return 0
# 获取 LLVM 整数类型的位宽
# 实现已收拢到 TSpec.SpecTypeBits(该块在 TypeSpec 模块中的唯一实现),此处只做委托。
# 私有调用点(self.get_llvm_type_bits)保持原样不受影响。
def get_llvm_type_bits(self, ty: llvmlite.LLVMType*) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
return TSpec.SpecTypeBits(ty)
def infer_expr_type(self, pool: memhub.MemBuddy*,
node: ast.AST*,
param_types: str) -> llvmlite.LLVMType*:
"""推断表达式的 LLVM 类型
param_types 格式: "name:bits,name:bits,..."
"""
if node is None:
return llvmlite.Int32(pool)
k: int = node.kind()
if k == ast.ASTKind.Constant:
return llvmlite.Int32(pool)
elif k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None and param_types is not None:
# 在 param_types 中查找: "name:bits" 格式
# 键按精确长度分配:固定 64 缓冲 + snprintf("%s:") 会静默截断超长参数名,
# 截断后的键可能与**别的参数前缀**匹配成功 ⇒ 取到错误的位宽(类型误判)。
id_len: sizet = string.strlen(nm.id)
search_key: str = pool.alloc(id_len + 2)
if search_key is not None:
string.strcpy(search_key, nm.id)
search_key[id_len] = ':'
search_key[id_len + 1] = '\0'
found: str = string.strstr(param_types, search_key)
if found is not None:
colon_pos: sizet = id_len + 1
bits_str: str = found + colon_pos
bits: int = 0
for bi in range(4):
ch: char = bits_str[bi]
if ch >= '0' and ch <= '9':
bits = bits * 10 + (ch - '0')
else:
break
if bits == 64:
return llvmlite.Int64(pool)
elif bits == 16:
return llvmlite.Int16(pool)
elif bits == 8:
return llvmlite.Int8(pool)
return llvmlite.Int32(pool)
elif k == ast.ASTKind.BinOp:
binop: ast.BinOp* = ast.BinOp*(node)
lhs_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, binop.left, param_types)
rhs_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, binop.right, param_types)
lhs_bits: int = self.get_llvm_type_bits(lhs_ty)
rhs_bits: int = self.get_llvm_type_bits(rhs_ty)
if lhs_bits >= rhs_bits:
return lhs_ty
return rhs_ty
return llvmlite.Int32(pool)
def infer_return_type(self, pool: memhub.MemBuddy*,
body: list[ast.AST*]*,
param_types: str) -> llvmlite.LLVMType*:
"""扫描函数体中的 return 语句,推断返回类型"""
if body is None:
return llvmlite.Int32(pool)
ret_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
bn: sizet = len(body)
for bi in range(bn):
stmt: ast.AST* = body.get(bi)
if stmt is not None and stmt.kind() == ast.ASTKind.Return:
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
if rt.value is not None:
expr_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, rt.value, param_types)
if expr_ty is not None:
cur_bits: int = self.get_llvm_type_bits(ret_ty)
new_bits: int = self.get_llvm_type_bits(expr_ty)
if new_bits > cur_bits:
ret_ty = expr_ty
return ret_ty
def body_has_value_return(self, pool: memhub.MemBuddy*,
body: list[ast.AST*]*) -> int:
"""扫描函数体,判断是否存在返回非 None 值的 return
递归检查控制流语句(if/while/for/try/with/match)内层 return,但跳过
嵌套函数/类定义(FunctionDef/ClassDef 由自身翻译时独立检查,避免外层
检查误伤嵌套作用域)。裸 return / return None(CONST_NONE)/ 无 return
均不算返回非 None 值。
供无返回类型注解函数的 fail-fast 检查使用:返回具体值必须有显式 ->
注解,编译器不替用户推断返回类型(推断位宽会掩盖返回契约)。
"""
if body is None:
return 0
bn: sizet = len(body)
for bi in range(bn):
stmt: ast.AST* = body.get(bi)
if stmt is None:
continue
k: int = stmt.kind()
if k == ast.ASTKind.Return:
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
if rt.value is not None:
# return None(CONST_NONE)不算返回非 None 值
if rt.value.kind() == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(rt.value)
if cn.const_kind == ast.CONST_NONE:
continue
return 1
elif k == ast.ASTKind.FunctionDef or k == ast.ASTKind.ClassDef:
continue
elif k == ast.ASTKind.If or k == ast.ASTKind.While or \
k == ast.ASTKind.For or k == ast.ASTKind.Try or \
k == ast.ASTKind.With or k == ast.ASTKind.Match:
sub: list[ast.AST*]* = stmt.children
if sub is not None and len(sub) > 0:
if self.body_has_value_return(pool, sub) != 0:
return 1
return 0
def build_param_types_str(self, pool: memhub.MemBuddy*,
args_node: ast.AST*) -> str:
"""构建参数类型编码字符串 "name:bits,name:bits,..."
[D5·截断纪律] 旧实现按 `an * 32 + 1` 预估缓冲,再用 `_strappend` 以
`remain - 1` 截断且**不报错**:参数名偏长(或参数偏多)时编码串被静默
截断 ⇒ `infer_expr_type` 的 `strstr(param_types, "name:")` 查找失配 /
命中他人前缀 ⇒ 静默回落 Int32(类型误判)。现改为**上界预估 + 单遍就地
拼接**:段一是只扫参数名的上界(name + ':' + 位宽 + ',' + NUL),段二是
解析类型后逐条目就地格式化并立即拼接 —— 缓冲恒够,不存在截断;
`_strappend` 一并删除(唯一调用点在本函数)。
"""
if args_node is None:
return None
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is None:
return None
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
if an == 0:
return None
# 1) 上界估长(**只扫参数名**,不解析类型,也不暂存结果指针):
# 每条 = name + ':' + 位宽(int32 十进制最长 11 字符含符号)+ ',';再加 NUL。
# 必须"上界"而非"精确":把条目指针存进 sizet 数组再取出转回指针 strcat 会出错;
# 上界缓冲不需要数组往返,每条目就地格式化、立即拼接。
bound: sizet = 1 # 末尾 NUL
pre_i: sizet
for pre_i in range(an):
pre_arg: ast.Arg* = ast.Arg*(alist.get(pre_i))
if pre_arg is None or pre_arg.arg is None:
continue
bound += string.strlen(pre_arg.arg) + 1 + 12 + 1
buf: str = pool.alloc(bound)
if buf is None:
return None
buf[0] = '\0'
# 2) 单遍:解析类型 → 就地格式化条目 → 立即拼接(缓冲为上界,不会截断)
cnt: sizet = 0
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
for ai in range(an):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is None or arg.arg is None:
continue
param_ty: llvmlite.LLVMType* = i32_ty
if arg.annotation is not None:
# 统一口径:CTypedef 动态表优先,miss 回退 map_t_type
# (阶段 2:删除 map_t_type 硬编码别名后,BYTE 等靠库 typedef)
if arg.annotation.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(arg.annotation)
if at.attr is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, None, None)
if resolved is not None:
param_ty = resolved
bits: int = self.get_llvm_type_bits(param_ty)
if bits == 0:
bits = 32
nlen: sizet = string.strlen(arg.arg)
# 条目缓冲:name + ':' + 位宽(预留 16 字节,足够 %d 任意取值)+ NUL
ent: str = pool.alloc(nlen + 1 + 16 + 1)
if ent is None:
continue
string.strcpy(ent, arg.arg)
ent[nlen] = ':'
viperlib.snprintf(ent + nlen + 1, 16, "%d", bits)
if cnt > 0:
string.strcat(buf, ",")
string.strcat(buf, ent)
cnt += 1
if cnt == 0:
return None
return buf
@@ -11,16 +11,14 @@ import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Gen.TypeGenBase as TypeGenBase
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
# ============================================================
# TypeGenMixin - 类型映射与解析 Mixin(对象化 P2,wiki/14-oo-refactor.md)
#
# 500~1500 行/文件架构重构阶段 D:全部方法已拆至
# TypeGenBase/CDef/CType/Resolve/Err,本文件保留类声明(Generator 继承链入口)。
# TypeGenMixin - 类型映射与解析 Mixin(Generator 继承链入口)
#
# 方法已全部拆至 TypeGenBase/CDef/CType/Resolve/Err,本文件保留类声明。
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# ============================================================
@@ -28,13 +26,12 @@ import lib.core.Gen.TypeGenBase as TypeGenBase
# ============================================================
# 常量(与 HandlesType 保持一致)
# ============================================================
TRACE_MAX: t.CDefine = 32
MAX_CTYPEDEF_DEPTH: t.CDefine = 10
define TRACE_MAX = 32
define MAX_CTYPEDEF_DEPTH = 10
# ============================================================
# TypeGenMixin - 类型映射/注解解析/编译期常量表/错误上报
# ============================================================
@t.NoVTable
class TypeGenMixin(TypeGenBase.TypeGenBase):
novirtual class TypeGenMixin(TypeGenBase.TypeGenBase):
"""类型映射与解析 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-229
View File
@@ -1,229 +0,0 @@
import lib.core.Gen.TypeGenMixin as TypeGenMixin
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
# ============================================================
# TypeGenBase - TypeGenMixin 拆分(阶段 D):字段 + InitTypeGen + trace/临时目录/generic 上下文
#
# 继承 TypeGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesType 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class TypeGenBase:
"""字段 + InitTypeGen + trace/临时目录/generic 上下文"""
# --- 仿 Python Traceback 调用栈(原 _g_trace_frames/_g_trace_count) ---
Gen: Generator.Generator | t.CPtr
TraceFrames: GenTypes.TraceFrame | t.CPtr # malloc 数组(TypeGenMixin.TRACE_MAX 槽)
TraceCount: int
# --- 全局泛型特化上下文(原 _g_generic_tp_names/_g_generic_type_args) ---
GenericTpNames: list[str] | t.CPtr
GenericTypeArgs: list[str] | t.CPtr
# 嵌套安全栈:set_generic_context 压入外层值,clear_generic_context 弹出
# 恢复(原单槽赋值:嵌套特化/泛型函数翻译退出时把外层上下文清成 None →
# 外层方法体后续的 T 替换丢失 → T.__sizeof__() NameError,VectorGenericTest
# 特化 Vector[int].__init__ 回归根因)。
GenericCtxStack: t.CSizeT | t.CPtr # i64* 固定数组:每层 2 个槽(tp_names, type_args 指针)
GenericCtxDepth: int
# --- 全局 CDefine 编译期常量表(原 _g_cdefine_*) ---
CDefineNames: list[str] | t.CPtr
CDefineValues: list[str] | t.CPtr
CDefineDoubleFlags: list[int] | t.CPtr
CDefineStrFlags: list[int] | t.CPtr # 1=字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2")
CDefineIsDouble: int
CDefineIsStr: int
CDefineFound: int
# --- 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) ---
# 2026-08-14:跨模块 CDefine 查找(_lookup_cross_module_cdefine)结果缓存到
# perm_pool,reset_to(mark2)/clear_cdefine_constants 不清除 → 跨文件复用,
# CONST_INT 递归查找只做一次(NewAstTest OOM 根因)。
XmodCdefNames: list[str] | t.CPtr
XmodCdefValues: list[str] | t.CPtr
XmodCdefDoubleFlags: list[int] | t.CPtr
XmodCdefStrFlags: list[int] | t.CPtr
# --- 当前模块 SHA1(原 _g_current_module_sha1) ---
CurrentModuleSha1: str
# --- 全局 CTypedef 表(原 _g_ctypedef_*) ---
CTypedefNames: list[str] | t.CPtr
CTypedefTypeStrs: list[str] | t.CPtr
CTypedefFound: int
CTypedefResolveDepth: int
CTypedefVisiting: list[str] | t.CPtr
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
ConstEvalOk: int
# --- 全局当前文件名(原 _g_current_file) ---
CurrentFile: str
def InitTypeGen(self) -> int:
"""初始化类型 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
if self is None:
return 1
self.TraceFrames = None
self.TraceCount = 0
self.GenericTpNames = None
self.GenericTypeArgs = None
self.GenericCtxStack = None
self.GenericCtxDepth = 0
self.CDefineNames = None
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
self.CDefineFound = 0
self.XmodCdefNames = None
self.XmodCdefValues = None
self.XmodCdefDoubleFlags = None
self.XmodCdefStrFlags = None
self.CurrentModuleSha1 = None
self.CTypedefNames = None
self.CTypedefTypeStrs = None
self.CTypedefFound = 0
self.CTypedefResolveDepth = 0
self.CTypedefVisiting = None
self.ConstEvalOk = 0
self.CurrentFile = None
return 0
# ============================================================
# P2 迁移方法(函数体来自 HandlesType.vp,全局变量改 self.字段,
# 同模块函数调用改 self.方法;签名与 HandlesType 原函数一致)
# ============================================================
def _ensure_trace_init(self) -> int:
"""确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)"""
if self.TraceFrames is not None:
return 1
entry_size: t.CSizeT = GenTypes.TraceFrame.__sizeof__()
self.TraceFrames = CompilerFactory.get_perm_pool().alloc(entry_size * TypeGenMixin.TRACE_MAX)
if self.TraceFrames is None:
return 0
string.memset(self.TraceFrames, 0, entry_size * TypeGenMixin.TRACE_MAX)
return 1
def _get_trace_frame(self, i: int) -> GenTypes.TraceFrame | t.CPtr:
"""获取第 i 个 TraceFrame 槽位"""
if self.TraceFrames is None or i < 0 or i >= TypeGenMixin.TRACE_MAX:
return None
entry_size: t.CSizeT = GenTypes.TraceFrame.__sizeof__()
addr: t.CUInt64T = t.CUInt64T(self.TraceFrames) + i * entry_size
return (GenTypes.TraceFrame | t.CPtr)(t.CVoid(addr, t.CPtr))
def push_trace(self, file_name: str, lineno: int, col_start: int = 0, col_end: int = 0) -> None:
"""压入一个翻译上下文帧(进入新的翻译作用域时调用)"""
if self._ensure_trace_init() == 0:
return
if self.TraceCount >= TypeGenMixin.TRACE_MAX:
return
frame: GenTypes.TraceFrame | t.CPtr = self._get_trace_frame(self.TraceCount)
if frame is None:
return
frame.file_name = file_name
frame.lineno = lineno
frame.col_start = col_start
frame.col_end = col_end
self.TraceCount += 1
def pop_trace(self) -> None:
"""弹出栈顶帧(退出翻译作用域时调用)"""
if self.TraceCount <= 0:
return
self.TraceCount -= 1
frame: GenTypes.TraceFrame | t.CPtr = self._get_trace_frame(self.TraceCount)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
def clear_trace(self) -> None:
"""清空调用栈(每个文件翻译开始前调用)"""
i: int
for i in range(self.TraceCount):
frame: GenTypes.TraceFrame | t.CPtr = self._get_trace_frame(i)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
self.TraceCount = 0
def set_temp_dir(self, temp_dir: str) -> None:
"""设置全局 temp_dir(统一写入 CallGenBase.TempDir,跨子对象经 Gen 指针)"""
self.Gen.CallGenBase.SetTempDir(temp_dir)
def get_temp_dir(self) -> str:
"""获取全局 temp_dir(从 CallGenBase 读取)"""
return self.Gen.CallGenBase.TempDir
def set_current_module_sha1(self, sha1: str) -> None:
"""设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)"""
self.CurrentModuleSha1 = sha1
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy | t.CPtr,
tree: ast.AST | t.CPtr, tag: str):
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
pass
def set_generic_context(self, tp_names: list[str] | t.CPtr,
type_args: list[str] | t.CPtr):
"""设置泛型特化上下文(进入泛型类方法体翻译时调用),嵌套安全
先压栈当前(外层)上下文,再设置新值。clear_generic_context 弹出恢复,
避免嵌套特化/泛型函数翻译退出时清空外层上下文(T.__sizeof__() 回归根因)。
"""
if self.GenericCtxStack is None:
self.GenericCtxStack = stdlib.malloc(8 * 2 * 32)
if self.GenericCtxStack is None:
self.GenericTpNames = tp_names
self.GenericTypeArgs = type_args
return
self.GenericCtxDepth = 0
if self.GenericCtxDepth < 32:
self.GenericCtxStack[self.GenericCtxDepth * 2] = t.CSizeT(self.GenericTpNames)
self.GenericCtxStack[self.GenericCtxDepth * 2 + 1] = t.CSizeT(self.GenericTypeArgs)
self.GenericCtxDepth += 1
self.GenericTpNames = tp_names
self.GenericTypeArgs = type_args
def clear_generic_context(self):
"""清除泛型特化上下文(离开泛型类方法体翻译时调用),恢复外层而非置 None"""
if self.GenericCtxDepth > 0:
self.GenericCtxDepth -= 1
self.GenericTpNames = (list[str] | t.CPtr)(t.CVoid(
self.GenericCtxStack[self.GenericCtxDepth * 2], t.CPtr))
self.GenericTypeArgs = (list[str] | t.CPtr)(t.CVoid(
self.GenericCtxStack[self.GenericCtxDepth * 2 + 1], t.CPtr))
else:
self.GenericTpNames = None
self.GenericTypeArgs = None
def resolve_generic_type_name(self, name: str) -> str:
"""如果 name 是泛型类型参数,返回对应的具体类型名;否则返回 name 本身
用于表达式中的 T 替换(如 T.__sizeof__() → int.__sizeof__())
"""
if name is None:
return name
rv_d: str = name
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
rg_n: t.CSizeT = len(self.GenericTpNames)
rg_i: t.CSizeT
for rg_i in range(rg_n):
rg_tpn: str = self.GenericTpNames.get(rg_i)
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
rg_ta: str = self.GenericTypeArgs.get(rg_i)
if rg_ta is not None:
rv_d = rg_ta
break
return rv_d
-298
View File
@@ -1,298 +0,0 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.TypeGenBase as TypeGenBase
# ============================================================
# TypeGenCDef - TypeGenMixin 拆分(阶段 D):CDefine 编译期常量表(register/lookup/is/set/clear)
#
# 继承 TypeGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesType 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class TypeGenCDef(TypeGenBase.TypeGenBase):
"""CDefine 编译期常量表(register/lookup/is/set/clear)"""
def register_cdefine_constant(self, pool: memhub.MemBuddy | t.CPtr,
name: str, value: t.CInt64T) -> None:
"""注册 CDefine 编译期常量到本地表
值以十进制字符串存储(64 位 %lld)。大整数 CDefine(如
sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断
成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。
0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。
"""
if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64)
self.CDefineNames.append(name)
val_buf: str = pool.alloc(32)
if val_buf is not None:
viperlib.snprintf(val_buf, 32, "%lld", value)
self.CDefineValues.append(val_buf)
else:
self.CDefineValues.append("0")
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(0)
def register_cdefine_double(self, pool: memhub.MemBuddy | t.CPtr,
name: str, value: double) -> None:
"""注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...)
值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。
"""
if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64)
self.CDefineNames.append(name)
val_buf: str = pool.alloc(32)
if val_buf is not None:
# 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的
# %g 浮点路径有 bug(%.17g 输出错误值,如 pi 显示 3.1397985 而非 3.1415926,
# 位模式 %a 正确),会导致 CDefine 表存错文本 → lookup 生成错 LLVM 常量。
stdio.snprintf(val_buf, 32, "%.17g", value)
self.CDefineValues.append(val_buf)
else:
self.CDefineValues.append("0")
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(1)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(0)
# fail-fast:CDefine 值缓冲区分配失败必须终止编译,
if val_buf is None:
VLogger.error("CDefine 浮点值缓冲区分配失败", "CDEF-DBG")
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点值缓冲区分配失败", "MemoryError")
def register_cdefine_string(self, pool: memhub.MemBuddy | t.CPtr,
name: str, value: str) -> None:
"""注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2")
字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写,
与浮点 CDefine 的恢复同理)。
"""
if pool is None or name is None:
return
if value is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值缺失: {name}", "InternalCompilerError")
return
if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64)
self.CDefineNames.append(name)
vlen: t.CSizeT = string.strlen(value)
vcopy: str = pool.alloc(vlen + 1)
if vcopy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值分配失败: {name}", "MemoryError")
return
string.strcpy(vcopy, value)
self.CDefineValues.append(vcopy)
if self.CDefineDoubleFlags is not None:
self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None:
self.CDefineStrFlags.append(1)
# 同步设置"最近注册/查表结果为字符串"标志(is_cdefine_str() 读取)。
# 跨模块字符串 CDefine 读回(_lookup_cross_module_cdefine 字符串分支只调
# 本函数 + 尾部 set_cdefine_found,无 lookup_cdefine_string 回查)→ 若此处
# 不设标志,属性路径 is_cdefine_str()==0 → 退化为整数分支返回 0 →
# IR 生成 inttoptr i32 0(NULL)→ 运行时报 (null-) + strlen(NULL-) 崩溃
# (test_pyzlib.vp pyzlib.ZLIB_VERSION 根因)。
self.CDefineIsStr = 1
self.CDefineFound = 1
# ============================================================
# 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用)
# ============================================================
def register_xmod_cdefine(self, name: str, value_str: str,
is_double: int, is_str: int) -> None:
"""注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(跨文件复用)
Args:
name: CDefine 常量名(如 CONST_INT)
value_str: 值文本(十进制/浮点/字符串)
is_double: 1=浮点值
is_str: 1=字符串值
"""
if name is None or value_str is None:
return
# 去重:同名已缓存则跳过(首次查找结果为准,CDefine 全局唯一约定)
if self.XmodCdefNames is not None:
x_n: t.CSizeT = len(self.XmodCdefNames)
x_i: t.CSizeT
for x_i in range(x_n):
x_nm: str = self.XmodCdefNames.get(x_i)
if x_nm is not None and string.strcmp(x_nm, name) == 0:
return
perm: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool()
if self.XmodCdefNames is None:
self.XmodCdefNames = list[str](perm, 64)
self.XmodCdefValues = list[str](perm, 64)
self.XmodCdefDoubleFlags = list[int](perm, 64)
self.XmodCdefStrFlags = list[int](perm, 64)
# key/value 深拷贝到 perm_pool:入参(pool.alloc 临时缓冲)在
# reset_to(mark2) 后悬空,append 只存指针 → 必须深拷贝跨 reset 存活
klen: t.CSizeT = string.strlen(name)
kcopy: str = perm.alloc(klen + 1)
if kcopy is None:
return
string.strcpy(kcopy, name)
vlen: t.CSizeT = string.strlen(value_str)
vcopy: str = perm.alloc(vlen + 1)
if vcopy is None:
return
string.strcpy(vcopy, value_str)
self.XmodCdefNames.append(kcopy)
self.XmodCdefValues.append(vcopy)
if self.XmodCdefDoubleFlags is not None:
self.XmodCdefDoubleFlags.append(is_double)
if self.XmodCdefStrFlags is not None:
self.XmodCdefStrFlags.append(is_str)
def lookup_xmod_cdefine(self, name: str) -> str:
"""查找跨模块 CDefine 缓存,命中设置 found/is_double/is_str 标志
Returns:
值文本(命中),未命中返回 None
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if name is None or self.XmodCdefNames is None:
return None
n: t.CSizeT = len(self.XmodCdefNames)
i: t.CSizeT
for i in range(n):
nm: str = self.XmodCdefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.XmodCdefValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1:
self.CDefineIsStr = 1
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
return val_str
return None
def lookup_cdefine_constant(self, name: str) -> t.CInt64T:
"""查找 CDefine 编译期常量
返回找到的值(64 位,大整数 CDefine 如 sys.maxsize 全精度)。
由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1),
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
调用方应在调用后检查 _g_cdefine_found。
浮点 CDefine 项返回整数部分(与 _g_cdefine_is_double 标志配合)。
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return 0
n: t.CSizeT = len(self.CDefineNames)
i: t.CSizeT
for i in range(n):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
# 字符串 CDefine(如 ZLIB_VERSION="1.3.2"):整数查询路径
# 标记 is_str,调用方改用 lookup_cdefine_string 取字符串。
self.CDefineIsStr = 1
return 0
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
return string.atoll(val_str)
return string.atoll(val_str)
return 0
def lookup_cdefine_double(self, name: str) -> double:
"""查找 CDefine 编译期常量,返回 double 值(浮点/整数统一)
与 lookup_cdefine_constant 共享 _g_cdefine_found / _g_cdefine_is_double
(found=1 且 is_double=1 → 浮点 CDefine,应返回浮点常量)。
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return 0.0
n: t.CSizeT = len(self.CDefineNames)
i: t.CSizeT
for i in range(n):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
self.CDefineIsStr = 1
return 0.0
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
self.CDefineIsDouble = 1
ret_d: double = string.atof(val_str)
return ret_d
ret_i2: double = double(string.atoll(val_str))
return ret_i2
return 0.0
def lookup_cdefine_string(self, name: str) -> str:
"""查找 CDefine 编译期字符串值(found=1 且 is_str=1 时有效)
返回的字符串指针位于 pool 内(当前模块生命周期内有效),
调用方应尽快用于 create_global_string 拷贝到 LLVM 常量。
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None:
return None
n: t.CSizeT = len(self.CDefineNames)
i: t.CSizeT
for i in range(n):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
self.CDefineIsStr = 1
return val_str
return val_str
return None
def is_cdefine_str(self) -> int:
"""检查最近一次 CDefine 查找命中的是否是字符串常量"""
return self.CDefineIsStr
def is_cdefine_double(self) -> int:
"""检查最近一次 CDefine 查找命中的是否是浮点常量"""
return self.CDefineIsDouble
def is_cdefine_found(self) -> int:
"""检查上次 lookup_cdefine_constant 是否找到值"""
return self.CDefineFound
def set_cdefine_found(self, val: int) -> None:
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
self.CDefineFound = val
def clear_cdefine_constants(self) -> None:
"""清空 CDefine 常量表(模块切换时调用)"""
self.CDefineNames = None
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
self.CDefineIsStr = 0
-423
View File
@@ -1,423 +0,0 @@
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.TypeGenBase as TypeGenBase
# ============================================================
# TypeGenCType - TypeGenMixin 拆分(阶段 D):CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)
#
# 继承 TypeGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesType 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class TypeGenCType(TypeGenBase.TypeGenBase):
"""CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)"""
def register_ctypedef(self, pool: memhub.MemBuddy | t.CPtr,
name: str, type_str: str) -> None:
"""注册 CTypedef 到本地表
Args:
pool: 内存池
name: CTypedef 名称(如 "HANDLE")
type_str: 类型表达式字符串(如 "VOIDPTR" 或 "t.CVoid | t.CPtr")
"""
if name is None or type_str is None or pool is None:
return
if self.CTypedefNames is None:
self.CTypedefNames = list[str](pool, 64)
self.CTypedefTypeStrs = list[str](pool, 64)
# 去重:若已存在同名 CTypedef,更新其类型字符串
if self.CTypedefNames is not None:
exist_n: t.CSizeT = len(self.CTypedefNames)
ei: t.CSizeT
for ei in range(exist_n):
en: str = self.CTypedefNames.get(ei)
if en is not None and string.strcmp(en, name) == 0:
# 已存在,更新类型字符串
self.CTypedefTypeStrs.set(ei, type_str)
return
self.CTypedefNames.append(name) # 存疑
self.CTypedefTypeStrs.append(type_str)
def lookup_ctypedef(self, name: str) -> str:
"""查找 CTypedef,返回类型表达式字符串
返回找到的类型表达式字符串,未找到返回 None。
调用方应在调用后检查 _g_ctypedef_found 标志区分"未找到"和"空字符串"。
"""
self.CTypedefFound = 0
if name is None:
return None
if self.CTypedefNames is None:
return None
n: t.CSizeT = len(self.CTypedefNames)
i: t.CSizeT
for i in range(n):
nm: str = self.CTypedefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
ts: str = self.CTypedefTypeStrs.get(i)
self.CTypedefFound = 1
return ts
return None
def is_ctypedef_found(self) -> int:
"""检查上次 lookup_ctypedef 是否找到值"""
return self.CTypedefFound
def clear_ctypedefs(self) -> None:
"""清空 CTypedef 表(模块切换时调用)"""
self.CTypedefNames = None
self.CTypedefTypeStrs = None
self.CTypedefResolveDepth = 0
self.CTypedefVisiting = None
def _ctypedef_push_visiting(self, pool: memhub.MemBuddy | t.CPtr, name: str) -> int:
"""将 CTypedef 名加入正在解析列表(循环检测)
Returns:
0=成功加入(无循环),1=检测到循环
"""
if name is None:
return 0
if self.CTypedefVisiting is None:
self.CTypedefVisiting = list[str](pool, 32)
if self.CTypedefVisiting is None:
return 0
vn: t.CSizeT = len(self.CTypedefVisiting)
vi: t.CSizeT
for vi in range(vn):
vs: str = self.CTypedefVisiting.get(vi)
if vs is not None and string.strcmp(vs, name) == 0:
return 1 # 循环检测
self.CTypedefVisiting.append(name)
return 0
def _ctypedef_pop_visiting(self) -> None:
"""从正在解析列表弹出最后一个 CTypedef 名"""
if self.CTypedefVisiting is None:
return
vn: t.CSizeT = len(self.CTypedefVisiting)
if vn > 0:
self.CTypedefVisiting.pop()
def _is_ctypedef_annotation(self, annotation: ast.AST | t.CPtr) -> int:
"""检测类型注解是否为 t.CTypedef
Args:
annotation: 类型注解 AST 节点
Returns:
1=是 t.CTypedef,0=否
"""
if annotation is None:
return 0
k_v: int = annotation.kind()
if k_v != ast.ASTKind.Attribute:
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annotation)
# 字段访问改用 ast 模块访问器(跨模块 stub 字段访问陷阱防护:
# TypeGenCType 直接访问 at.attr 可能按 stub 类型生成错误 GEP,
# 读垃圾地址 → strcmp 0xC0000005 崩溃,stdint.vp 根因)。
at_attr_v: str = ast.attribute_get_attr(at)
if at_attr_v is None:
return 0
# 限长比较替代 strcmp:attr 是零拷贝引用 src_buf 的 token 字符串,
# 可能无 NUL 结尾("CTypedef" 后紧跟空格/其他字符),strcmp 会
# 无限读穿到页边界 → 0xC0000005(stdint.vp 崩溃根因)。strncmp
# 只读 8 字节,再校验第 9 字节为终止/分隔符(防 "CTypedefXXX" 误匹配)。
if string.strncmp(at_attr_v, "CTypedef", 8) != 0:
return 0
sep_ch_d: t.CChar = at_attr_v[8]
if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' \
and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|':
return 0
at_val_v: ast.AST | t.CPtr = ast.attribute_get_value(at)
if at_val_v is None or at_val_v.kind() != ast.ASTKind.Name:
return 0
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_val_v)
mod_id_v: str = ast.name_get_id(mod_nm)
if mod_id_v is None:
return 0
# 同样限长比较("t" 无 NUL 结尾时 strcmp 读穿)
if string.strncmp(mod_id_v, "t", 1) != 0:
return 0
sep_ch_m: t.CChar = mod_id_v[1]
if sep_ch_m != '\0' and sep_ch_m != ' ' and sep_ch_m != '\n' \
and sep_ch_m != '\r' and sep_ch_m != '\t' and sep_ch_m != '|':
return 0
return 1
def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy | t.CPtr,
tree: ast.AST | t.CPtr) -> int:
"""扫描 AST 顶层节点,注册所有 NAME: t.CTypedef = Type 声明
在 _translate_module_level 中、模块级变量预注册之前调用,
确保 CTypedef 名称在类型解析时可用。
Args:
pool: 内存池
tree: 模块 AST 根节点
Returns:
注册的 CTypedef 数量
"""
if pool is None or tree is None:
return 0
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is None:
return 0
n: t.CSizeT = len(ch)
count: int = 0
i: t.CSizeT
for i in range(n):
child: ast.AST | t.CPtr = ch.get(i)
if child is None:
continue
if child.kind() != ast.ASTKind.AnnAssign:
continue
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child)
if aa.annotation is None:
continue
# 检测注解是否为 t.CTypedef
if self._is_ctypedef_annotation(aa.annotation) == 0:
continue
# 提取目标名称(必须是 Name 节点)
if aa.target is None or aa.target.kind() != ast.ASTKind.Name:
continue
tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target)
tgt_id_v: str = ast.name_get_id(tgt)
if tgt_id_v is None:
continue
# 提取类型表达式字符串(从 value AST 节点)
if aa.value is None:
continue
type_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, aa.value)
if type_str is None:
continue
# 注册 CTypedef
self.register_ctypedef(pool, tgt_id_v, type_str)
count += 1
return count
def register_ctypedef_from_source(self, pool: memhub.MemBuddy | t.CPtr,
src: str) -> int:
"""从源码文本逐行扫描注册 CTypedef(NAME: t.CTypedef = TYPE)
stdint.vp 崩溃根因修复:AST 字符串字段(attr/id)token 零拷贝指向
src_buf,跨阶段内容不稳定(悬空),ScanCTypedefDeclarations 读
attr 会 0xC0000005。本函数在 src_buf 有效期内(Phase1 1b translate
前)用纯文本匹配注册,不依赖 AST 节点内容。
"""
if pool is None or src is None:
return 0
# 文件边界防护:clear_ctypedefs 只在 translate 前调用(Phase1Main
# L800),文件级 reset_to(mark1) 回收本文件翻译期 pool 分配后,上一
# 文件的 CTypedefNames 表指针悬空(非 None,list 本身也 pool 分配)。
# 本函数去重遍历(register_ctypedef 内 strcmp)读已回收内存 →
# 0xC0000005(1.log: stdint.vp 翻译时 strcmp 读地址 0x4)。先清表
# 重建,杜绝悬空遍历。翻译期 Handle 按需注册不受影响(L800 会再清)。
self.clear_ctypedefs()
src_len: t.CSizeT = string.strlen(src)
count: int = 0
line_start: t.CSizeT = 0
i: t.CSizeT = 0
while i <= src_len:
if i == src_len or src[i] == '\n':
line_len: t.CSizeT = i - line_start
if line_len > 0:
# 找行内第一个 ':'(跳过注释行)
j: t.CSizeT = 0
found_colon: int = 0
while j < line_len:
ch_c: t.CChar = src[line_start + j]
if ch_c == ':':
found_colon = 1
break
if ch_c == '#' or ch_c == '\r':
break
j += 1
if found_colon != 0:
# 左段 NAME(trim 尾随空格)
name_len: t.CSizeT = j
while name_len > 0 and src[line_start + name_len - 1] == ' ':
name_len -= 1
if name_len > 0 and name_len < 128:
# 右段必须含 "t.CTypedef"
rest: str = src + line_start + j + 1
if string.strstr(rest, "t.CTypedef") is not None:
# 提取 "=" 后的类型表达式(trim 空格)
eq_ptr: str = string.strstr(rest, "=")
if eq_ptr is not None:
ty_start: str = eq_ptr + 1
while ty_start[0] == ' ':
ty_start += 1
ty_end: t.CSizeT = string.strlen(ty_start)
while ty_end > 0 and (ty_start[ty_end - 1] == ' '
or ty_start[ty_end - 1] == '\r'):
ty_end -= 1
if ty_end > 0:
name_buf: str = pool.alloc(name_len + 1)
ty_buf: str = pool.alloc(ty_end + 1)
if name_buf is not None and ty_buf is not None:
string.strncpy(name_buf, src + line_start, name_len)
name_buf[name_len] = '\0'
string.strncpy(ty_buf, ty_start, ty_end)
ty_buf[ty_end] = '\0'
self.register_ctypedef(pool, name_buf, ty_buf)
count += 1
line_start = i + 1
i += 1
return count
def _eval_const_int_expr(self, node: ast.AST | t.CPtr,
pool: memhub.MemBuddy | t.CPtr = None,
from_imports: str = None,
imported_modules: str = None,
depth: int = 0) -> int:
"""递归计算常量整数表达式
pool/from_imports/imported_modules:跨模块 CDefine 查找上下文
(t.CArray count 引用依赖模块的 CDefine,如 SHA512_BLOCK_LEN 定义在
hashlib/__sha512.vp,本地 CDefine 表每模块清空,需跨模块回退查找)。
depth:递归深度守卫(防御 AST 损坏成自引用环导致的无限递归——
本函数被 resolve_annotation_type 的 t.CArray count 解析调用,其内部
BinOp 自递归不经过 resolve_annotation_type 的 depth 守卫)。
"""
if node is None:
self.ConstEvalOk = 0
return 0
# 递归深度守卫:BinOp 左右子树成环时无限递归直至栈耗尽。
# fail-fast 终止(fatal_type_error → sys.exit(1)),不静默回落。
if depth > 64:
self.Gen.TypeGenErr.fatal_type_error(node, "常量表达式递归过深(AST 环或嵌套超过 64 层)")
k: int = node.kind()
# Constant: 整数字面量
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT:
self.ConstEvalOk = 1
return cn.int_val
self.ConstEvalOk = 0
return 0
# Name: CDefine 常量名(本地表优先,未命中走跨模块查找)
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is None:
self.ConstEvalOk = 0
return 0
val: int = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# 本地表未命中:跨模块 CDefine 查找
# 用 HandlesExpr 模块级委托(self._lookup_cross_module_cdefine 是
# ExprGenMixin 方法,TypeGenMixin 直接调用会生成未定义引用)
if (from_imports is not None or imported_modules is not None) \
and pool is not None:
import lib.core.Handles.HandlesExpr as HandlesExpr
x_val: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(
pool, nm.id, from_imports, imported_modules)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
self.ConstEvalOk = 1
return x_val
# 定位日志:跨模块 CDefine 查找失败(t.CArray count 场景)
fb_cdf: str = VLogger.fmt_buf()
if fb_cdf is not None:
viperlib.snprintf(fb_cdf, 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(fb_cdf, "CDEF")
self.ConstEvalOk = 0
return 0
self.ConstEvalOk = 1
return val
# Attribute: 模块限定常量名,如 hashlib.MD5_DIGEST_LEN
# (t.CArray[t.CUInt8T, hashlib.MD5_DIGEST_LEN] 的 count 是 Attribute,
# 复用跨模块 CDefine 查找:*:模块 → 源文件提取;re-export 常量由
# _lookup_cross_module_cdefine 的 from-import 递归子模块兜底)
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at.attr is None:
self.ConstEvalOk = 0
return 0
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
at_mod: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if at_mod.id is not None:
if (from_imports is not None or imported_modules is not None) \
and pool is not None:
mod_len: t.CSizeT = string.strlen(at_mod.id)
fi_buf: str = pool.alloc(mod_len + 3)
if fi_buf is not None:
fi_buf[0] = '*'
fi_buf[1] = ':'
for mi in range(mod_len):
fi_buf[2 + mi] = at_mod.id[mi]
fi_buf[2 + mod_len] = '\0'
import lib.core.Handles.HandlesExpr as HandlesExpr
av: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(
pool, at.attr, fi_buf, imported_modules)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
self.ConstEvalOk = 1
return av
self.ConstEvalOk = 0
return 0
# BinOp: 算术/位运算
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
# 递归计算左操作数
lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules, depth + 1)
if self.ConstEvalOk == 0:
return 0
# 递归计算右操作数
rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules, depth + 1)
if self.ConstEvalOk == 0:
return 0
# 根据运算符执行计算
if bop.op == ast.OpKind.Add:
self.ConstEvalOk = 1
return lv + rv
if bop.op == ast.OpKind.Sub:
self.ConstEvalOk = 1
return lv - rv
if bop.op == ast.OpKind.Mult:
self.ConstEvalOk = 1
return lv * rv
if bop.op == ast.OpKind.Div:
# 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355)
if rv == 0:
self.ConstEvalOk = 0
return 0
self.ConstEvalOk = 1
return lv // rv
if bop.op == ast.OpKind.FloorDiv:
if rv == 0:
self.ConstEvalOk = 0
return 0
self.ConstEvalOk = 1
return lv // rv
if bop.op == ast.OpKind.BitOr:
self.ConstEvalOk = 1
return lv | rv
if bop.op == ast.OpKind.BitAnd:
self.ConstEvalOk = 1
return lv & rv
# 不支持的运算符
self.ConstEvalOk = 0
return 0
# 不支持的节点类型
self.ConstEvalOk = 0
return 0
-396
View File
@@ -1,396 +0,0 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import sys
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Gen.TypeGenBase as TypeGenBase
# ============================================================
# TypeGenErr - TypeGenMixin 拆分(阶段 D):错误上报/源行读取/类型推断(fatal_error/infer_* 等)
#
# 继承 TypeGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesType 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class TypeGenErr(TypeGenBase.TypeGenBase):
"""错误上报/源行读取/类型推断(fatal_error/infer_* 等)"""
def _read_source_line(self, file_path: str, lineno: int) -> str:
"""从源文件读取第 lineno 行内容"""
if file_path is None or lineno <= 0:
return None
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
if f.closed:
return None
# 按文件实际大小分配缓冲区:固定 256KB 会截断大源文件,末尾行号读不到 →
# 误报"文件不存在或行号超出范围"(如 CallGenMixin.vp 555KB/9236 行的第
# 8390 行)。64MB 上限防御异常超大文件整体读取。
fsize: LONGLONG = f.size()
if fsize <= 0 or fsize > 67108864:
f.close()
return None
src_size: t.CSizeT = t.CSizeT(fsize)
buf: bytes = stdlib.malloc(src_size + 1)
if buf is None:
f.close()
return None
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
f.close()
if bytes_read <= 0:
stdlib.free(buf)
return None
# 逐行查找第 lineno 行
cur_line: int = 1
line_start: t.CSizeT = 0
for i in range(bytes_read):
if buf[i] == '\n':
if cur_line == lineno:
line_len: t.CSizeT = i - line_start
if line_len > 0 and buf[line_start + line_len - 1] == '\r':
line_len -= 1
line_buf: bytes = stdlib.malloc(line_len + 1)
if line_buf is not None:
string.strncpy(line_buf, buf + line_start, line_len)
line_buf[line_len] = '\0'
stdlib.free(buf)
return line_buf
stdlib.free(buf)
return None
cur_line += 1
line_start = i + 1
stdlib.free(buf)
return None
def read_source_line(self, file_path: str, lineno: int) -> str:
"""公开包装:从源文件读取第 lineno 行内容(供 traceback 展示)"""
return self._read_source_line(file_path, lineno)
def dump_parse_traceback(self, file_path: str) -> None:
"""解析失败时以 Python 风格输出 traceback(仿 fatal_error)"""
err_line: int = ast.get_parse_err_line()
err_col: int = ast.get_parse_err_col()
err_msg: str = ast.get_parse_err_msg()
stdio.printf("Traceback (most recent call last):\n")
if file_path is None or file_path[0] == 0:
file_path = "<unknown>"
if err_line > 0:
stdio.printf(' File "%s", line %d\n', file_path, err_line)
src: str = self._read_source_line(file_path, err_line)
if src is not None:
stdio.printf(" %s\n", src)
# 列标记:在出错 token 起始列下方标 ^(parser 的 col_offset 是 0-based,
# 前缀 4 空格 + err_col 个空格后落点恰好是该 token 首字符)
if err_col > 0:
stdio.printf(" ")
c: int
for c in range(err_col):
stdio.printf(" ")
stdio.printf("^\n")
stdlib.free(src)
else:
stdio.printf(' File "%s"\n', file_path)
if err_msg is None or err_msg[0] == 0:
err_msg = "语法错误"
stdio.printf("SyntaxError: %s\n", err_msg)
def fatal_error(self, node: ast.AST | t.CPtr, msg: str,
err_type: str = "SyntaxError",
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(支持多帧、错误类型、列范围)
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
"""
stdio.printf("Traceback (most recent call last):\n")
# 输出调用栈帧(从底到顶)
i: int
for i in range(self.Gen.TypeGenBase.TraceCount):
frame: GenTypes.TraceFrame | t.CPtr = self.Gen.TypeGenBase._get_trace_frame(i)
if frame is not None and frame.file_name is not None:
if frame.lineno > 0:
stdio.printf(' File "%s", line %d\n', frame.file_name, frame.lineno)
src: str = self._read_source_line(frame.file_name, frame.lineno)
if src is not None:
stdio.printf(" %s\n", src)
stdlib.free(src)
else:
stdio.printf(' File "%s"\n', frame.file_name)
# 输出当前帧(node 对应的位置)
lineno: int = 0
if node is not None:
lineno = node.lineno
file_name: str = self.Gen.TypeGenResolve.get_current_file()
if file_name is None:
file_name = "<unknown>"
# 行号兜底:克隆/构造节点 lineno 可能为 0(如泛型特化 _clone_arguments 的
# ast.Arg 构造器硬编码 0,astaux.vp L139)。此时回退到 trace 栈最后一个
# 有效帧(文件+行号)作为出错位置,避免输出误导性的 "line 0"。
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
while ti_b >= 0:
tfb: GenTypes.TraceFrame | t.CPtr = self.Gen.TypeGenBase._get_trace_frame(ti_b)
if tfb is not None and tfb.file_name is not None and tfb.lineno > 0:
file_name = tfb.file_name
lineno = tfb.lineno
break
ti_b -= 1
# 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。
# 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信
# (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。
src_line: str = None
line_ok: int = 1
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
src_line = self._read_source_line(file_name, lineno)
if src_line is None:
line_ok = 0
if line_ok == 0:
stdio.printf(' File "%s", line %d (!! 无法读取该行源码:文件不存在或行号超出范围,错误可能来自被复用的 AST 树,请以错误消息为准)\n',
file_name, lineno)
else:
stdio.printf(' File "%s", line %d\n', file_name, lineno)
if src_line is not None:
stdio.printf(" %s\n", src_line)
# 列范围标记:在源代码行下方用 ^ 标记出错范围
if col_start > 0 and col_end >= col_start:
stdio.printf(" ")
j: int
for j in range(col_end):
if j >= col_start - 1 and j < col_end:
stdio.printf("^")
else:
stdio.printf(" ")
stdio.printf("\n")
stdlib.free(src_line)
# 输出错误类型 + 消息
if err_type is None or err_type[0] == 0:
err_type = "SyntaxError"
stdio.printf("%s: %s\n", err_type, msg)
sys.exit(1)
def fatal_type_error(self, node: ast.AST | t.CPtr, msg: str,
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(TypeError 版本)"""
self.fatal_error(node, msg, "TypeError", col_start, col_end)
def try_extract_callable_ret(self, pool: memhub.MemBuddy | t.CPtr,
annot: ast.AST | t.CPtr,
imported_modules: str,
from_imports: str,
trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""提取 t.Callable 注解的返回类型 LLVMType;非 Callable 注解返回 None"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return None
sub_cr: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub_cr.value is None or sub_cr.value.kind() != ast.ASTKind.Attribute:
return None
at_cr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub_cr.value)
if at_cr.attr is None or string.strcmp(at_cr.attr, "Callable") != 0:
return None
# slice 必须是 Tuple(elts=[args_list, ret_node])
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple:
return None
tp_cr: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub_cr.slice)
if tp_cr.elts is None or len(tp_cr.elts) != 2:
return None
ret_node_cr: ast.AST | t.CPtr = tp_cr.elts.get(1)
if ret_node_cr is None:
return None
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
def has_decorator_marker(self, node: ast.AST | t.CPtr,
marker_name: str,
depth: int = 0) -> int:
"""检测类型注解中是否包含指定的装饰器标记"""
if node is None or marker_name is None:
return 0
# 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数,
# vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813),
# BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。
if depth > 128:
self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)")
k: int = node.kind()
# Attribute 节点: t.CExtern, t.State, t.CExport, t.CInline
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at.attr is not None:
if string.strcmp(at.attr, marker_name) == 0:
return 1
return 0
# BinOp 节点: int | t.CExtern(递归检测左右子节点)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0:
return 1
if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0:
return 1
return 0
return 0
def is_pass_body(self, body: list[ast.AST | t.CPtr] | t.CPtr) -> int:
"""检测函数体是否为 pass(空体或仅含 pass 语句)"""
if body is None:
return 1
bn: t.CSizeT = len(body)
if bn == 0:
return 1
if bn == 1:
stmt: ast.AST | t.CPtr = body.get(0)
if stmt is not None and stmt.kind() == ast.ASTKind.Pass:
return 1
return 0
def get_llvm_type_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Int(bits):
return bits
case _:
return 0
def infer_expr_type(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
param_types: str) -> llvmlite.LLVMType | t.CPtr:
"""推断表达式的 LLVM 类型
param_types 格式: "name:bits,name:bits,..."
"""
if node is None:
return llvmlite.Int32(pool)
k: int = node.kind()
if k == ast.ASTKind.Constant:
return llvmlite.Int32(pool)
elif k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None and param_types is not None:
# 在 param_types 中查找: "name:bits" 格式
search_key: str = pool.alloc(64)
if search_key is not None:
viperlib.snprintf(search_key, 64, "%s:", nm.id)
found: str = string.strstr(param_types, search_key)
if found is not None:
colon_pos: t.CSizeT = string.strlen(nm.id) + 1
bits_str: str = found + colon_pos
bits: int = 0
for bi in range(4):
ch: t.CChar = bits_str[bi]
if ch >= '0' and ch <= '9':
bits = bits * 10 + (ch - '0')
else:
break
if bits == 64:
return llvmlite.Int64(pool)
elif bits == 16:
return llvmlite.Int16(pool)
elif bits == 8:
return llvmlite.Int8(pool)
return llvmlite.Int32(pool)
elif k == ast.ASTKind.BinOp:
binop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
lhs_ty: llvmlite.LLVMType | t.CPtr = self.infer_expr_type(
pool, binop.left, param_types)
rhs_ty: llvmlite.LLVMType | t.CPtr = self.infer_expr_type(
pool, binop.right, param_types)
lhs_bits: int = self.get_llvm_type_bits(lhs_ty)
rhs_bits: int = self.get_llvm_type_bits(rhs_ty)
if lhs_bits >= rhs_bits:
return lhs_ty
return rhs_ty
return llvmlite.Int32(pool)
def infer_return_type(self, pool: memhub.MemBuddy | t.CPtr,
body: list[ast.AST | t.CPtr] | t.CPtr,
param_types: str) -> llvmlite.LLVMType | t.CPtr:
"""扫描函数体中的 return 语句,推断返回类型"""
if body is None:
return llvmlite.Int32(pool)
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
bn: t.CSizeT = len(body)
for bi in range(bn):
stmt: ast.AST | t.CPtr = body.get(bi)
if stmt is not None and stmt.kind() == ast.ASTKind.Return:
rt: ast.Return | t.CPtr = (ast.Return | t.CPtr)(stmt)
if rt.value is not None:
expr_ty: llvmlite.LLVMType | t.CPtr = self.infer_expr_type(
pool, rt.value, param_types)
if expr_ty is not None:
cur_bits: int = self.get_llvm_type_bits(ret_ty)
new_bits: int = self.get_llvm_type_bits(expr_ty)
if new_bits > cur_bits:
ret_ty = expr_ty
return ret_ty
def build_param_types_str(self, pool: memhub.MemBuddy | t.CPtr,
args_node: ast.AST | t.CPtr) -> str:
"""构建参数类型编码字符串 "name:bits,name:bits,..." """
if args_node is None:
return None
ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
if ags.args is None:
return None
alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args
an: t.CSizeT = len(alist)
if an == 0:
return None
buf_size: t.CSizeT = an * 32 + 1
buf: str = pool.alloc(buf_size)
if buf is None:
return None
buf[0] = '\0'
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
for ai in range(an):
arg: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(ai))
if arg is not None and arg.arg is not None:
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if arg.annotation is not None:
# 直接调用 map_t_type 解析 Attribute 注解
if arg.annotation.kind() == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(arg.annotation)
if at.attr is not None:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.map_t_type(pool, at.attr)
if resolved is not None:
param_ty = resolved
bits: int = self.get_llvm_type_bits(param_ty)
if bits == 0:
bits = 32
tmp: str = pool.alloc(32)
if tmp is not None:
viperlib.snprintf(tmp, 32, "%s:%d", arg.arg, bits)
if ai > 0:
self._strappend(buf, buf_size, ",")
self._strappend(buf, buf_size, tmp)
return buf
def _strappend(self, dst: str, dst_size: t.CSizeT, src: str):
"""将 src 追加到 dst 末尾"""
if dst is None or src is None:
return
dlen: t.CSizeT = string.strlen(dst)
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0:
return
# 内联 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' # i 退出值存疑
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,4 +1,4 @@
# ============================================================
# Gen - 对象化生成器(P1 骨架 + P2 TypeGenMixin)
# Generator: 翻译状态容器 + Mixin 组合(BaseGen 状态 + TypeGen 类型映射,
# Generator: 翻译状态容器
# ============================================================
-197
View File
@@ -1,197 +0,0 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.CallGenLeafKit as CallGenLeafKit
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
# ============================================================
# ExprCallTable - HandlesExprCall 拆分(阶段 E):ExprCallTable
#
# 直接移动模式:委托函数迁至此模块,原 HandlesExprCall.vp 仅
# 保留 _g_gen/常量/类;调用方经本模块前缀访问。
# ============================================================
# ============================================================
# 初始化函数表
# ============================================================
def init_func_table(pool: memhub.MemBuddy | t.CPtr,
max_count: int) -> GenTypes.FuncEntry | t.CPtr:
"""分配并清零函数表数组(委托到 gen.init_func_table)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.init_func_table(pool, max_count)
return None
# ============================================================
# 查找函数
# ============================================================
def find_func_in_table(funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str) -> llvmlite.Function | t.CPtr:
"""在函数表中按名称查找,返回 Function 指针或 None(委托到 gen.find_func_in_table)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.find_func_in_table(funcs, func_count, name)
return None
# ============================================================
# 添加函数到函数表
# ============================================================
def add_func_to_table(funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str,
func: llvmlite.Function | t.CPtr,
max_count: int,
defaults: t.CVoid | t.CPtr = None,
default_count: int = 0,
param_count: int = 0,
pool: memhub.MemBuddy | t.CPtr = None) -> int:
"""添加函数到函数表,返回 0 成功(委托到 gen.add_func_to_table)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.add_func_to_table(funcs, func_count, name, func, max_count,
defaults, default_count, param_count, pool)
return 0
# ============================================================
# 全局函数注册表(跨模块函数查找)
#
# 所有模块翻译的函数都会注册到此表(通过 add_func_to_table 自动注册)。
# 主要用途: vtable 初始化时查找跨模块继承方法的 Function 对象,
# 以获取函数类型信息构造 bitcast 字符串。
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
def _init_global_func_table() -> int:
"""初始化全局函数表(用 stdlib.malloc 分配,生命周期=整个程序)(委托到 gen._init_global_func_table)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit._init_global_func_table()
return 0
# _build_func_type_str — 从 Function 对象生成函数类型文本(stdlib 存储)
#
# 格式: ret_ty(param1_ty, param2_ty, ...),与 vtable bitcast 需要一致。
# 用 stdlib.malloc 分配返回,跨文件永久存活(1b reset 不回收)。
# 返回 None 表示失败。
def _build_func_type_str(pool: memhub.MemBuddy | t.CPtr,
func: llvmlite.Function | t.CPtr) -> str:
"""生成函数类型文本(stdlib 分配)(委托到 gen._build_func_type_str)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit._build_func_type_str(pool, func)
return None
def _register_global_func(name: str, func: llvmlite.Function | t.CPtr,
pool: memhub.MemBuddy | t.CPtr = None) -> int:
"""注册函数到全局表(已存在则跳过),返回 0 成功(委托到 gen._register_global_func)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit._register_global_func(name, func, pool)
return 0
def update_global_func_typestr(pool: memhub.MemBuddy | t.CPtr,
name: str,
func: llvmlite.Function | t.CPtr) -> int:
"""更新全局函数表的 TypeStr(在函数参数全部添加完成后调用)(委托到 gen.update_global_func_typestr)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.update_global_func_typestr(pool, name, func)
return 0
def find_func_global(name: str) -> llvmlite.Function | t.CPtr:
"""在全局函数表中按名称查找,返回 Function 指针或 None(委托到 gen.find_func_global)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.find_func_global(name)
return None
def find_func_entry_global(name: str) -> GenTypes.FuncEntry | t.CPtr:
"""在全局函数表中按名称查找,返回完整 GenTypes.FuncEntry(含 TypeStr)或 None(委托到 gen.find_func_entry_global)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.find_func_entry_global(name)
return None
# ============================================================
# 全局默认参数注册表(跨模块默认参数填充)
#
# 独立于 _global_funcs,因为 _register_global_func 可能在
# add_func_to_table 之前被调用(如 stub 注入),此时 defaults 还不可用。
# ============================================================
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
def _init_global_defaults_table() -> int:
"""初始化全局默认参数表(委托到 gen._init_global_defaults_table)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit._init_global_defaults_table()
return 0
def _register_global_defaults(name: str,
defaults: t.CVoid | t.CPtr,
default_count: int,
param_count: int) -> int:
"""注册函数默认参数到全局表,返回 0 成功(委托到 gen._register_global_defaults)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit._register_global_defaults(name, defaults, default_count, param_count)
return 0
def find_func_defaults(name: str) -> GenTypes.FuncDefaultsEntry | t.CPtr:
"""按函数名查找默认参数信息,返回 GenTypes.FuncDefaultsEntry 或 None(委托到 gen.find_func_defaults)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit.find_func_defaults(name)
return None
# _build_const_from_snapshot — 从常量快照重建常量 IR 值
#
# 用于跨模块默认参数填充:gd_entry.Defaults(pool 内 AST)可能已被 reset 回收,
# 改用注册时提取的 stdlib 常量快照(ConstKinds/ConstInts/ConstStrs)重建常量 IR。
# 返回 llvmlite.Value*,失败返回 None。
def _build_const_from_snapshot(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
gd_entry: GenTypes.FuncDefaultsEntry | t.CPtr,
di: int,
trans: HT.Translator | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr) -> llvmlite.Value | t.CPtr:
"""从快照重建第 di 个默认值常量 IR(委托到 gen._build_const_from_snapshot)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(HandlesExprCall._g_gen, t.CPtr))
if g is not None:
return g.CallGenLeafKit._build_const_from_snapshot(pool, mod, gd_entry, di, trans, builder)
return None
def find_func_entry_in_table(funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str) -> GenTypes.FuncEntry | t.CPtr:
"""在函数表中按名称查找,返回完整 GenTypes.FuncEntry 或 None"""
if name is None or funcs is None:
return None
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
i: int = 0
while i < func_count:
entry_addr: t.CUInt64T = t.CUInt64T(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
i += 1
return None
-262
View File
@@ -1,262 +0,0 @@
import t, c
from stdint import *
import string
import viperlib
import memhub
import hashtable
import lib.core.Handles.HandlesTranslator as HT
# ============================================================
# TypeKind - 类型种类枚举
#
# 替代 TransPyC 中 CType.position frozenset 的元属性系统。
# 每个 TypeInfo 通过 Kind 字段标记其种类,避免 Python ClassVar 反射。
# ============================================================
class TypeKind(t.CEnum):
Basic: t.State # 基本类型(int/char/float/double/bool 等)
Pointer: t.State # 指针(T*)— 指针层数由 TypeInfo.PtrCount 表达
Struct: t.State # 结构体/类
Union: t.State # 联合体
Enum: t.State # 枚举
Typedef: t.State # 类型别名
Function: t.State # 函数指针
Array: t.State # 数组 T[N]
Void: t.State # void
# ============================================================
# TypeInfo - 类型信息结构
#
# 设计原则(相对 TransPyC CTypeInfo 的简化):
# - POD 数据结构,无虚函数,无 Python 元特性(ClassVar/Generic/frozenset)
# - 通过 mbuddy 分配,调用方管理生命周期(参考 llvmlite Module 模式)
# - 通过 Kind 枚举代替 position frozenset
# - PtrCount 表达指针层数(0=非指针, 1=T*, 2=T**)
# - IsSigned 三态:-1=不适用(void/float), 0=unsigned, 1=signed
#
# 字段布局(8 字段):
# Kind : i32 类型种类(TypeKind 枚举值)
# Name : i8* 类型名(如 'int', 'CInt', 'MyStruct'),可能为 None
# Size : i32 位宽(0=void/未知;int=32, char=8, double=64, long long=64)
# Align : i32 对齐字节数(0=默认)
# IsSigned : i32 -1=N/A, 0=unsigned, 1=signed
# PtrCount : i32 指针层数
# IsConst : i32 const 限定符(0/1)
# IsVolatile : i32 volatile 限定符(0/1)
#
# 注意:Size 使用位宽(bit width)而非字节数,与 LLVM IR 的 i{N} 和
# TransPyC CType.Size 约定一致(CInt.Size=32, CChar.Size=8)。
# ============================================================
@t.NoVTable
class TypeInfo:
Kind: int
Name: str
Size: int
Align: int
IsSigned: int
PtrCount: int
IsConst: int
IsVolatile: int
# ============================================================
# TypeRegistry - 类型注册表
#
# 维护 name(str) → TypeInfo* 映射,基于 HashTable 实现 O(1) 查找。
# 替代 TransPyC CTypeRegistry 的 _name_to_class dict。
# 内置基本类型(int/char/void/...)由 InitBasicTypes 注册。
# ============================================================
class TypeRegistry:
_ht: hashtable.HashTable | t.CPtr
__mbuddy__: memhub.MemBuddy | t.CPtr
def Register(self, ti: TypeInfo | t.CPtr) -> int:
"""注册一个 TypeInfo。ti.Name 字段必须已设置。
Returns:
1 表示成功,0 表示失败(ti 为 None 或 Name 为 None)
"""
if ti is None:
return 0
if ti.Name is None:
return 0
self._ht[ti.Name] = ti
return 1
def Lookup(self, name: str) -> TypeInfo | t.CPtr:
"""按名称查找 TypeInfo。
Returns:
找到返回 TypeInfo*,找不到返回 None
"""
if name is None:
return None
return self._ht[name]
def Has(self, name: str) -> int:
"""检查类型是否已注册。"""
if name is None:
return 0
return name in self._ht
# ============================================================
# NewTypeInfo - 工厂函数:分配并初始化一个 TypeInfo
#
# 默认值:Kind=Basic, IsSigned=-1, 其余=0/None
# ============================================================
def NewTypeInfo(pool: memhub.MemBuddy | t.CPtr) -> TypeInfo | t.CPtr:
ptr: TypeInfo | t.CPtr = pool.alloc(TypeInfo.__sizeof__())
if ptr is None:
return None
string.memset(ptr, 0, TypeInfo.__sizeof__())
ptr.Kind = TypeKind.Basic
ptr.IsSigned = -1
return ptr
# ============================================================
# NewTypeRegistry - 工厂函数:创建类型注册表
# ============================================================
def NewTypeRegistry(pool: memhub.MemBuddy | t.CPtr) -> TypeRegistry | t.CPtr:
ptr: TypeRegistry | t.CPtr = pool.alloc(TypeRegistry.__sizeof__())
if ptr is None:
return None
string.memset(ptr, 0, TypeRegistry.__sizeof__())
ptr.__mbuddy__ = pool
ptr._ht = hashtable.HashTable(pool)
return ptr
# ============================================================
# TypeToLLVM - 将 TypeInfo 转换为 LLVM IR 类型字符串
#
# 规则:
# - Kind == Void 或 (Size==0 且 IsSigned==-1 且 PtrCount==0) → "void"
# - void 有 PtrCount > 0 → "i8" + PtrCount 个 "*"
# - IsSigned == -1 且 Size > 0 → 浮点: half/float/double/fp128
# - IsSigned != -1 且 Size > 0 → 整数: i{Size}
# - 未知 → "i8*"
#
# Args:
# buf: 输出缓冲区(i8*)
# buf_size: 缓冲区容量
# ti: TypeInfo 指针
#
# Returns:
# 写入的字符数(不含 NUL),失败返回 -1
# ============================================================
def TypeToLLVM(buf: str, buf_size: t.CSizeT,
ti: TypeInfo | t.CPtr) -> int:
if buf is None:
return -1
if ti is None:
return -1
if buf_size == 0:
return -1
# 判定 void
is_void: int = 0
if ti.Kind == TypeKind.Void:
is_void = 1
elif ti.Size == 0 and ti.IsSigned == -1 and ti.PtrCount == 0:
is_void = 1
# 写入 base 类型字符串到 buf
base_len: int = 0
if is_void == 1:
if ti.PtrCount == 0:
# "void"
if buf_size < 5:
return -1
string.strcpy(buf, "void")
base_len = 4
else:
# void* → i8*(LLVM 中 void* 表示为 i8*)
if buf_size < 4:
return -1
string.strcpy(buf, "i8")
base_len = 2
elif ti.IsSigned == -1:
# 浮点
if ti.Size == 16:
if buf_size < 5:
return -1
string.strcpy(buf, "half")
base_len = 4
elif ti.Size == 32:
if buf_size < 6:
return -1
string.strcpy(buf, "float")
base_len = 5
elif ti.Size == 64:
if buf_size < 7:
return -1
string.strcpy(buf, "double")
base_len = 6
elif ti.Size == 128:
if buf_size < 6:
return -1
string.strcpy(buf, "fp128")
base_len = 5
else:
# 未知浮点尺寸 → double 兜底
if buf_size < 7:
return -1
string.strcpy(buf, "double")
base_len = 6
else:
# 整数: i{Size},用 snprintf 安全写入
if ti.Size <= 0:
# 无效尺寸 → i8 兜底
if buf_size < 4:
return -1
string.strcpy(buf, "i8")
base_len = 2
else:
viperlib.snprintf(buf, buf_size, "i%d", ti.Size)
base_len = string.strlen(buf)
# 追加 PtrCount 个 "*"
if base_len + ti.PtrCount + 1 > buf_size:
return -1
pos: int = base_len
for i in range(ti.PtrCount):
buf[pos] = '*'
pos += 1
buf[pos] = 0
return pos
# ============================================================
# Mixin - 所有 Handle 的非多态基类(对应 TransPyC 的 BaseHandle)
#
# @t.NoVTable 继承:字段展平嵌入子类,无 vtable 开销(对应 C++ 非多态继承)。
# 子类继承 Trans 字段 + 共享工具方法,编译器自动生成子类方法包装
# (self bitcast 为父类指针后调用),子类可直接调用继承的方法。
#
# 用法:
# @t.NoVTable
# class AssignHandle(Mixin):
# _CurrentClass: str
# def __init__(self, trans):
# self.InitMixin(trans)
# self._CurrentClass = None
# def Handle(self, node) -> int:
# rhs = self.Trans.ExprH.HandleValue(node.value)
# ...
# ============================================================
@t.NoVTable
class Mixin:
"""所有 Handle 的非多态基类:持有 Translator 回指针 + 共享委托方法"""
Trans: HT.Translator | t.CPtr
def InitMixin(self, trans: HT.Translator | t.CPtr) -> int:
"""初始化 Mixin 字段(子类 __init__ 中调用)"""
self.Trans = trans
return 0
# 全局 mbuddy 指针
_mbuddy: t.CVoid | t.CPtr
-953
View File
@@ -1,953 +0,0 @@
import lib.core.Gen.ExprGenAttr as ExprGenAttr
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Gen.ExprGenCdef as ExprGenCdef
import lib.core.Gen.ExprGenCls as ExprGenCls
import lib.core.Gen.ExprGenConst as ExprGenConst
import lib.core.Gen.ExprGenFstr as ExprGenFstr
import lib.core.Gen.ExprGenMixin as ExprGenMixin
import lib.core.Gen.ExprGenName as ExprGenName
import lib.core.Gen.ExprGenOps as ExprGenOps
import lib.core.Gen.FuncGenMixin as FuncGenMixin
import t, c
from stdint import *
import ast
import memhub
import string
import llvmlite
import stdlib
import stdio
import viperlib
import w32.fileio as fileio
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.Generator as Generator
# ============================================================
# HandlesExpr - 表达式处理(P3 对象化后的委托层)
#
# 全部模块级函数已迁移为 lib.core.Gen.ExprGenMixin 的方法
# (全局状态迁入 gen 字段),本模块保留同名模块级函数作为
# "一行委托":延迟 import Gen 取全局单例(BuildPipeline/main
# 创建后 set_global_gen 注册),再调用 gen 的对应方法。签名与
# 旧函数完全一致,调用方零改动。
#
# ExprHandle 类及其 NewExprHandle 工厂保留(translator handler 模式)。
# ============================================================
# ============================================================
# 全局 gen 单例(模块级镜像,同 HandlesType/HandlesStruct 模式)
# ============================================================
_g_gen: Generator.Generator | t.CPtr = None
# ============================================================
# 委托函数:延迟 import Gen 取全局单例 → 调 gen 方法
# (gen 未注册时正常流程不会发生;有返回值函数返回默认值)
# ============================================================
# ============================================================
# 从 AST 节点提取函数名
# ============================================================
def get_func_name(func_node: ast.AST | t.CPtr) -> str:
"""从函数节点提取函数名(委托到 gen.get_func_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.get_func_name(func_node)
return None
# ============================================================
# 转义 LLVM IR 字符串字面量
# ============================================================
def escape_llvm_string(pool: memhub.MemBuddy | t.CPtr,
src: str) -> str:
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00"(委托到 gen.escape_llvm_string)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.escape_llvm_string(pool, src)
return None
# ============================================================
# 获取 LLVM 整数类型的位宽
# ============================================================
def get_llvm_type_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托到 gen.get_llvm_type_bits)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.get_llvm_type_bits(ty)
return 0
# ============================================================
# 获取 LLVM 浮点类型的位宽
# ============================================================
def get_llvm_float_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0(委托到 gen.get_llvm_float_bits)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.get_llvm_float_bits(ty)
return 0
# ============================================================
# is_ptr_type - 检查类型是否为 Ptr
#
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
# ============================================================
def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否(委托到 gen.is_ptr_type)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.is_ptr_type(ty)
return 0
# ============================================================
# get_ptr_level - 计算指针层级
#
# 返回值:
# 0 = 非指针(数值/结构体等)
# 1 = 一级指针(如 i8*, Struct*)
# 2 = 二级指针(如 i8**, Struct**)
# 3+ = 更高级指针
# ============================================================
def get_ptr_level(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针...(委托到 gen.get_ptr_level)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.get_ptr_level(ty)
return 0
# ============================================================
# is_array_type - 检查类型是否为 Array
#
# 返回 1=是, 0=否。类型检查工具函数。
# ============================================================
def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否(委托到 gen.is_array_type)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.is_array_type(ty)
return 0
# ============================================================
# _zext_index_to_i32 - 数组下标索引零扩展
#
# LLVM GEP 对窄整数索引默认符号扩展:i8 0xE2(226) 会被当成 -30,
# 导致数组下标越界读(字节 >= 128 的乱码根因)。
# 数组下标语义为非负,<32 位整数索引一律 zext 到 i32。
# ============================================================
def _zext_index_to_i32(builder: llvmlite.IRBuilder | t.CPtr,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""将 <32 位整数下标零扩展到 i32(数组下标非负语义)(委托到 gen._zext_index_to_i32)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase._zext_index_to_i32(builder, idx_val)
return None
# ============================================================
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
#
# 处理两种情况:
# - ty 直接是 Struct → 检查是否注册
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
# ============================================================
def _get_custom_struct_cls_nm(pool: memhub.MemBuddy | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> str:
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None(委托到 gen._get_custom_struct_cls_nm)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase._get_custom_struct_cls_nm(pool, ty)
return None
# ============================================================
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
# ============================================================
def _is_list_subscript_base(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> int:
"""判断下标基座是否为 list[T] 变量(1=是)(委托到 gen._is_list_subscript_base)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase._is_list_subscript_base(pool, trans, sub_node)
return 0
# ============================================================
# _sub_base_class_name - 解析下标基座变量的结构体类名
# ============================================================
def _sub_base_class_name(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> str:
"""解析下标基座变量的结构体类名(无则 None)(委托到 gen._sub_base_class_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase._sub_base_class_name(pool, trans, sub_node)
return None
# ============================================================
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
# ============================================================
def _sub_base_uses_getitem(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> int:
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)(委托到 gen._sub_base_uses_getitem)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase._sub_base_uses_getitem(pool, mod, trans, sub_node)
return 0
# ============================================================
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
# ============================================================
def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针(委托到 gen._deref_if_ptr_ptr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase._deref_if_ptr_ptr(builder, obj_ptr)
return None
# ============================================================
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
# ============================================================
def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
val: llvmlite.Value | t.CPtr,
target_ty: llvmlite.LLVMType | t.CPtr,
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""将 val 强制转换为 target_ty(委托到 gen.coerce_to_type)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.coerce_to_type(builder, val, target_ty, node)
return None
# ============================================================
# translate_ptr_arith - 指针算术: ptr + int / ptr - int
#
# 用 getelementptr(按 pointee 元素大小缩放索引),对齐 TPC
# ExprGen.emit_binary_op / HandlesAugAssign.py。
# ============================================================
def translate_ptr_arith(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
ptr_val: llvmlite.Value | t.CPtr,
int_val: llvmlite.Value | t.CPtr,
op: int) -> llvmlite.Value | t.CPtr:
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)(委托到 gen.translate_ptr_arith)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenBase.translate_ptr_arith(pool, builder, ptr_val, int_val, op)
return None
# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
str_val: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局字符串常量并返回 i8* bitcast(委托到 gen.create_global_string)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenConst.create_global_string(builder, pool, mod, str_val, trans)
return None
# ============================================================
# 模块引用 AST → 模块路径字符串(递归拼接 Attribute 链)
# ============================================================
def attr_module_path_str(pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr) -> str:
"""把模块引用 AST 转成模块路径字符串(Name('sys')→"sys"、
Attribute(Attribute(Name('os'),'_win32'))→"os._win32";非模块引用形态返回 None)
跨 mixin 委托:CallGenMixin 等不能 self. 直接调用 ExprGenMixin 的方法
(@t.NoVTable 静态绑定会 mangle 成当前类名 → undefined reference)。
"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr._attr_module_path_str(pool, node)
return None
# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译常量(int/str/bool)(委托到 gen.translate_constant)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenConst.translate_constant(builder, pool, mod, node, trans)
return None
# ============================================================
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
# ============================================================
def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
value: llvmlite.Value | t.CPtr) -> str:
"""从 LLVM Value 的类型推断 list 元素类型名(委托到 gen._infer_elem_type_from_value)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenConst._infer_elem_type_from_value(pool, value)
return None
# ============================================================
# _find_pool_var — 查找上下文中的 pool 变量
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找上下文中的 pool 变量,返回 alloca 或 None(委托到 gen._find_pool_var)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenConst._find_pool_var(trans)
return None
# ============================================================
# translate_list_literal — 翻译 list 字面量 [a, b, c]
#
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
# ============================================================
def translate_list_literal(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
elem_type_name: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列(委托到 gen.translate_list_literal)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenConst.translate_list_literal(builder, pool, mod, node, elem_type_name, trans)
return None
# ============================================================
# 翻译值表达式(RHS 分派)— 模块级版本
# ============================================================
def translate_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value(委托到 gen.translate_value)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGen.translate_value(builder, pool, mod, node, funcs_ptr, func_count, trans)
return None
# ============================================================
# 翻译 lambda 表达式 — 模块级委托
#
# translate_lambda 定义在 FuncGenMixin(跨 Mixin 方法),ExprGenMixin
# 不能 self. 直接调用(NoVTable 下方法 mangle 成定义类符号,跨 Mixin
# self 调用会解析失败),与 translate_value 同模式走模块级委托。
# ============================================================
def translate_lambda(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译 lambda 表达式,返回闭包指针(i8*)(委托到 gen.translate_lambda)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.FuncGen.translate_lambda(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译三元表达式 IfExp(test, body, orelse)
# ============================================================
def translate_ifexp(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译三元表达式 (body if test else orelse),返回 phi 结果 Value(委托到 gen.translate_ifexp)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译比较表达式 Compare(left, ops, comparators)
# ============================================================
def should_zext_widen(pool: memhub.MemBuddy | t.CPtr,
narrow_val: llvmlite.Value | t.CPtr,
wide_val: llvmlite.Value | t.CPtr,
narrow_node: ast.AST | t.CPtr = None) -> int:
"""判断整数拓宽方向:1=zext(无符号语义),0=sext(委托到 gen.should_zext_widen)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps.should_zext_widen(pool, narrow_val, wide_val, narrow_node)
return 0
def _node_type_unsigned(pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> int:
"""轻量判断 AST 节点是否解析为无符号整数类型(1=是,0=否/未知)(委托到 gen._node_type_unsigned)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps._node_type_unsigned(pool, node, trans)
return 0
def _type_name_unsigned(type_name: str) -> int:
"""类型名是否为无符号整数类型(精确匹配,避免子串误伤如 nUIntCount)(委托到 gen._type_name_unsigned)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps._type_name_unsigned(type_name)
return 0
def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译比较表达式,返回 i1 结果(委托到 gen.translate_compare)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译一元运算表达式 UnaryOp(op, operand)
# ============================================================
def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译一元运算(-x, +x, not x, ~x)(委托到 gen.translate_unaryop)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1
# ============================================================
def translate_boolop(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译布尔运算(and/or)的短路求值,返回 i1(委托到 gen.translate_boolop)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
return None
# ============================================================
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .vp/.py 文件路径
# ============================================================
def _build_source_path_from_sha1(sha1: str) -> str:
"""通过 SHA1 从全局存储器构建源 .py 文件路径(委托到 gen._build_source_path_from_sha1)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCdef._build_source_path_from_sha1(sha1)
return None
# ============================================================
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
# ============================================================
def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
name: str,
from_imports: str,
imported_modules: str = None,
depth: int = 0,
node: ast.AST | t.CPtr = None) -> t.CInt64T:
"""跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)(委托到 gen._lookup_cross_module_cdefine)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCdef._lookup_cross_module_cdefine(pool, name, from_imports, imported_modules, depth, node)
return 0
# ============================================================
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
# ============================================================
def _is_cdefine_name_pattern(name: str) -> int:
"""检查名字是否符合 CDefine 命名约定(全大写+下划线+数字)(委托到 gen._is_cdefine_name_pattern)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCdef._is_cdefine_name_pattern(name)
return 0
# ============================================================
# 翻译变量引用(Name 节点)→ load
# ============================================================
def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译变量引用(Name 节点)→ load(委托到 gen.translate_name_value)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName.translate_name_value(builder, pool, node, trans)
return None
# ============================================================
# ExprHandle - 表达式处理器(Mixin 继承模式)
#
# HandleValue 提供 trans 接口,委托到模块级 translate_value
# ============================================================
@t.NoVTable
class ExprHandle(HandlesBase.Mixin):
"""表达式处理器:继承 Mixin 获得 Trans 回指针"""
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
# ============================================================
# HandleValue - 翻译值表达式为 LLVM Value
# ============================================================
def HandleValue(self, node: ast.AST | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译值表达式,从 self.Trans 获取共享状态"""
return translate_value(
self.Trans._cur_builder, self.Trans.Pool, self.Trans.Module,
node,
self.Trans._funcs, self.Trans._func_count, self.Trans)
# ============================================================
# NewExprHandle - 工厂函数
# ============================================================
def NewExprHandle(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> ExprHandle | t.CPtr:
h: ExprHandle | t.CPtr = pool.alloc(ExprHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, ExprHandle.__sizeof__())
h.Trans = trans
return h
# ============================================================
# list_getitem_inline — 内联生成 list[T] __getitem__ 逻辑
#
# list 结构布局 (6 字段 × 8 字节):
# __data__(0) __count__(8) __capacity__(16) __pool__(24) __elem_size__(32) __iter_index__(40)
# 返回元素地址 (i8*),调用方根据元素类型 load 正确的值
# ============================================================
def list_getitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""内联生成 list __getitem__ 逻辑,返回元素地址 (i8*)(委托到 gen.list_getitem_inline)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName.list_getitem_inline(builder, pool, lm_obj, idx_val, node)
return None
# ============================================================
# _list_elem_type_from_name — 从 list 类型名提取元素 LLVM 类型
# ============================================================
def _list_elem_type_from_name(pool: memhub.MemBuddy | t.CPtr,
struct_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从 list 类型名提取元素 LLVM 类型(委托到 gen._list_elem_type_from_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName._list_elem_type_from_name(pool, struct_name)
return None
# ============================================================
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
# ============================================================
def list_setitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None) -> int:
"""内联生成 list __setitem__ 逻辑,返回 0(委托到 gen.list_setitem_inline)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName.list_setitem_inline(builder, pool, lm_obj, idx_val, rhs_val, node)
return 0
# ============================================================
# is_list_subscript — 检查 Subscript 节点是否是 list[T] 类型
#
# 返回 list 对象指针 (Ptr(list[T])) 或 None
# ============================================================
def is_list_subscript(node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""检查 Subscript 节点是否是 list[T] 类型,返回 list 对象指针或 None(委托到 gen.is_list_subscript)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName.is_list_subscript(node, trans)
return None
# ============================================================
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
# ============================================================
def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译下标表达式,返回加载的元素值(委托到 gen.translate_subscript)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
return None
# ============================================================
# find_global_in_module - 在模块全局变量链表中按名称查找
#
# 查找策略:
# 1. 精确匹配 (如 "_mbuddy" 匹配 @_mbuddy)
# 2. 后缀匹配 (如 "_mbuddy" 匹配 @"sha1._mbuddy")
# ============================================================
def find_global_in_module(mod: llvmlite.LLVMModule | t.CPtr,
name: str) -> llvmlite.GlobalVariable | t.CPtr:
"""在模块全局变量链表中按名称查找全局变量(委托到 gen.find_global_in_module)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr.find_global_in_module(mod, name)
return None
# ============================================================
# make_global_ref - 创建全局变量引用 (@name,类型为 ty*)
# ============================================================
def make_global_ref(pool: memhub.MemBuddy | t.CPtr,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局变量引用 (类型为 ty*,名称为 @name)(委托到 gen.make_global_ref)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr.make_global_ref(pool, name, ty)
return None
# ============================================================
# _is_t_type_object_name - 检查 name 是否是 t.py 中的类型对象名
# ============================================================
def _is_t_type_object_name(name: str) -> int:
"""检查 name 是否是 t.py 中的类型对象名(CPtr/CInt/CDouble 等)(委托到 gen._is_t_type_object_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr._is_t_type_object_name(name)
return 0
# ============================================================
# _resolve_module_attribute_global - 解析模块属性访问的全局变量引用
# ============================================================
def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
ty_hint: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""解析模块属性访问,返回全局变量引用 (用于跨模块 global 访问)(委托到 gen._resolve_module_attribute_global)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr._resolve_module_attribute_global(pool, mod, trans, at, ty_hint)
return None
# ============================================================
# _get_cderef_attr_base - c.Deref(ptr).attr 属性访问的 base 指针
# ============================================================
def _get_cderef_attr_base(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
at_value: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""检测 at.value 是否为 c.Deref(ptr) 调用,返回 ptr 指针(不做 load)(委托到 gen._get_cderef_attr_base)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr._get_cderef_attr_base(builder, pool, mod, at_value, trans)
return None
# ============================================================
# 翻译属性访问 Attribute(value, attr, ctx) — obj.field
# ============================================================
def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译属性访问,返回加载的字段值(委托到 gen.translate_attribute)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
return None
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
# ============================================================
def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""获取下标表达式的元素指针(用于赋值 lhs)(委托到 gen.get_subscript_ptr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenName.get_subscript_ptr(builder, pool, mod, node, trans)
return None
# ============================================================
# 获取属性访问的字段指针(不加载值,用于赋值)
# ============================================================
def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""获取属性访问的字段指针(用于赋值 lhs)(委托到 gen.get_attribute_ptr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr.get_attribute_ptr(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译 f-string(JoinedStr → snprintf 生成 i8* 栈缓冲)— 模块级委托
# ============================================================
def translate_joined_str(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译 f-string(委托到 gen.translate_joined_str,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译裸 FormattedValue(f"{expr:spec}" 无字面量)— 模块级委托
# ============================================================
def translate_formatted_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译裸 FormattedValue(委托到 gen.translate_formatted_value,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
return None
# ============================================================
# CDefine 整数常量 → LLVM 常量(64 位升级规则)— 模块级委托
# ============================================================
def _const_int_for_cdefine(pool: memhub.MemBuddy | t.CPtr,
value: t.CInt64T) -> llvmlite.Value | t.CPtr:
"""CDefine 整数常量 → LLVM 常量(委托到 gen._const_int_for_cdefine,定义于 ExprGenCdef)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCdef._const_int_for_cdefine(pool, value)
return None
# ============================================================
# 解析嵌套 Attribute 链的 docstring 归属 — 模块级委托
# ============================================================
def _resolve_doc_symbol_attr(trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
out_mod: str, out_sym: str) -> int:
"""解析嵌套 Attribute 链的 docstring 归属(委托到 gen._resolve_doc_symbol_attr,定义于 ExprGenCls)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCls._resolve_doc_symbol_attr(trans, at, out_mod, out_sym)
return 0
# ============================================================
# 解析 Name 符号的 docstring 归属 — 模块级委托
# ============================================================
def _resolve_doc_symbol_name(trans: HT.Translator | t.CPtr, name_id: str,
out_mod: str, out_sym: str) -> int:
"""解析 Name 符号的 docstring 归属(委托到 gen._resolve_doc_symbol_name,定义于 ExprGenCls)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCls._resolve_doc_symbol_name(trans, name_id, out_mod, out_sym)
return 0
# ============================================================
# 查找 docstring 内容(key="{module_sha1}.{symbol}")— 模块级委托
# ============================================================
def lookup_docstring(module_sha1: str, symbol: str) -> str:
"""查找 docstring 内容(委托到 gen.lookup_docstring,定义于 ExprGenCls)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenCls.lookup_docstring(module_sha1, symbol)
return None
# ============================================================
# 解析属性链(a.b.c)中 c 字段的注解类名 — 模块级委托
# ============================================================
def _resolve_attr_field_class(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
attr_node: ast.AST | t.CPtr) -> str:
"""解析属性链最终字段的注解类名(委托到 gen._resolve_attr_field_class,定义于 ExprGenAttr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenAttr._resolve_attr_field_class(pool, trans, attr_node)
return None
# ============================================================
# 翻译二元运算(自动类型提升)— 模块级委托
# ============================================================
def translate_binop(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译二元运算(委托到 gen.translate_binop,定义于 ExprGenOps)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
return None
# ============================================================
# binop op → dunder 方法名(如 Add → "__add__")— 模块级委托
# ============================================================
def _binop_to_dunder(op: int) -> str:
"""binop op → dunder 方法名(委托到 gen._binop_to_dunder,定义于 ExprGenOps)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps._binop_to_dunder(op)
return None
# ============================================================
# unaryop op → dunder 方法名(如 USub → "__neg__")— 模块级委托
# ============================================================
def _unaryop_to_dunder(op: int) -> str:
"""unaryop op → dunder 方法名(委托到 gen._unaryop_to_dunder,定义于 ExprGenOps)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenOps._unaryop_to_dunder(op)
return None
# ============================================================
# f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf — 模块级委托
# ============================================================
def _fstr_finalize(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
fmt_buf: str,
arg_head: llvmlite.Value | t.CPtr,
arg_tail: llvmlite.Value | t.CPtr,
arg_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""f-string 尾部(委托到 gen._fstr_finalize,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenFstr._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
return None
# ============================================================
# 追加字面量到格式串(% 转义)— 模块级委托
# ============================================================
def _fstr_append_literal(fmt_buf: str, lit: str) -> None:
"""追加字面量到格式串(委托到 gen._fstr_append_literal,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
g.ExprGenFstr._fstr_append_literal(fmt_buf, lit)
# ============================================================
# Python format_spec → C printf 格式串 — 模块级委托
# ============================================================
def _fstr_parse_format_spec(pool: memhub.MemBuddy | t.CPtr,
spec_node: ast.AST | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr) -> str:
"""Python format_spec → C printf 格式串(委托到 gen._fstr_parse_format_spec,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenFstr._fstr_parse_format_spec(pool, spec_node, val, arg_node)
return None
# ============================================================
# 无 format_spec 时按值类型推断 C printf 格式 — 模块级委托
# ============================================================
def _fstr_infer_c_fmt(pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
"""按值类型推断 C printf 格式(委托到 gen._fstr_infer_c_fmt,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenFstr._fstr_infer_c_fmt(pool, val, arg_node, trans)
return None
# ============================================================
# 按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)— 模块级委托
# ============================================================
def _fstr_cast_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
c_fmt: str,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""按 C 格式转换值类型(委托到 gen._fstr_cast_value,定义于 ExprGenFstr)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.ExprGenFstr._fstr_cast_value(builder, pool, val, c_fmt, arg_node, trans)
return None
-137
View File
@@ -1,137 +0,0 @@
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as Generator
# ============================================================
# 全局 gen 单例(模块级镜像,同 HandlesType/HandlesStruct 模式)
# ============================================================
_g_gen: Generator.Generator | t.CPtr = None
# ============================================================
# 本地常量(阶段 E 拆分时随函数块误删,补回;子模块经 HandlesExprCall.常量 引用)
# ============================================================
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
ASM_CONST_BUF_SIZE: t.CDefine = 512
ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
MAX_CLOSURE_NAMES: t.CDefine = 128
MAX_FUNC_PTR_SIGS: t.CDefine = 64
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
# ============================================================
# AsmOperand - 内联汇编操作数条目
# ============================================================
@t.NoVTable
class AsmOperand:
"""内联汇编操作数条目"""
Value: llvmlite.Value | t.CPtr # 输入值或输出 alloca
Constraint: str # 约束字符串 (如 "r", "=r")
IsOutput: int # 1=输出, 0=输入
# ============================================================
# FuncEntry 结构体(用于函数表)
# ============================================================
@t.NoVTable
class FuncEntry:
"""函数表条目"""
Name: str
Func: llvmlite.Function | t.CPtr
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值
DefaultCount: int # 默认值数量
ParamCount: int # 形参数量(不含 self)
# TypeStr: 函数类型文本(stdlib 深拷贝,跨文件安全)
# 1b 每文件 reset_to(mark1) 回收 pool 内 Function 对象后,
# vtable 生成 find_func_global 读类型改用此文本,避免悬空。
TypeStr: str
@t.NoVTable
class FuncDefaultsEntry:
"""默认参数表条目"""
Name: str
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
DefaultCount: int
ParamCount: int # 形参数量(不含 self)
# 常量快照(stdlib 分配,跨文件永久存活):
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
ConstKinds: t.CInt | t.CPtr # const_kind 数组(DefaultCount 个 i32)
ConstInts: t.CInt64T | t.CPtr # int_val 数组(DefaultCount 个 i64)
ConstStrs: t.CSizeT | t.CPtr # str_val 指针数组(DefaultCount 个 i8*)
# ============================================================
# ExprCallHandle - 函数调用处理器(Mixin 继承模式)
#
# HandleCall / HandlePrintfCall 提供 trans 接口
# ============================================================
@t.NoVTable
class ExprCallHandle(HandlesBase.Mixin):
"""函数调用处理器:继承 Mixin 获得 Trans 回指针"""
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
# ============================================================
# HandleCall - 翻译函数调用,返回 call 结果 Value
# ============================================================
def HandleCall(self, node: ast.AST | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译函数调用,从 self.Trans 获取共享状态"""
return GenApi.translate_call(
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
node,
self.Trans._funcs, self.Trans._func_count, self.Trans)
# ============================================================
# HandlePrintfCall - 翻译 printf 调用
# ============================================================
def HandlePrintfCall(self, cl: ast.Call | t.CPtr):
"""翻译 printf 调用,从 self.Trans 获取共享状态"""
GenApi.translate_printf_call(
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
cl, self.Trans)
# ============================================================
# HandlePrintCall - 翻译 print 调用
# ============================================================
def HandlePrintCall(self, cl: ast.Call | t.CPtr):
"""翻译 print 调用(类型感知格式化 + 默认换行)"""
GenApi.translate_print_call(
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
cl, self.Trans)
-758
View File
@@ -1,758 +0,0 @@
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.core.Handles.ExprCallTable as ExprCallTable
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Gen.Generator as Generator
# ============================================================
# HandlesTranslator - 翻译器状态管理 + 主入口
#
# 从 translator.py 拆分出来,负责:
# 1. Translator 类 - 状态管理(Module, 变量表, 函数表, 导入)
# 2. translate() - 主翻译入口
# 3. dump_ir() - IR 输出
#
# 语句/表达式翻译全部委托给 Handles 模块:
# HandlesMain - wrapper main 创建 + children 遍历
# HandlesBody - 语句分派
# HandlesExpr - 表达式翻译
# HandlesFunctions - 函数定义处理
# 注意:str = bytes = t.CChar | t.CPtr = i8*
#
# P6-E 对象化重构:模块级 global/nonlocal 名称管理函数已迁移为
# lib.core.Gen.TranslatorGenMixin 的方法,本模块保留同名模块级
# 函数作为"一行委托"(同 P2-P5 模式)。Translator 类保留在本模块。
# ============================================================
# 常量
MAX_VARS: t.CDefine = 1024
MAX_FUNCS: t.CDefine = 1024
MAX_GLOBAL_NAMES: t.CDefine = 32
# 协程表容量(TPV-M1):async 函数数 / 协程变量·局部名数
MAX_CORO_FUNCS: t.CDefine = 256
MAX_CORO_VARS: t.CDefine = 256
MAX_CORO_TRY: t.CDefine = 16
# 翻译主环最大递归深度(translate_stmt/translate_value 嵌套)。AST 自引用环
# 或翻译 bug 可致翻译主环(语句/表达式/调用)无限递归 → ASan stack-overflow。
# match 守卫(MatchTranslateDepth)只保护 match 递归,本常量保护语句/表达式
# 翻译环(fail-fast,对齐 TypeGenResolve 深度守卫模式)。
TRANSLATE_MAX_DEPTH: t.CDefine = 256
# ============================================================
# 全局 gen 单例(模块级镜像,同 HandlesType/HandlesStruct 模式)
# ============================================================
_g_gen: Generator.Generator | t.CPtr = None
@t.NoVTable
class Translator:
"""翻译器:管理编译状态,委托 Handles 模块执行翻译"""
# LLVM 上下文(共享状态)
Module: llvmlite.LLVMModule | t.CPtr
Pool: memhub.MemBuddy | t.CPtr
# 函数表
_funcs: GenTypes.FuncEntry | t.CPtr
_func_count: t.CInt
# 协程(TPV-M1 async def / await / coro.coro_run)
# _async_funcs: 已注册 async 函数裸名表(char* 数组,8 字节/项)
# _coro_var_async: 变量名→async 函数名 成对表(char* 数组,16 字节/项)
# _coro_locals: 当前 async 函数局部变量名表(char* 数组,8 字节/项)
# _coro_frames: async 名→帧类型 成对表(16 字节/项 = char* + LLVMType*)
_async_funcs: str
_async_func_count: t.CInt
_coro_var_async: str
_coro_var_async_count: t.CInt
_coro_locals: str
_coro_local_count: t.CInt
_coro_frames: str
_coro_frame_count: t.CInt
# resume_<name> 翻译上下文(AsyncGenMixin 段翻译共享)
_coro_state_ptr: llvmlite.Value | t.CPtr
_coro_done_ptr: llvmlite.Value | t.CPtr
_coro_ret_ptr: llvmlite.Value | t.CPtr
_coro_exit_bb: llvmlite.BasicBlock | t.CPtr
_coro_await_slot: llvmlite.Value | t.CPtr
_coro_frame: llvmlite.Value | t.CPtr
_coro_next_state: t.CInt
_coro_nseg: t.CInt
# M2:async 参数表(char* 数组,8 字节/项)+ 预扫描计数
_coro_params: str
_coro_param_count: t.CInt
_coro_hoist_count: t.CInt # __await_N 槽计数(预扫描与翻译共用,序一致)
_coro_await_count: t.CInt # 总 await 数(= 恢复块数 = switch case 数)
_coro_local_base: t.CInt # 局部槽起始 index(= 3 + nparams)
_coro_extra_base: t.CInt # 额外槽(__await_N)起始 index
_coro_break_bb: llvmlite.BasicBlock | t.CPtr # While break 目标(M2 单层)
_coro_continue_bb: llvmlite.BasicBlock | t.CPtr # While continue 目标
_coro_state_bbs: t.CSizeT | t.CPtr # 预建 state 块数组(await 恢复块定位)
# M3-M7 协程扩展
_coro_exc_ptr: llvmlite.Value | t.CPtr # 帧 exc 槽(i32,M4)
_coro_msg_ptr: llvmlite.Value | t.CPtr # 帧 msg 槽(i8*,M7)
_coro_wake_ptr: llvmlite.Value | t.CPtr # 帧 wake 槽(i64,M3 coro_sleep)
_coro_try_stack: t.CSizeT | t.CPtr # try 栈:except/finally 块指针数组(8B/项)
_coro_try_stack_count: t.CInt
_coro_for_counter: t.CInt # range-For 编号(M3,翻译期递增)
_coro_scan_for_count: t.CInt # 预扫描期 For 计数
_coro_uses_sleep: t.CInt # 函数体是否用 coro_sleep(预扫描置位)
_coro_extra_types: str # 额外槽类型表(i8 数组:1=i32 2=i64)
_coro_extra_count: t.CInt # 额外槽总数
_coro_await_slots: t.CInt | t.CPtr # __await_N 的帧相对槽 index(预扫描填)
_coro_for_slots: t.CInt | t.CPtr # range-For N 的 __f_idx 帧相对槽 index(预扫描填)
# per-async wake 槽 index 表(coro_run/gather 轮询反查用)
_coro_wake_names: str # char* 数组(8B/项)
_coro_wake_idxs: t.CInt | t.CPtr # i32 数组(4B/项,-1=无 wake 槽)
_coro_wake_count: t.CInt
# 导入模块跟踪
_imported_modules: str
_from_imports: str
# 当前翻译上下文
_cur_func: llvmlite.Function | t.CPtr
_cur_builder: llvmlite.IRBuilder | t.CPtr
# 当前赋值语句的注解节点(AnnAssign 翻译 rhs 前设置,Assign 翻译前清空)。
# 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → bitcast i32* load,
# 对齐 TPC HandlesSpecialCall._infer_deref_target_type(DictTest 500→-12 根因:
# i8* Deref 固定读 1 字节 + sext 截断)。
CurAssignAnnot: ast.AST | t.CPtr
# 当前赋值语句的 RHS 节点(AnnAssign 翻译 rhs 前设置,rhs 翻译后清除)。
# 供 c.Deref 判定"是否为赋值直接 RHS":仅 v: int = c.Deref(p) 时用
# CurAssignAnnot 推断读取宽度;嵌套在 while 条件/比较/binop/类型转换
# 调用中(如 while c.Deref(p) != 0、total += t.CInt(c.Deref(p)))不推断,
# 否则 i8* 会被残留 CurAssignAnnot 误判为 i32* 读 4 字节(对齐 TPC
# _infer_deref_target_type 的直接 RHS 检查)。
CurAssignNode: ast.AST | t.CPtr
# 循环控制流目标(break/continue)
_break_bb: llvmlite.BasicBlock | t.CPtr
_continue_bb: llvmlite.BasicBlock | t.CPtr
# 独立标签计数器(不与 builder.Counter 共享,避免 SSA 编号非单调)
_label_counter: t.CInt
# 追加90:控制流 merge 块可达性(1=有 br 进入可达/0=全分支 return 结构性不可达)
_ctrl_merge_reachable: t.CInt
# match 翻译递归深度(match → case body → translate_value → 嵌套 match)。
# 守卫:AST 自引用环(子节点回指祖先)或翻译 bug 导致的无限递归在此
# fail-fast(对齐 TypeGenResolve 深度守卫,防 ASan stack-overflow)。
MatchTranslateDepth: t.CInt
# 翻译主环递归深度(translate_stmt/translate_value 入口计数)。match 守卫
# 只覆盖 match 递归;语句/表达式/调用环(如 translate_value ↔ translate_call)
# 由本字段守卫,超 TRANSLATE_MAX_DEPTH 即 fail-fast(防 ASan stack-overflow)。
TranslateDepth: t.CInt
# global 声明的变量名集合(当前函数内)
_global_names: str
_global_name_count: t.CInt
# nonlocal 声明的变量名集合(当前函数内)
_nonlocal_names: str
_nonlocal_name_count: t.CInt
# nonlocal 变量的类型信息(LLVMType 指针数组,与 _nonlocal_names 一一对应)
# 用于 env 指针数组方案:子函数访问 nonlocal 变量时需要知道原始类型
_nonlocal_types: str
# 闭包相关:当前函数名(用于嵌套函数提升命名)
_cur_func_name: str
# *args 变长参数支持:当前函数的 va_list 指针(i8* alloca)
# 仅当函数声明了 *args 时设置,供 arg(type) 糖函数使用
_va_list_ptr: llvmlite.Value | t.CPtr
# *args 参数名(如 "args"),用于符号表注册
_vararg_name: str
# OOP iter 协议:当前 __next__ 方法的隐藏 stop-flag 参数(i1*)。
# _translate_method 注入 __stop_iter_flag__ 参数时设置;translate_raise
# 检测 raise StopIteration 时对该指针 store 1(对齐 TPC HandlesRaise.py)。
# 非 __next__ 函数中为 None。
_stop_iter_flag_ptr: llvmlite.Value | t.CPtr
# try-except 机制(对齐 TPC HandlesTry.py):当前最内层 try 的
# 分发块 + 异常编码/消息 alloca。translate_try 设置,translate_raise
# 与调用侧 EH 检测写入/跳转。try 外为 None。
_eh_dispatch_bb: llvmlite.BasicBlock | t.CPtr
_eh_code_ptr: llvmlite.Value | t.CPtr
_eh_msg_ptr: llvmlite.Value | t.CPtr
# 当前函数的 __eh_msg_out__(i8**)/__eh_code_out__(i32*)输出参数
# (函数签名含 raise 时由 translate_function_def/_translate_method 注入)。
# 无 EH 参数时 _cur_func_has_eh == 0。
_eh_msg_out_param: llvmlite.Value | t.CPtr
_eh_code_out_param: llvmlite.Value | t.CPtr
_cur_func_has_eh: int
# 模块 SHA1 前缀(16 字符),用于函数名混淆(SHA1 命名空间)
ModuleSha1: str
# 当前文件所属包名(如 "llvmlite"),用于解析相对导入(from . import ...)
# None 表示当前文件是顶级模块(无包),相对导入无法解析
CurrentPackage: str
# 闭包 env 中的 nonlocal 变量偏移映射(每个 nonlocal 变量在 env 中的字节偏移)
_closure_env_offsets: t.CInt
_closure_env_count: t.CInt
# lambda 表达式全局唯一 ID 计数器(生成提升函数名 __lambda_{id})
# 独立于 _closure_env_count:后者随函数进入被 clear_scope_names 重置,
# lambda ID 需跨函数全局唯一(提升函数名避免冲突)
_lambda_count: t.CInt
# 函数链内 nonlocal 变量集合(预扫描收集,含嵌套函数声明的 nonlocal)。
# 用于"不同层捕获不同变量"场景(如 level1→a, level2→b):b 定义在最外层
# 但 level1 不捕获——level1 env 无 b 槽,level2 无法经 env 链获取 b 的堆。
# 解决:在最外层函数中为链内所有 nonlocal 建堆,并把堆地址 store 到
# 模块级映射变量 @nlheap.{sha1}.{name};内层闭包创建时从映射 load。
_nl_chain_names: t.CVoid | t.CPtr
_nl_chain_count: t.CInt
# 链内 nonlocal 堆预建标志(每函数只建一次):translate_function_def 进入
# 新函数时重置为 0;_prepare_chain_nonlocals 首次调用置 1。多个嵌套函数
# 定义共享同一堆(防重复 malloc + 二次重定向 VarEntry.Alloca)。
_nl_chain_done: t.CInt
# 泛型特化上下文:当前正在特化的类型参数名/实参名列表
# 由 _specialize_generic_class 设置,方法体翻译时用于将 T 替换为具体类型
# None 表示不在泛型特化上下文中
GenericTypeParamNames: list[str] | t.CPtr
GenericTypeArgs: list[str] | t.CPtr
# Phase 1a 声明模式标志:1=只注册 struct/enum/union 不翻译代码体,0=全量翻译
_declare_only: t.CInt
# Phase A 依赖未就绪标志:1=本文件翻译时依赖模块的 text.ll 尚未生成
# (同项目源文件按顺序翻译,main.vp 先于其 import 的 mbuddytest.vp),
# 跨模块函数返回类型无法精确解析。Phase A 多遍循环据此下遍重试。
_deps_pending: t.CInt
# 嵌套作用域符号表
SymTab: GenTypes.SymbolTable | t.CPtr
def __init__(self):
self.Module = None
self.Pool = None
self._funcs = None
self._func_count = 0
self._cur_func = None
self._cur_builder = None
self._break_bb = None
self._continue_bb = None
self._label_counter = 0
self._imported_modules = None
self._from_imports = None
self._global_names = None
self._global_name_count = 0
self._nonlocal_names = None
self._nonlocal_name_count = 0
self._nonlocal_types = None
self._cur_func_name = None
self._va_list_ptr = None
self._vararg_name = None
self._stop_iter_flag_ptr = None
self._eh_dispatch_bb = None
self._eh_code_ptr = None
self._eh_msg_ptr = None
self._eh_msg_out_param = None
self._eh_code_out_param = None
self._cur_func_has_eh = 0
self.ModuleSha1 = None
self.CurrentPackage = None
self._closure_env_offsets = 0
self._closure_env_count = 0
self._lambda_count = 0
self._nl_chain_names = None
self._nl_chain_count = 0
self._nl_chain_done = 0
self.GenericTypeParamNames = None
self.GenericTypeArgs = None
self._declare_only = 0
self._deps_pending = 0
self.SymTab = None
self._async_funcs = None
self._async_func_count = 0
self._coro_var_async = None
self._coro_var_async_count = 0
self._coro_locals = None
self._coro_local_count = 0
self._coro_frames = None
self._coro_frame_count = 0
self._coro_state_ptr = None
self._coro_done_ptr = None
self._coro_ret_ptr = None
self._coro_exit_bb = None
self._coro_await_slot = None
self._coro_frame = None
self._coro_next_state = 1
self._coro_nseg = 0
self._coro_params = None
self._coro_param_count = 0
self._coro_hoist_count = 0
self._coro_await_count = 0
self._coro_local_base = 3
self._coro_extra_base = 3
self._coro_break_bb = None
self._coro_continue_bb = None
self._coro_state_bbs = None
self._coro_exc_ptr = None
self._coro_msg_ptr = None
self._coro_wake_ptr = None
self._coro_try_stack = None
self._coro_try_stack_count = 0
self._coro_for_counter = 0
self._coro_scan_for_count = 0
self._coro_uses_sleep = 0
self._coro_extra_types = None
self._coro_extra_count = 0
self._coro_await_slots = None
self._coro_for_slots = None
self._coro_wake_names = None
self._coro_wake_idxs = None
self._coro_wake_count = 0
# ============================================================
# 状态初始化
# ============================================================
def _init_state(self, pool: memhub.MemBuddy | t.CPtr):
"""初始化变量表、函数表和子 Handle"""
self.Pool = pool
if self._funcs is None:
self._funcs = GenApi.init_func_table(pool, MAX_FUNCS)
# 嵌套作用域符号表(新版)
if self.SymTab is None:
self.SymTab = GenApi.init_symbol_table(pool)
# global/nonlocal 名称集合缓冲区(32 个 char* 指针 = 256 字节)
if self._global_names is None:
self._global_names = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
if self._global_names is not None:
string.memset(self._global_names, 0, MAX_GLOBAL_NAMES * 8)
self._global_name_count = 0
if self._nonlocal_names is None:
self._nonlocal_names = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
if self._nonlocal_names is not None:
string.memset(self._nonlocal_names, 0, MAX_GLOBAL_NAMES * 8)
self._nonlocal_name_count = 0
# nonlocal 变量类型数组(LLVMType 指针数组,与 _nonlocal_names 一一对应)
if self._nonlocal_types is None:
self._nonlocal_types = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
if self._nonlocal_types is not None:
string.memset(self._nonlocal_types, 0, MAX_GLOBAL_NAMES * 8)
# 协程表(TPV-M1):async 函数名 / 变量→async 映射 / 局部名 / 帧类型
if self._async_funcs is None:
self._async_funcs = stdlib.malloc(MAX_CORO_FUNCS * 8)
if self._async_funcs is not None:
string.memset(self._async_funcs, 0, MAX_CORO_FUNCS * 8)
self._async_func_count = 0
if self._coro_var_async is None:
self._coro_var_async = stdlib.malloc(MAX_CORO_VARS * 16)
if self._coro_var_async is not None:
string.memset(self._coro_var_async, 0, MAX_CORO_VARS * 16)
self._coro_var_async_count = 0
if self._coro_locals is None:
self._coro_locals = stdlib.malloc(MAX_CORO_VARS * 8)
if self._coro_locals is not None:
string.memset(self._coro_locals, 0, MAX_CORO_VARS * 8)
if self._coro_params is None:
self._coro_params = stdlib.malloc(MAX_CORO_VARS * 8)
if self._coro_params is not None:
string.memset(self._coro_params, 0, MAX_CORO_VARS * 8)
if self._coro_try_stack is None:
self._coro_try_stack = stdlib.malloc(MAX_CORO_TRY * 8)
if self._coro_try_stack is not None:
string.memset(self._coro_try_stack, 0, MAX_CORO_TRY * 8)
if self._coro_extra_types is None:
self._coro_extra_types = stdlib.malloc(MAX_CORO_VARS)
if self._coro_extra_types is not None:
string.memset(self._coro_extra_types, 0, MAX_CORO_VARS)
if self._coro_await_slots is None:
self._coro_await_slots = stdlib.malloc(MAX_CORO_VARS * 4)
if self._coro_await_slots is not None:
string.memset(self._coro_await_slots, 0, MAX_CORO_VARS * 4)
if self._coro_for_slots is None:
self._coro_for_slots = stdlib.malloc(MAX_CORO_VARS * 4)
if self._coro_for_slots is not None:
string.memset(self._coro_for_slots, 0, MAX_CORO_VARS * 4)
if self._coro_wake_names is None:
self._coro_wake_names = stdlib.malloc(MAX_CORO_FUNCS * 8)
if self._coro_wake_names is not None:
string.memset(self._coro_wake_names, 0, MAX_CORO_FUNCS * 8)
if self._coro_wake_idxs is None:
self._coro_wake_idxs = stdlib.malloc(MAX_CORO_FUNCS * 4)
if self._coro_wake_idxs is not None:
string.memset(self._coro_wake_idxs, 0, MAX_CORO_FUNCS * 4)
self._coro_local_count = 0
if self._coro_frames is None:
self._coro_frames = stdlib.malloc(MAX_CORO_FUNCS * 16)
if self._coro_frames is not None:
string.memset(self._coro_frames, 0, MAX_CORO_FUNCS * 16)
self._coro_frame_count = 0
# ============================================================
# 主翻译入口
# ============================================================
def translate(self, tree: ast.AST | t.CPtr) -> int:
"""将 AST 翻译为 LLVM IR"""
if tree is None:
return 1
if _mbuddy is None:
return 1
pool: memhub.MemBuddy | t.CPtr = _mbuddy
# 初始化状态
self._init_state(pool)
# 创建 LLVM 模块
mod: llvmlite.LLVMModule | t.CPtr = llvmlite.new_module(pool, "main")
if mod is None:
VLogger.error("NewModule returned NULL", "TR")
return 1
self.Module = mod
# 设置目标(优先使用 project.vpj 中的配置)
triple: str = Config.TargetTriple
if triple is None:
triple = "x86_64-pc-windows-msvc"
llvmlite.module_set_target(mod, triple)
# 类型
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 声明 printf
printf_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, "printf", i32_ty)
if printf_func is None:
VLogger.error("CreateDeclare printf returned NULL", "TR")
return 1
llvmlite.add_param(pool, printf_func, i8_ptr_ty, "fmt")
printf_func.IsVarArg = 1
# 声明 LLVM va_start/va_end intrinsic(供 *args 函数体内使用)
# declare void @llvm.va_start(i8*)
# declare void @llvm.va_end(i8*)
void_ty_decl: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
va_start_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, "llvm.va_start", void_ty_decl)
if va_start_func is not None:
llvmlite.add_param(pool, va_start_func, i8_ptr_ty, "ap")
va_end_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, "llvm.va_end", void_ty_decl)
if va_end_func is not None:
llvmlite.add_param(pool, va_end_func, i8_ptr_ty, "ap")
# 声明 malloc(闭包分配用)
malloc_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, "malloc", i8_ptr_ty)
if malloc_func is not None:
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
llvmlite.add_param(pool, malloc_func, i64_ty, "size")
# 检查用户是否定义了 main 函数
has_user_main: int = 0
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is not None:
cn_count: t.CSizeT = len(ch)
for ci in range(cn_count):
child: ast.AST | t.CPtr = ch.get(ci)
if child is not None and child.kind() == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
if fd is not None and fd.name is not None:
if string.strcmp(fd.name, "main") == 0:
has_user_main = 1
break
if self._declare_only != 0:
# Phase 1a-pre(2=import扫描) / Phase 1a(1=struct注册): 只处理模块级,不创建 main 函数和 builder
self._translate_module_level(pool, mod, tree)
elif has_user_main == 0:
# 无用户 main → 只翻译模块级语句,不创建 wrapper main
# 修复:避免每个文件都生成 define i32 @main() 导致链接时 main 冲突
# 只有包含 def main() 的入口文件才会有 define i32 @main()
self._translate_module_level(pool, mod, tree)
else:
# 用户已定义 main → 委托 HandlesMain 翻译模块级
self._translate_module_level(pool, mod, tree)
return 0
# ============================================================
# 包装 main 函数(无用户 main 时)→ 委托 HandlesMain
# ============================================================
def _translate_wrapper_main(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
tree: ast.AST | t.CPtr,
i32_ty: llvmlite.LLVMType | t.CPtr):
"""委托 GenApi.create_wrapper_main() 创建包装 main(trans 单参)"""
added: int = GenApi.create_wrapper_main(self, tree, i32_ty)
# ============================================================
# 模块级翻译(用户已定义 main)→ 委托 HandlesMain
# ============================================================
def _translate_module_level(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
tree: ast.AST | t.CPtr):
"""委托 GenApi.translate_children() 翻译模块级语句(trans 单参)"""
# 全量翻译模式下,先处理导入语句,再创建前向声明,解决前向引用问题
if self._declare_only == 0:
# 预处理导入语句,确保 _imported_modules 和 _from_imports 已填充
# (前向声明需要解析类型注解,如 t.CArray[str] 依赖 t 模块已导入)
# 记录最后处理的 import 行号,供 vpi 注入错误上下文帧定位
cur_imp_lineno: int = 0
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is not None:
cn_count: t.CSizeT = len(ch)
for ci in range(cn_count):
child: ast.AST | t.CPtr = ch.get(ci)
if child is None:
continue
kd: int = child.kind()
if kd == ast.ASTKind.Import:
cur_imp_lineno = child.lineno
GenApi.translate_import(self, child)
elif kd == ast.ASTKind.ImportFrom:
cur_imp_lineno = child.lineno
GenApi.translate_import_from_module(self, child)
GenApi.translate_import_from_names(self, child)
# 对齐 TPC _inject_auto_imports:检测源文件对 list/dict 容器的使用
# (裸名注解 d: dict / 构造 dict(...) / 下标 list[T]),自动把容器
# 类标记为可见结构体。背景:_list.vp/_dict.vp 定义内建容器类且已由
# Phase1 注册,但源文件不显式 import 它们(TPC 在翻译前自动注入
# import _list/_dict);TPV 用可见性机制等效替代——否则裸名
# `d: dict` 注解 resolve_annotation_type 时 is_struct_visible=0
# 解析失败(DictTest 的 "变量类型注解解析失败")。
self._inject_auto_container_visibility(pool, tree)
# 扫描当前模块的 CTypedef 声明已由 Phase1 1b 文本扫描注册
# (RegisterCTypedefFromSource,见 Phase1Main.vp)——AST 字符串字段
# token 零拷贝指向 src_buf,跨阶段内容不稳定,此处 AST 扫描读 attr
# 会 0xC0000005(stdint.vp 崩溃根因),故移除。
# 跨模块 .vpi 注入:加载依赖模块的 .vpi 文件,
# 将函数 declare 声明注入到当前模块,解决跨模块函数调用找不到函数的问题
# 严格保证 declare_only == 0 时才注入(避免 Phase A-pre-inc 死锁)
if self._imported_modules is not None:
# 错误上下文帧:当前模块触发依赖 stub 加载的 import 位置
GenApi.push_trace(GenApi.get_current_file(), cur_imp_lineno)
StubMergerBuild.LoadDepVpiIntoModule(
pool, mod, self._imported_modules, self._from_imports,
GenApi.get_temp_dir(), self)
GenApi.pop_trace()
# 预注册模块级变量(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:
GenApi.handle_module_level_var(self, child)
# 创建前向声明
GenApi.forward_declare_functions(self, tree)
added: int = GenApi.translate_children(self, tree)
# ============================================================
# 容器模块自动可见性注入(对齐 TPC _inject_auto_imports)
#
# TPC 在翻译前自动注入 `import _list / import _dict (+json)` 到源文件,
# 使 list/dict 内建容器类在符号表中可见。TPV 用可见性机制等效替代:
# _list.vp/_dict.vp 已由 Phase1 注册(可达集合含它们),这里只需在
# 当前文件翻译时把容器类名标记为可见结构体,裸名注解/构造即可解析。
#
# 检测模式(与 TPC Phase2Translator._inject_auto_imports 一致):
# - AnnAssign 注解裸名 Name(list/dict):d: dict
# - 构造调用 func 裸名 Name(list/dict):dict(bd)
# - Subscript 的 value 裸名 Name(list/dict):list[int]
# ============================================================
def _inject_auto_container_visibility(self, pool: memhub.MemBuddy | t.CPtr,
tree: ast.AST | t.CPtr):
"""检测 list/dict 容器使用,注入容器类可见性"""
if tree is None or tree.children is None:
return
# 用栈式缓冲区传递检测结果(跨函数参数用 4 字节 int 指针)
need_list_box: t.CPtr = stdlib.malloc(4)
need_dict_box: t.CPtr = stdlib.malloc(4)
if need_list_box is None or need_dict_box is None:
if need_list_box is not None:
stdlib.free(need_list_box)
if need_dict_box is not None:
stdlib.free(need_dict_box)
return
need_list_box[0] = 0
need_dict_box[0] = 0
self._scan_container_usage(tree, need_list_box, need_dict_box)
if need_dict_box[0] != 0:
# dict 类可见 + _dict 模块导入(构造/方法调用的 SHA1 解析)
GenApi.add_visible_struct(pool, "dict")
if self._imported_modules is None or \
GenApi.is_module_imported(self._imported_modules, "_dict") == 0:
self._imported_modules = GenApi.add_imported_module(
pool, self._imported_modules, "_dict")
if need_list_box[0] != 0:
GenApi.add_visible_struct(pool, "list")
if self._imported_modules is None or \
GenApi.is_module_imported(self._imported_modules, "_list") == 0:
self._imported_modules = GenApi.add_imported_module(
pool, self._imported_modules, "_list")
stdlib.free(need_list_box)
stdlib.free(need_dict_box)
def _scan_container_usage(self, node: ast.AST | t.CPtr,
need_list_box: t.CPtr,
need_dict_box: t.CPtr):
"""递归扫描 AST 语句结构,检测 list/dict 容器使用"""
if node is None:
return
k: int = node.kind()
if k == ast.ASTKind.AnnAssign:
aa_s: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa_s.annotation is not None:
self._check_container_type_node(aa_s.annotation, need_list_box, need_dict_box)
elif k == ast.ASTKind.Call:
cl_s: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl_s.func is not None:
self._check_container_type_node(cl_s.func, need_list_box, need_dict_box)
elif k == ast.ASTKind.Subscript:
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub_s.value is not None:
self._check_container_type_node(sub_s.value, need_list_box, need_dict_box)
elif k == ast.ASTKind.List:
# [] 字面量:容器模板仍需注册
need_list_box[0] = 1
elif k == ast.ASTKind.Dict:
# {} 字面量:容器模板仍需注册
need_dict_box[0] = 1
# 递归语句块子节点
if node.children is not None:
ch_s: list[ast.AST | t.CPtr] | t.CPtr = node.children
n_s: t.CSizeT = len(ch_s)
for ci_s in range(n_s):
self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box)
def _check_container_type_node(self, node: ast.AST | t.CPtr,
need_list_box: t.CPtr,
need_dict_box: t.CPtr):
"""检测类型位置节点是否为 list/dict(支持 Name 与 Subscript 外层)"""
if node is None:
return
k: int = node.kind()
if k == ast.ASTKind.Name:
nm_s: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm_s.id is not None:
if string.strcmp(nm_s.id, "dict") == 0:
need_dict_box[0] = 1
elif string.strcmp(nm_s.id, "list") == 0:
need_list_box[0] = 1
elif k == ast.ASTKind.Subscript:
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub_s.value is not None:
self._check_container_type_node(sub_s.value, need_list_box, need_dict_box)
# ============================================================
# IR 输出
# ============================================================
def dump_ir(self, buf: bytes, size: t.CSizeT, mode: int):
"""将 LLVM IR 输出到缓冲区
mode: 0=完整, 1=stub(仅声明), 2=text(仅代码)
"""
if self.Module is None or buf is None:
return
if _mbuddy is None:
return
buf[0] = '\0'
llvmlite.LLVMModulePrint(buf, size, self.Module, _mbuddy, mode)
# ============================================================
# global/nonlocal 名称管理(模块级辅助函数,委托到 gen 方法)
# ============================================================
def add_global_name(trans: HT.Translator | t.CPtr, name: str):
"""添加一个 global 变量名(委托到 gen.add_global_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
g.TranslatorGen.add_global_name(trans, name)
def is_global_name(trans: HT.Translator | t.CPtr, name: str) -> int:
"""检查 name 是否在当前函数的 global 集合中(委托到 gen.is_global_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.TranslatorGen.is_global_name(trans, name)
return 0
def add_nonlocal_name(trans: HT.Translator | t.CPtr, name: str,
var_ty: llvmlite.LLVMType | t.CPtr = None):
"""添加一个 nonlocal 变量名(委托到 gen.add_nonlocal_name)
Args:
name: nonlocal 变量名
var_ty: 变量的 LLVM 类型(用于 env 指针数组方案的类型恢复,
None 时回退为 i32)
"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
g.TranslatorGen.add_nonlocal_name(trans, name, var_ty)
def get_nonlocal_type(trans: HT.Translator | t.CPtr,
name: str) -> llvmlite.LLVMType | t.CPtr:
"""获取 nonlocal 变量的 LLVM 类型(委托到 gen.get_nonlocal_type)
Returns: 找到的类型指针,找不到返回 None
"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.TranslatorGen.get_nonlocal_type(trans, name)
return None
def is_nonlocal_name(trans: HT.Translator | t.CPtr, name: str) -> int:
"""检查 name 是否在当前函数的 nonlocal 集合中(委托到 gen.is_nonlocal_name)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.TranslatorGen.is_nonlocal_name(trans, name)
return 0
def get_nonlocal_index(trans: HT.Translator | t.CPtr, name: str) -> int:
"""获取 nonlocal 变量在 env 中的索引(0-based),找不到返回 -1(委托到 gen.get_nonlocal_index)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.TranslatorGen.get_nonlocal_index(trans, name)
return 0
def clear_scope_names(trans: HT.Translator | t.CPtr):
"""清空当前函数的 global/nonlocal 名称集合(进入新函数时调用)(委托到 gen.clear_scope_names)"""
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
g.TranslatorGen.clear_scope_names(trans)
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
-9
View File
@@ -1,9 +0,0 @@
import t, c
from stdint import *
# Handles 实体模块(去除委托层后保留:数据类/Translator/实体逻辑)
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.ExprCallTable as ExprCallTable
+507 -377
View File
@@ -1,377 +1,507 @@
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
import w32.win32file as win32file
import w32.win32base as win32base
import hashlib
import viperlib
import lib.Projectrans.Utils as Utils
import lib.core.VLogger as VLogger
# ============================================================
# IncludesScanner - includes 目录递归扫描
#
# 递归扫描 includes 目录,收集所有 .vp/.py 文件路径和 SHA1,
# 为 Phase1 stub 生成和 Phase2 stub 合并提供基础数据。
#
# 使用 Win32 FindFirstFileA/FindNextFileA 实现目录遍历。
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
# 文件路径最大长度
MAX_PATH_LEN: t.CDefine = 512
# 单次扫描最大文件数
MAX_FILES: t.CDefine = 1024
# ============================================================
# FileEntry - 文件条目(路径 + SHA1)
# ============================================================
@t.NoVTable
class FileEntry:
"""扫描到的文件条目"""
Path: str # 文件绝对路径
Sha1: str # SHA1 前16字符(16字节+null)
RelPath: str # 相对于 includes 根目录的路径
ModuleName: str # 模块名(如 "ast.parser")
IncDir: str # 所属 includes 根目录(多 includes 目录支持)
# ============================================================
# ScanResult - 扫描结果
# ============================================================
@t.NoVTable
class ScanResult:
"""扫描结果集合"""
Entries: FileEntry | t.CPtr # FileEntry 数组
Count: t.CInt
Capacity: t.CInt
# ============================================================
# create_scan_result - 创建扫描结果容器
# ============================================================
def create_scan_result(pool: memhub.MemBuddy | t.CPtr) -> ScanResult | t.CPtr:
"""创建扫描结果容器,预分配 MAX_FILES 个槽位"""
if pool is None:
return None
size: t.CSizeT = MAX_FILES * FileEntry.__sizeof__()
# 使用 stdlib.malloc 避免 mbuddy 池耗尽(与 StubMerger 保持一致)
entries: FileEntry | t.CPtr = stdlib.malloc(size)
if entries is None:
return None
string.memset(entries, 0, size)
result: ScanResult | t.CPtr = stdlib.malloc(ScanResult.__sizeof__())
if result is None:
return None
string.memset(result, 0, ScanResult.__sizeof__())
result.Entries = entries
result.Count = 0
result.Capacity = MAX_FILES
return result
# ============================================================
# add_file_entry - 向扫描结果添加文件条目
# ============================================================
def add_file_entry(result: ScanResult | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
abs_path: str, rel_path: str,
sha1: str, inc_dir: str) -> int:
"""添加文件条目到扫描结果,返回 0 成功(inc_dir 为所属 includes 根目录)"""
if result is None or pool is None:
return 1
if result.Count >= result.Capacity:
return 1
# 计算模块名:rel_path 中 / 替换为 .,去掉 .vp/.py 扩展名(__init__.vp 特判由 Utils 统一处理)
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
if mod_buf is None:
return 1
# 获取条目地址
entry_size: t.CSizeT = FileEntry.__sizeof__()
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + result.Count * entry_size
entry: FileEntry | t.CPtr = (FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is None:
return 1
# 复制路径字符串
abs_len: t.CSizeT = string.strlen(abs_path)
abs_buf: str = stdlib.malloc(abs_len + 1)
if abs_buf is None:
return 1
string.strcpy(abs_buf, abs_path)
entry.Path = abs_buf
# 复制 SHA1
sha1_buf: str = stdlib.malloc(17)
if sha1_buf is None:
return 1
string.strcpy(sha1_buf, sha1)
entry.Sha1 = sha1_buf
# 复制相对路径
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
if rel_buf is None:
return 1
string.strcpy(rel_buf, rel_path)
entry.RelPath = rel_buf
# 复制模块名
entry.ModuleName = mod_buf
# 复制所属 includes 根目录(多 includes 目录支持,Phase2 拼路径用)
if inc_dir is not None:
inc_len: t.CSizeT = string.strlen(inc_dir)
inc_buf: str = stdlib.malloc(inc_len + 1)
if inc_buf is None:
return 1
string.strcpy(inc_buf, inc_dir)
entry.IncDir = inc_buf
result.Count += 1
return 0
# ============================================================
# compute_file_sha1 - 读取文件内容并计算 SHA1
#
# 与 Projectrans.py 一致:CRLF → LF 转换后计算 SHA1。
# Projectrans.py 用 Python 文本模式读取(自动 CRLF→LF),
# IncludesScanner 用二进制模式读取,需手动去除 \r。
# ============================================================
def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr,
file_path: str) -> str:
"""读取文件内容并计算 SHA1 前16字符(CRLF→LF 后计算,与 Projectrans.py 一致)"""
if pool is None or file_path is None:
return None
# 读取文件内容(stdlib.malloc 分配,避免 mbuddy 池耗尽)
# BUF_SIZE 与翻译侧 SRC_BUF_SIZE(1MB)一致:若截断到 128KB,
# 超过 128KB 的文件尾部内容变更不会改变 SHA1 → 增量缓存永不失效。
BUF_SIZE: t.CSizeT = 1048576
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
if buf is None:
return None
# 原地去除 \r(CRLF → LF),与 Projectrans.py 文本模式读取一致
buf_len: t.CSizeT = string.strlen(buf)
write_pos: t.CSizeT = 0
for ch in buf:
if ch != '\r':
buf[write_pos] = ch
write_pos += 1
# 添加 null 终止符
buf[write_pos] = 0
# 计算 SHA1
ctx: hashlib.sha1 | t.CPtr = hashlib.sha1()
if ctx is None:
stdlib.free(buf)
return None
ctx.update(buf)
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
if digest is None:
stdlib.free(buf)
return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符,stdlib 分配保持原行为)
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
stdlib.free(buf)
stdlib.free(digest)
return hex_buf
# ============================================================
# scan_directory_recursive - 递归扫描目录
#
# 使用 FindFirstFileA/FindNextFileA 遍历目录树,
# 对每个 .vp/.py 文件计算 SHA1 并添加到结果中。
# ============================================================
def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
root_dir: str,
rel_prefix: str,
result: ScanResult | t.CPtr,
inc_dir: str) -> int:
"""递归扫描目录,收集 .vp/.py 文件(inc_dir 为所属 includes 根目录)"""
if pool is None or root_dir is None or result is None:
return 1
# 子目录递归失败计数(不静默忽略,汇总处 fail-fast 终止)
subdir_failed: int = 0
# 构造搜索模式: root_dir/*
root_len: t.CSizeT = string.strlen(root_dir)
pattern: bytes = stdlib.malloc(root_len + 4)
if pattern is None:
return 1
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
# 使用 FindFirstFileA 开始搜索
find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
return 1
string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__())
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE:
return 1
# 遍历所有文件和子目录
while True:
# 跳过 . 和 ..
fname: str = find_data.cFileName
if fname is not None:
fname0: t.CChar = fname[0]
if fname0 == '.':
fname1: t.CChar = fname[1]
if fname1 == '\0':
# "."
if win32file.FindNextFileA(handle, find_data) == 0:
break
continue
elif fname1 == '.':
fname2: t.CChar = fname[2]
if fname2 == '\0':
# ".."
if win32file.FindNextFileA(handle, find_data) == 0:
break
continue
# 检查是否为目录
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
# 构造完整路径
fname_len: t.CSizeT = string.strlen(fname)
full_path: bytes = Utils.JoinPath(root_dir, fname)
if full_path is None:
break
# 构造相对路径
prefix_len: t.CSizeT = 0
if rel_prefix is not None:
prefix_len = string.strlen(rel_prefix)
rel_path: bytes = None
if rel_prefix is not None and prefix_len > 0:
rel_path = Utils.JoinPath(rel_prefix, fname)
else:
rel_path = stdlib.malloc(fname_len + 1)
if rel_path is None:
break
string.strcpy(rel_path, fname)
if rel_path is None:
break
if is_dir != 0:
# 递归扫描子目录
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0:
subdir_failed += 1
else:
# 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
is_py: int = 0
if Utils.is_vp_at(fname, 0, fname_len) != 0:
is_py = 1
if is_py != 0:
# 跳过 __pycache__ 目录下的文件
is_pycache: int = 0
if rel_prefix is not None:
if string.strcmp(rel_prefix, "__pycache__") == 0:
is_pycache = 1
if is_pycache == 0:
# 计算 SHA1
sha1: str = compute_file_sha1(pool, full_path)
if sha1 is not None:
add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir)
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "扫描: %s -> %s", rel_path, sha1)
VLogger.debug(fb, "scan")
else:
fb_sha: str = VLogger.fmt_buf()
if fb_sha is not None:
viperlib.snprintf(fb_sha, 1024, "SHA1 计算失败: %s", full_path)
VLogger.warning(fb_sha, "scan")
# 继续搜索下一个文件
if win32file.FindNextFileA(handle, find_data) == 0:
break
win32file.FindClose(handle)
if subdir_failed > 0:
fb_sdf: str = VLogger.fmt_buf()
if fb_sdf is not None:
viperlib.snprintf(fb_sdf, 1024, "子目录扫描失败 %d 个", subdir_failed)
VLogger.error(fb_sdf, "scan")
return 0
# ============================================================
# scan_includes - 扫描 includes 目录入口
#
# 扫描指定的 includes 目录,返回所有 .vp/.py 文件的路径和 SHA1。
# ============================================================
def scan_includes(pool: memhub.MemBuddy | t.CPtr,
includes_dirs: str) -> ScanResult | t.CPtr:
"""扫描 includes 目录(支持空格分隔的多目录),返回所有 .vp/.py 文件的扫描结果"""
if pool is None or includes_dirs is None:
return None
result: ScanResult | t.CPtr = create_scan_result(pool)
if result is None:
return None
# 解析空格分隔的多 includes 目录(如 "../../includes ./includes"),逐目录递归扫描合并
dirs_total: t.CSizeT = string.strlen(includes_dirs)
d_i: t.CSizeT = 0
while d_i < dirs_total:
while d_i < dirs_total and includes_dirs[d_i] == ' ':
d_i += 1
if d_i >= dirs_total:
break
d_start: t.CSizeT = d_i
while d_i < dirs_total and includes_dirs[d_i] != ' ':
d_i += 1
d_len: t.CSizeT = d_i - d_start
if d_len == 0:
continue
dir_buf: bytes = stdlib.malloc(d_len + 1)
if dir_buf is None:
VLogger.error("includes 目录分配失败,终止扫描", "scan")
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
dir_buf[d_len] = '\0'
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
stdlib.free(dir_buf)
return result
# ============================================================
# find_entry_by_module - 按模块名查找文件条目
# ============================================================
def find_entry_by_module(result: ScanResult | t.CPtr,
module_name: str) -> FileEntry | t.CPtr:
"""按模块名查找文件条目"""
if result is None or module_name is None:
return None
entry_size: t.CSizeT = FileEntry.__sizeof__()
for i in range(result.Count):
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry: FileEntry | t.CPtr = (FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is not None:
if entry.ModuleName is not None:
if string.strcmp(entry.ModuleName, module_name) == 0:
return entry
return None
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
import w32.win32file as win32file
import w32.win32base as win32base
import hashlib
import viperlib
import lib.Projectrans.Utils as Utils
import lib.core.VLogger as VLogger
# ============================================================
# IncludesScanner - includes 目录递归扫描(Win32 FindFirstFileA/FindNextFileA)
# 递归收集 .vp 文件路径与 SHA1,供 Phase1 stub 生成 / Phase2 stub 合并使用
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy*
# 文件路径最大长度
define MAX_PATH_LEN = 512
# 单次扫描最大文件数
define MAX_FILES = 1024
# ============================================================
# FileEntry - 文件条目(路径 + SHA1)
# ============================================================
novirtual class FileEntry:
"""扫描到的文件条目"""
Path: str # 文件绝对路径
Sha1: str # SHA1 前16字符(16字节+null)
RelPath: str # 相对于 includes 根目录的路径
ModuleName: str # 模块名(如 "ast.parser")
IncDir: str # 所属 includes 根目录(多 includes 目录支持)
# ============================================================
# ScanResult - 扫描结果
# ============================================================
novirtual class ScanResult:
"""扫描结果集合"""
Entries: FileEntry* # FileEntry 数组
Count: int
Capacity: int
# ============================================================
# create_scan_result - 创建扫描结果容器
# ============================================================
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
"""创建扫描结果容器,预分配 MAX_FILES 个槽位"""
if pool is None: return None
size: sizet = MAX_FILES * FileEntry.__sizeof__()
# 使用 stdlib.malloc 避免 mbuddy 池耗尽(与 StubMerger 保持一致)
entries: FileEntry* = stdlib.malloc(size)
if entries is None: return None
string.memset(entries, 0, size)
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
if result is None: return None
string.memset(result, 0, ScanResult.__sizeof__())
result.Entries = entries
result.Count = 0
result.Capacity = MAX_FILES
return result
# ============================================================
# add_file_entry - 向扫描结果添加文件条目
# ============================================================
def add_file_entry(result: ScanResult*,
pool: memhub.MemBuddy*,
abs_path: str, rel_path: str,
sha1: str, inc_dir: str) -> int:
"""添加文件条目到扫描结果,返回 0 成功(inc_dir 为所属 includes 根目录)"""
if result is None or pool is None: return 1
if result.Count >= result.Capacity: return 1
# 计算模块名:rel_path 中 / 替换为 .,去掉 .vp/.py 扩展名(__init__.vp 特判由 Utils 统一处理)
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
if mod_buf is None: return 1
# 获取条目地址
entry_size: sizet = FileEntry.__sizeof__()
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
entry: FileEntry* = (FileEntry*)(entry_addr)
if entry is None: return 1
# 复制路径字符串
abs_len: sizet = string.strlen(abs_path)
abs_buf: str = stdlib.malloc(abs_len + 1)
if abs_buf is None: return 1
string.strcpy(abs_buf, abs_path)
entry.Path = abs_buf
# 复制 SHA1
sha1_buf: str = stdlib.malloc(17)
if sha1_buf is None: return 1
string.strcpy(sha1_buf, sha1)
entry.Sha1 = sha1_buf
# 复制相对路径
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
if rel_buf is None: return 1
string.strcpy(rel_buf, rel_path)
entry.RelPath = rel_buf
# 复制模块名
entry.ModuleName = mod_buf
# 复制所属 includes 根目录(多 includes 目录支持,Phase2 拼路径用)
if inc_dir is not None:
inc_len: sizet = string.strlen(inc_dir)
inc_buf: str = stdlib.malloc(inc_len + 1)
if inc_buf is None:
return 1
string.strcpy(inc_buf, inc_dir)
entry.IncDir = inc_buf
result.Count += 1
return 0
# ============================================================
# compute_file_sha1 - 读取文件内容并计算 SHA1(CRLF→LF 后计算,与 Projectrans.py 一致)
# ============================================================
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
"""读取文件内容并计算 SHA1 前16字符(CRLF→LF 后计算,与 Projectrans.py 一致)"""
if pool is None or file_path is None: return None
# 读取文件内容(stdlib.malloc 分配,避免 mbuddy 池耗尽);BUF_SIZE 须与翻译侧 SRC_BUF_SIZE(1MB)一致,否则尾部变更不改 SHA1 导致缓存永不失效
BUF_SIZE: sizet = 1048576
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
if buf is None: return None
# 原地去除 \r(CRLF → LF),与 Projectrans.py 文本模式读取一致
buf_len: sizet = string.strlen(buf)
write_pos: sizet = 0
for ch in buf:
if ch != '\r':
buf[write_pos] = ch
write_pos += 1
# 添加 null 终止符
buf[write_pos] = 0
# 计算 SHA1
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
if ctx is None:
stdlib.free(buf)
return None
ctx.update(buf)
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
if digest is None:
stdlib.free(buf)
return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符,stdlib 分配保持原行为)
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
stdlib.free(buf)
stdlib.free(digest)
return hex_buf
# ============================================================
# scan_directory_recursive - 递归扫描目录(FindFirstFileA/FindNextFileA 遍历目录树)
# ============================================================
def scan_directory_recursive(pool: memhub.MemBuddy*,
root_dir: str,
rel_prefix: str,
result: ScanResult*,
inc_dir: str) -> int:
"""递归扫描目录,收集 .vp/.py 文件(inc_dir 为所属 includes 根目录)"""
if pool is None or root_dir is None or result is None:
return 1
# 子目录递归失败计数(不静默忽略,汇总处 fail-fast 终止)
subdir_failed: int = 0
# 构造搜索模式: root_dir/*
root_len: sizet = string.strlen(root_dir)
pattern: bytes = stdlib.malloc(root_len + 4)
if pattern is None:
return 1
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
# find_data 缓冲 +16 余量:TPV 的 WIN32_FIND_DATAA.__sizeof__() 小于 Windows 实际写入,越界写会破坏相邻堆块
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(fd_size_s + 16)
if find_data is None:
return 1
string.memset(find_data, 0, fd_size_s + 16)
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE:
return 1
# 遍历所有文件和子目录
while True:
# 跳过 . 和 ..
fname: str = find_data.cFileName
if fname is not None:
fname0: char = fname[0]
if fname0 == '.':
fname1: char = fname[1]
if fname1 == '\0':
# "."
if win32file.FindNextFileA(handle, find_data) == 0:
break
continue
elif fname1 == '.':
fname2: char = fname[2]
if fname2 == '\0':
# ".."
if win32file.FindNextFileA(handle, find_data) == 0:
break
continue
# 检查是否为目录
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
# 构造完整路径
fname_len: sizet = string.strlen(fname)
full_path: bytes = Utils.JoinPath(root_dir, fname)
if full_path is None:
break
# 构造相对路径
prefix_len: sizet = 0
if rel_prefix is not None:
prefix_len = string.strlen(rel_prefix)
rel_path: bytes = None
if rel_prefix is not None and prefix_len > 0:
rel_path = Utils.JoinPath(rel_prefix, fname)
else:
rel_path = stdlib.malloc(fname_len + 1)
if rel_path is None:
break
string.strcpy(rel_path, fname)
if rel_path is None:
break
if is_dir != 0:
# 递归扫描子目录
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0:
subdir_failed += 1
else:
# 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
is_py: int = 0
if Utils.is_vp_at(fname, 0, fname_len) != 0:
is_py = 1
if is_py != 0:
# 跳过 __pycache__ 目录下的文件
is_pycache: int = 0
if rel_prefix is not None:
if string.strcmp(rel_prefix, "__pycache__") == 0:
is_pycache = 1
if is_pycache == 0:
# 计算 SHA1
sha1: str = compute_file_sha1(pool, full_path)
if sha1 is not None:
add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir)
VLogger.debug(f"扫描: {rel_path} -> {sha1}", "scan")
else:
VLogger.warning(f"SHA1 计算失败: {full_path}", "scan")
# 继续搜索下一个文件
if win32file.FindNextFileA(handle, find_data) == 0:
break
win32file.FindClose(handle)
if subdir_failed > 0:
VLogger.error(f"子目录扫描失败 {subdir_failed} 个", "scan")
return 0
# ============================================================
# scan_includes - 扫描 includes 目录入口(返回所有 .vp 文件路径与 SHA1)
# ============================================================
def scan_includes(pool: memhub.MemBuddy*,
includes_dirs: str) -> ScanResult*:
"""扫描 includes 目录(支持空格分隔的多目录),返回所有 .vp/.py 文件的扫描结果"""
if pool is None or includes_dirs is None:
return None
result: ScanResult* = create_scan_result(pool)
if result is None:
return None
# 解析空格分隔的多 includes 目录(如 "../../includes ./includes"),逐目录递归扫描合并
dirs_total: sizet = string.strlen(includes_dirs)
d_i: sizet = 0
while d_i < dirs_total:
while d_i < dirs_total and includes_dirs[d_i] == ' ':
d_i += 1
if d_i >= dirs_total:
break
d_start: sizet = d_i
while d_i < dirs_total and includes_dirs[d_i] != ' ':
d_i += 1
d_len: sizet = d_i - d_start
if d_len == 0:
continue
dir_buf: bytes = stdlib.malloc(d_len + 1)
if dir_buf is None:
VLogger.error("includes 目录分配失败,终止扫描", "scan")
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
dir_buf[d_len] = '\0'
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
stdlib.free(dir_buf)
return result
# ============================================================
# find_entry_by_module - 按模块名查找文件条目
# ============================================================
def find_entry_by_module(result: ScanResult*,
module_name: str) -> FileEntry*:
"""按模块名查找文件条目"""
if result is None or module_name is None:
return None
entry_size: sizet = FileEntry.__sizeof__()
for i in range(result.Count):
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: FileEntry* = (FileEntry*)(entry_addr)
if entry is not None:
if entry.ModuleName is not None:
if string.strcmp(entry.ModuleName, module_name) == 0:
return entry
return None
# ============================================================
# _PathExistsA - 路径存在性检查(GetFileAttributesA)
# ============================================================
def _PathExistsA(path: str) -> int:
"""路径是否存在(1=存在,0=不存在/参数错误)
用 GetFileAttributesA(无输出缓冲)替代 FindFirstFileA:后者写
WIN32_FIND_DATAA 结构,而 TPV 该结构 sizeof 小于 Windows 实际写入
(约 592 字节,差距 >270 字节),会越界写坏相邻堆块(BFS 输出槽
0x2e 崩溃根因)。GetFileAttributesA 只返回属性值,无越界风险。
"""
if path is None:
return 0
attr: ULONG = win32file.GetFileAttributesA(path)
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
return 0
return 1
# ============================================================
# _TryResolveModuleFile - 尝试单个候选路径并写输出
#
# Args:
# inc_dir: includes 根目录
# rel_seg: 模块名转出的相对路径段(ast/parser)
# rel_len: rel_seg 长度
# suffix: 候选后缀(".vp" 或 "/__init__.vp")
# out_abs_path/out_rel_path/out_inc_dir: 输出槽(t.CPtr 指针)
#
# Returns:
# 1 命中(已写输出),0 未命中
# ============================================================
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet,
suffix: str,
out_abs_path: t.CPtr, out_rel_path: t.CPtr,
out_inc_dir: t.CPtr) -> int:
if inc_dir is None or rel_seg is None or suffix is None:
return 0
inc_len: sizet = string.strlen(inc_dir)
suf_len: sizet = string.strlen(suffix)
# 候选绝对路径: {inc}/{rel}{suffix}(手写拼接,避免 %. *s 精度修饰符)
cand_len: sizet = inc_len + 1 + rel_len + suf_len
cand: bytes = stdlib.malloc(cand_len + 1)
if cand is None:
return 0
string.strcpy(cand, inc_dir)
cand[inc_len] = '/'
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
if _PathExistsA(cand) == 0:
stdlib.free(cand)
return 0
# 相对 includes 根目录的路径: {rel}{suffix}
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
if rel_path is None:
stdlib.free(cand)
return 0
string.strncpy(rel_path, rel_seg, rel_len)
rel_path[rel_len] = '\0'
string.strcat(rel_path, suffix)
# 所属 includes 根目录深拷贝:先全部分配就绪再统一写槽,
# 避免 inc 分配失败时 abs/rel 已转移所有权造成的半写状态
inc_buf: bytes = None
if out_inc_dir is not None:
inc_buf = stdlib.malloc(inc_len + 1)
if inc_buf is None:
stdlib.free(cand)
stdlib.free(rel_path)
return 0
string.strcpy(inc_buf, inc_dir)
# 写输出槽(stdlib 深拷贝,调用方负责 free)
# 槽联合必须用 (sizet*):t.CPtr|t.CPtr 会被折叠成 t.CPtr,
# [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为
# 8 字节,写入用 sizet(ptr) 直接转换;读取用 t\.CVoid(u64, t.CPtr)
# 转回指针(t\.CVoid 仅配合 t.CPtr 表示 void* 强转)。
if out_abs_path is not None:
abs_slot: sizet* = (sizet*)(out_abs_path)
abs_slot[0] = sizet(cand)
else:
stdlib.free(cand)
if out_rel_path is not None:
rel_slot: sizet* = (sizet*)(out_rel_path)
rel_slot[0] = sizet(rel_path)
else:
stdlib.free(rel_path)
if out_inc_dir is not None and inc_buf is not None:
inc_slot: sizet* = (sizet*)(out_inc_dir)
inc_slot[0] = sizet(inc_buf)
return 1
# ============================================================
# ResolveModuleToPath - 模块名→文件路径(按文件系统按需解析,不做全量目录遍历)
#
# 解析规则(多 includes 目录逐个尝试,模块优先于包):
# {inc_dir}/{mod.replace('.','/')}.vp (文件模块)
# {inc_dir}/{mod.replace('.','/')}/__init__.vp (包模块)
#
# Args:
# includes_dirs: 空格分隔的多个 includes 根目录
# mod_name: 绝对模块名
# out_abs_path: 输出绝对路径(stdlib.malloc,调用方负责 free)
# out_rel_path: 输出相对 includes 根目录的路径(同上,如 ast/parser.vp)
# out_inc_dir: 输出所属 includes 根目录(同上)
#
# Returns:
# 0 找到;1 未找到/参数错误
# ============================================================
def ResolveModuleToPath(includes_dirs: str, mod_name: str,
out_abs_path: t.CPtr, out_rel_path: t.CPtr,
out_inc_dir: t.CPtr) -> int:
if includes_dirs is None or mod_name is None:
return 1
mn_len: sizet = string.strlen(mod_name)
if mn_len == 0:
return 1
# mod_name → 相对路径段(. → /):ast.parser → ast/parser
rel_seg: bytes = stdlib.malloc(mn_len + 1)
if rel_seg is None:
return 1
rel_len: sizet = 0
for mi in range(mn_len):
ch_m: char = mod_name[mi]
if ch_m == '.':
rel_seg[rel_len] = '/'
else:
rel_seg[rel_len] = ch_m
rel_len += 1
rel_seg[rel_len] = '\0'
found: int = 0
d_total: sizet = string.strlen(includes_dirs)
d_i: sizet = 0
while d_i < d_total:
while d_i < d_total and includes_dirs[d_i] == ' ':
d_i += 1
if d_i >= d_total:
break
d_start: sizet = d_i
while d_i < d_total and includes_dirs[d_i] != ' ':
d_i += 1
d_len: sizet = d_i - d_start
if d_len == 0:
continue
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
if inc_dir_buf is None:
continue
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
inc_dir_buf[d_len] = '\0'
# 候选 1: {inc}/{rel}.vp(文件模块)
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp",
out_abs_path, out_rel_path, out_inc_dir) != 0:
found = 1
stdlib.free(inc_dir_buf)
break
# 候选 2: {inc}/{rel}/__init__.vp(包模块)
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp",
out_abs_path, out_rel_path, out_inc_dir) != 0:
found = 1
stdlib.free(inc_dir_buf)
if found != 0:
break
stdlib.free(rel_seg)
if found != 0:
return 0
return 1
+2970
View File
File diff suppressed because it is too large. Load diff
+275 -305
View File
@@ -1,305 +1,275 @@
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 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 对齐
# ============================================================
# 日志级别
class LogLevel(t.CEnum):
DEBUG = 0
INFO = 1
WARNING = 2
ERROR = 3
SUCCESS = 4
CRITICAL = 5
# Win32 控制台前景色属性
FOREGROUND_BLUE: t.CDefine = 0x0001
FOREGROUND_GREEN: t.CDefine = 0x0002
FOREGROUND_RED: t.CDefine = 0x0004
FOREGROUND_INTENSITY: t.CDefine = 0x0008
FOREGROUND_WHITE: t.CDefine = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
FOREGROUND_CYAN: t.CDefine = FOREGROUND_BLUE | FOREGROUND_GREEN
FOREGROUND_YELLOW: t.CDefine = FOREGROUND_RED | FOREGROUND_GREEN
FOREGROUND_MAGENTA: t.CDefine = FOREGROUND_RED | FOREGROUND_BLUE
# Win32 控制台背景色属性
BACKGROUND_RED: t.CDefine = 0x0040
BACKGROUND_GREEN: t.CDefine = 0x0020
BACKGROUND_BLUE: t.CDefine = 0x0010
BACKGROUND_INTENSITY: t.CDefine = 0x0080
BACKGROUND_WHITE: t.CDefine = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
@t.NoVTable
class Logger:
_level: int
_console_handle: win32base.HANDLE
_use_color: int
__mbuddy__: memhub.MemBuddy | t.CPtr
def __init__(self, level: int = 1):
self._level = level
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
self._use_color = 1
self.__mbuddy__ = _mbuddy
def _set_color(self, attr: WORD) -> int:
uc: int = self._use_color
if uc:
ch: win32base.HANDLE = self._console_handle
ret: int = w32cmd.SetConsoleTextAttribute(ch, attr)
return ret
return 0
def _reset_color(self) -> int:
if self._use_color:
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
return 0
# ============================================================
# _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:
# 前缀符号着色
self._set_color(sym_color)
stdio.printf("%s ", prefix_sym)
# LEVEL 标签着色
self._set_color(level_color)
stdio.printf("%s", level_str)
self._reset_color()
# category 可选
if category is not None and category[0] != 0:
stdio.printf(" [%s]", category)
stdio.printf(": %s\n", msg)
return 0
def debug(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg,
FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
def info(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg,
FOREGROUND_GREEN,
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
return ret
def warning(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.WARNING, "├", "WARN", msg,
FOREGROUND_YELLOW,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
category)
def error(self, msg: str, category: str = "") -> int:
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,
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
# ============================================================
# 高级日志方法
# ============================================================
def banner(self, msg: str) -> int:
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
stdio.printf("\n=== %s ===\n\n", msg)
self._reset_color()
return 0
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
"""格式化编译错误输出(红底白字标题 + 位置 + 错误信息)。"""
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
stdio.printf(" 编译错误 ")
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0:
stdio.printf(" 位置: %s:%d\n", file, line)
elif file[0] != 0:
stdio.printf(" 文件: %s\n", file)
stdio.printf(" 错误: %s\n", msg)
self._reset_color()
return 0
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
"""格式化编译警告输出(黄底黑字标题 + 位置 + 警告信息)。"""
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf(" 编译警告 ")
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0:
stdio.printf(" 位置: %s:%d\n", file, line)
elif file[0] != 0:
stdio.printf(" 文件: %s\n", file)
stdio.printf(" 警告: %s\n", msg)
self._reset_color()
return 0
# 全局 mbuddy 指针(由 lib.InitLib 注入)
_mbuddy: memhub.MemBuddy | t.CPtr
# 全局 logger 指针
_g_logger: Logger | t.CPtr
# 全局格式化缓冲区(用于带变量的日志消息,1024 字节)
_fmt_buf: str
def fmt_buf() -> str:
"""获取全局格式化缓冲区(1024 字节)。
用法:
buf: str = 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
raw: t.CVoid | t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
if raw is None:
return None
_g_logger = raw
_g_logger.__before_init__()
_g_logger.__init__(LogLevel.INFO)
return _g_logger
def set_logger(logger: Logger | t.CPtr) -> int:
"""设置全局 logger 实例。"""
global _g_logger
_g_logger = logger
return 0
# ============================================================
# --nowarn-register 开关(追加):抑制 Phase A 依赖类 WARN 输出——
# ①"外部函数返回类型未登记"(HandlesExprCall)②"Phase A 依赖未就绪,
# 下遍重试"(PhaseA-DBG)。自举编译中大量出现,刷屏影响日志收敛。
# 仅抑制输出,不影响内部逻辑(默认 i32 推断、标记重试等照常执行)。
# ============================================================
_nowarn_register_flag: t.CInt = 0
def set_nowarn_register(flag: int) -> None:
"""设置 --nowarn-register(1=抑制 Phase A 依赖类 WARN 输出)"""
global _nowarn_register_flag
_nowarn_register_flag = flag
def nowarn_register() -> int:
"""查询 --nowarn-register 开关状态(1=抑制)"""
return _nowarn_register_flag
# ============================================================
# 模块级便捷函数(与 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
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 win32base
# ============================================================
# VLogger - 原生日志系统(Windows 控制台彩色输出)
# 输出格式: ├ LEVEL: message / ├ LEVEL[category]: message(与 TPC lib/core/VLogger.py 对齐)
# ============================================================
# 错误恢复(REPL/JIT 模式):error 打印后 longjmp 跳回 REPL 的 setjmp 点。
# mingw-w64 x86_64:longjmp 原型为 void longjmp(jmp_buf, int);setjmp 宏展开为
# _setjmp(buf, NULL)(jmp_buf 实为 _JBTYPE[16],见 Repl.vp 的 _REPL_JMPBUF_SIZE)。
extern def longjmp(buf: rawptr, val: int)
# REPL 恢复点指针(由 Repl 在会话开始时注册、会话结束时清除)。
# 生命周期 = 整个 REPL 会话:Repl 每轮片段都会重新武装(Repl.run 每轮 _setjmp +
# Repl.ReplExecFragment 入口重注册),故它覆盖整条片段流水线(翻译 → 写 .ll →
# llc → clang++ → LoadLibrary → GetProcAddress → 调用 → 回显),而不只是翻译期。
# 批处理路径从不调用 set_repl_jmpbuf ⇒ 恒为 None ⇒ 所有出口退回 sys.exit(1)。
_repl_jmpbuf: rawptr = None
# ============================================================
# repl_abort - TPV 内"诊断后终止"的唯一出口
#
# 约定:调用方**已打印**诊断(Traceback / VLogger.error / [OOM] 等),本函数只决定
# "跳回 REPL 继续"还是"终止进程":
# * REPL 会话(_repl_jmpbuf 已注册):fflush 后 longjmp 回 Repl.run 的恢复点,
# 进程存活、提示符继续(宽容 Traceback);
# * 批处理 / 未武装:sys.exit(1),与改动前的 fail-fast 行为逐字一致。
# 收敛点:Logger.error、模块级 error、TypeGenErr.fatal_error、
# ImportsGenMixin._report_import_error、CallGenBase/VpiReader 的链表哨兵。
# ============================================================
def repl_abort() -> int:
"""诊断后的统一出口:已注册恢复点则 longjmp 回 REPL,否则终止进程。"""
if _repl_jmpbuf is not None:
stdio.fflush(None)
longjmp(_repl_jmpbuf, 1)
sys.exit(1)
return 0
def set_repl_jmpbuf(buf: t.CPtr) -> int:
"""注册 REPL 的 jmp_buf(会话开始;每片段入口可幂等重注册)。"""
global _repl_jmpbuf
_repl_jmpbuf = buf
return 0
def clear_repl_jmpbuf() -> int:
"""清除已注册的 jmp_buf(REPL 会话结束时调用;中途不清)。"""
global _repl_jmpbuf
_repl_jmpbuf = None
return 0
# 日志级别
enum LogLevel:
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
# Win32 控制台前景色属性
define FOREGROUND_BLUE = 0x0001
define FOREGROUND_GREEN = 0x0002
define FOREGROUND_RED = 0x0004
define FOREGROUND_INTENSITY = 0x0008
define FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
define FOREGROUND_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN
define FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN
define FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE
# Win32 控制台背景色属性
define BACKGROUND_RED = 0x0040
define BACKGROUND_GREEN = 0x0020
define BACKGROUND_BLUE = 0x0010
define BACKGROUND_INTENSITY = 0x0080
define BACKGROUND_WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
novirtual class Logger:
_level: int
_console_handle: win32base.HANDLE
_use_color: int
jit_flag: bool
__mbuddy__: memhub.MemBuddy*
def __init__(self, level: int = 1):
self._level = level
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
self._use_color = 1
self.jit_flag = False
self.__mbuddy__ = _mbuddy
def _set_color(self, attr: WORD) -> int:
uc: int = self._use_color
if uc:
ch: win32base.HANDLE = self._console_handle
ret: int = w32cmd.SetConsoleTextAttribute(ch, attr)
return ret
return 0
def _reset_color(self) -> int:
if self._use_color:
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
return 0
# ============================================================
# _log - 核心日志输出(分段着色:前缀符号 ├ / LEVEL 标签 / 消息内容)
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
# ============================================================
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str,
sym_color: WORD, level_color: WORD,
category: str = "") -> int:
# 前缀符号着色
self._set_color(sym_color)
stdio.printf("%s ", prefix_sym)
# LEVEL 标签着色
self._set_color(level_color)
stdio.printf("%s", level_str)
self._reset_color()
# category 可选
if category is not None and category[0] != 0:
stdio.printf(" [%s]", category)
stdio.printf(": %s\n", msg)
return 0
def debug(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
def info(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
return ret
def warning(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
def error(self, msg: str, category: str = "") -> int:
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_INTENSITY, category)
# JIT/REPL 模式:诊断已打印,经统一出口跳回 REPL 循环(未武装时该出口仍终止进程)
if self.jit_flag:
repl_abort()
return ret
# 批处理模式:错误立即终止进程(行为与改动前逐字一致)
sys.exit(1)
return ret
def success(self, msg: str, category: str = "") -> int:
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
def set_jit_flag(self, v: bool) -> int:
"""设置 JIT/REPL 模式标志:True 时 error 改为 longjmp 回 REPL 循环。"""
self.jit_flag = v
return 0
# ============================================================
# 高级日志方法
# ============================================================
def banner(self, msg: str) -> int:
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
stdio.printf("\n=== %s ===\n\n", msg)
self._reset_color()
return 0
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
"""格式化编译错误输出(红底白字标题 + 位置 + 错误信息)。"""
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
stdio.printf(" 编译错误 ")
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0:
stdio.printf(" 位置: %s:%d\n", file, line)
elif file[0] != 0:
stdio.printf(" 文件: %s\n", file)
stdio.printf(" 错误: %s\n", msg)
self._reset_color()
return 0
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
"""格式化编译警告输出(黄底黑字标题 + 位置 + 警告信息)。"""
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf(" 编译警告 ")
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
stdio.printf("\n")
if file is not None:
if file[0] != 0 and line > 0:
stdio.printf(" 位置: %s:%d\n", file, line)
elif file[0] != 0:
stdio.printf(" 文件: %s\n", file)
stdio.printf(" 警告: %s\n", msg)
self._reset_color()
return 0
# 全局 mbuddy 指针(由 lib.InitLib 注入)
_mbuddy: memhub.MemBuddy*
# 全局 logger 指针
logger: Logger*
log: Logger*
def init_logger():
"""初始化全局 logger 实例。"""
global logger, log
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
# if raw is None: return None 存疑,如何去管理内存?
logger = raw
logger.__before_init__()
logger.__init__(LogLevel.INFO)
log = logger
# if fmtbuffer is None: return None
# ============================================================
# --nowarn-register 开关:抑制 Phase A 依赖类 WARN 输出(仅抑制输出,不影响内部逻辑)
# ============================================================
_nowarn_register: int = 0
# ============================================================
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐,无需先 get_logger())
# ============================================================
def info(msg: str, category: str = "") -> int:
"""输出 INFO 级别日志。"""
if log is not None: return log.info(msg, category)
return 0
def warning(msg: str, category: str = "") -> int:
"""输出 WARN 级别日志。"""
if log is not None: return log.warning(msg, category)
return 0
def error(msg: str, category: str = "") -> int:
"""输出 ERROR 级别日志:批处理模式终止进程;JIT/REPL 模式 longjmp 回 REPL。"""
if log is not None: return log.error(msg, category)
# logger 不可用时也走统一出口(REPL 已注册恢复点时仍可恢复)
repl_abort()
return 0
def success(msg: str, category: str = "") -> int:
"""输出 SUCCESS 级别日志。"""
if log is not None: return log.success(msg, category)
return 0
def critical(msg: str, category: str = "") -> int:
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
if log is not None: return log.critical(msg, category)
return 0
def debug(msg: str, category: str = "") -> int:
"""输出 DEBUG 级别日志。"""
if log is not None: return log.debug(msg, category)
return 0
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff. Show more