chore(comments): includes 头注释英文化(R11 收尾)

把 includes 下 64 个 .vp 的文件头注释(含 traceback.vp 第 13-427 行的整块文件级说明)以及 2 处漏网的正文中文注释(ast/lexer.vp、json/__writer.vp)与 1 处中文 docstring(memhub.vp)逐行译为英文;同步把 fltk/gen_ffi.py 的生成模板与其自身注释英文化,使重新生成仍为英文。

只改注释/文档串文字:行数与代码逐字节不变(骨架比对 140 文件漂移 0),运行时中文字符串字面量 305 处保持原样。验证:三重重编 SUCCESS(170.21s)+ 全量 77/77 顺序回归通过 + VET 编译 SUCCESS / --search-paths 7 条 exit 0。
This commit is contained in:
TermiNexus committed 2026-10-06 09:42:25 +08:00
1 parent 14ba84db8e
commit c0d56b127d
65 files changed
+744 -744

No files matched your search

+17 -17
View File
@@ -11,25 +11,25 @@ from .tokens import (
# ============================================================ # ============================================================
# 整数字面量溢出(词法层 fail-fast) # Integer literal overflow (lexer-layer fail-fast)
# #
# 整数常量在 token/AST 中是 int64(tokens.vp `int_val` / exprs.vp `int_val`)。 # Integer constants are int64 in the token/AST (tokens.vp `int_val` / exprs.vp `int_val`).
# 旧实现用 `string.atoll`(string.vp:292)在 int64 下 `num = num * 10 + digit` # The old implementation used `string.atoll` (string.vp:292) to, under int64, do `num = num * 10 + digit`
# 逐位累加 ⇒ 值 > 2^64-1 时**静默回绕**(如 2^64 回绕成 0,2^64-1 回绕成 -1)。 # digit-by-digit accumulation => when the value > 2^64-1 it **silently wraps** (e.g. 2^64 wraps to 0, 2^64-1 wraps to -1).
# 本实现在 `_emit_number` 里对**整数**字面量先做纯文本幅值判定 # This implementation, in `_emit_number`, first performs a pure-text magnitude check on **integer** literals
# (`_lex_int_exceeds_u64`,与 AssignGenMixin._int_text_exceeds_64 同口径:按 # (`_lex_int_exceeds_u64`, using the same criteria as AssignGenMixin._int_text_exceeds_64: by
# 进制统计有效位数 / 与 2^64-1 的十进制文本做长度+字典序比较,**不落进任何定宽 # radix, counting significant digits / comparing length + lexicographic order against the decimal text of 2^64-1, **without landing in any fixed-width
# 容器、不回绕后再比较**),超出 2^64-1 即记录 fail-fast(不再静默回绕)。 # container and without wrapping before the comparison**), so exceeding 2^64-1 records a fail-fast (no more silent wrapping).
# 合法范围 0..2^64-1 仍按 64 位位模式存入 int64(既有合法路径的产物形态不变, # The legal range 0..2^64-1 is still stored into int64 as a 64-bit bit pattern (the shape of the results on the existing legal path is unchanged,
# 如 0xFFFFFFFFFFFFFFFF → int64 -1、0xA5A5A5A5A5A5A5A5 保持其 64 位位模式)。 # e.g. 0xFFFFFFFFFFFFFFFF -> int64 -1, 0xA5A5A5A5A5A5A5A5 keeps its 64-bit bit pattern).
# 各进制(2/8/10/16)口径与 TPC(Python int(x, 0),任意精度)一致:接受区间同为 # The criteria for each radix (2/8/10/16) match TPC's (Python int(x, 0), arbitrary precision): the accepted interval is likewise
# [-2^63, 2^64-1](字面量本身无符号,负数侧由一元减表达),超出即拒绝。 # [-2^63, 2^64-1] (literals themselves are unsigned; the negative side is expressed by unary minus), and anything beyond is rejected.
# #
# 上报通道:lexer 无独立 fatal 出口,故记录到模块级三元组(`_lex_record_overflow`), # Reporting channel: the lexer has no separate fatal exit, so it records into a module-level triple (`_lex_record_overflow`),
# 由 parser 的 parse_tokens 取出后转写 _g_parse_err_*(位置 + 消息),复用既有 # which the parser's parse_tokens reads and transcribes into _g_parse_err_* (position + message), reusing the existing
# Traceback/SyntaxError 报告路径("文件:行:列 + 源码行"由该路径补全并终止编译)。 # Traceback/SyntaxError report path (the "file:line:col + source line" is filled in by that path, which also terminates compilation).
# 这覆盖全部源码字面量(文件级解析必经 parse_tokens);parse_expression 只用于编译器 # This covers all source literals (file-level parsing necessarily goes through parse_tokens); parse_expression is used only for compiler-
# 合成的短类型串,不承载源码字面量,故不另设通道。 # synthesized short type strings and carries no source literals, so no separate channel is provided.
# ============================================================ # ============================================================
_g_lex_err_line: int = 0 _g_lex_err_line: int = 0
_g_lex_err_col: int = 0 _g_lex_err_col: int = 0
+10 -10
View File
@@ -1,7 +1,7 @@
# ============================================================ # ============================================================
# fltk — Viper 的 FLTK GUI 绑定包(对接 cfltk C API) # fltk — Viper FLTK GUI binding package (wraps the cfltk C API)
# #
# 用户用法: # Usage:
# import fltk # import fltk
# win = fltk.Fl_Window(400, 300, "Hello") # win = fltk.Fl_Window(400, 300, "Hello")
# box = fltk.Fl_Box(10, 10, 380, 40, "Hello, World!") # box = fltk.Fl_Box(10, 10, 380, 40, "Hello, World!")
@@ -10,18 +10,18 @@
# win.show() # win.show()
# fltk.Fl.run() # fltk.Fl.run()
# #
# 说明: # Notes:
# * 本包只做 extern FFI(fltk._cfl* 模块)+ 少量 Pythonic 包装类; # * This package only provides extern FFI (the fltk._cfl* modules) + a few Pythonic wrapper classes;
# 所有 FLTK 对象都是不透明指针,方法即 cfltk 的 Fl_<类>_<动作>。 # all FLTK objects are opaque pointers; methods map to cfltk's Fl_<Class>_<action>.
# * 完整 C API 通过 `from fltk._cfl import *` 等直接可用。 # * The full C API is directly available via `from fltk._cfl import *` etc.
# * 回调支持:Viper 模块级函数可直接作为 Fl_Callback 传给 FLTK—— # * Callback support: a Viper module-level function can be passed to FLTK directly as an Fl_Callback--
# TPV 把函数名自动 bitcast 成函数指针(如 CreateThread 传函数)。 # TPV auto-bitcasts the function name into a function pointer (e.g. CreateThread passes a function).
# 回调签名约定: def cb(w: bytes, data: bytes): ...(w=控件指针,data=用户数据) # Callback signature convention: def cb(w: bytes, data: bytes): ... (w=widget pointer, data=user data)
# def on_click(w: bytes, data: bytes): # def on_click(w: bytes, data: bytes):
# Fl_Widget_set_label(w, "Clicked!") # Fl_Widget_set_label(w, "Clicked!")
# btn.set_callback(on_click, 0) # btn.set_callback(on_click, 0)
# Fl.add_timeout(1.0, on_tick, 0) # Fl.add_timeout(1.0, on_tick, 0)
# * 事件驱动也可用 Fl.check()/Fl.event*() 轮询。 # * Event-driven code may also poll with Fl.check()/Fl.event*().
# ============================================================ # ============================================================
import t, c import t, c
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl.vp — cfl.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl.vp — extern FFI declarations for cfl.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_run() -> int extern def Fl_run() -> int
extern def Fl_check() -> int extern def Fl_check() -> int
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_box.vp — cfl_box.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_box.vp — extern FFI declarations for cfl_box.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Box (widget_declare) ----- # ----- Fl_Box (widget_declare) -----
extern def Fl_Box_new(x: int, y: int, width: int, height: int, title: str) -> Fl_BoxPtr extern def Fl_Box_new(x: int, y: int, width: int, height: int, title: str) -> Fl_BoxPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_browser.vp — cfl_browser.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_browser.vp — extern FFI declarations for cfl_browser.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Browser (widget_declare) ----- # ----- Fl_Browser (widget_declare) -----
extern def Fl_Browser_new(x: int, y: int, width: int, height: int, title: str) -> Fl_BrowserPtr extern def Fl_Browser_new(x: int, y: int, width: int, height: int, title: str) -> Fl_BrowserPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_button.vp — cfl_button.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_button.vp — extern FFI declarations for cfl_button.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Button (widget_declare) ----- # ----- Fl_Button (widget_declare) -----
extern def Fl_Button_new(x: int, y: int, width: int, height: int, title: str) -> Fl_ButtonPtr extern def Fl_Button_new(x: int, y: int, width: int, height: int, title: str) -> Fl_ButtonPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_dialog.vp — cfl_dialog.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_dialog.vp — extern FFI declarations for cfl_dialog.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_Native_File_Chooser_new(a0: int) -> Fl_Native_File_ChooserPtr extern def Fl_Native_File_Chooser_new(a0: int) -> Fl_Native_File_ChooserPtr
extern def Fl_Native_File_Chooser_delete(self: Fl_Native_File_ChooserPtr) extern def Fl_Native_File_Chooser_delete(self: Fl_Native_File_ChooserPtr)
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_draw.vp — cfl_draw.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_draw.vp — extern FFI declarations for cfl_draw.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_set_color_int(c: uint32) extern def Fl_set_color_int(c: uint32)
extern def Fl_set_color_rgb(r: uint8, g: uint8, b: uint8) extern def Fl_set_color_rgb(r: uint8, g: uint8, b: uint8)
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_group.vp — cfl_group.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_group.vp — extern FFI declarations for cfl_group.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Group (widget_declare) ----- # ----- Fl_Group (widget_declare) -----
extern def Fl_Group_new(x: int, y: int, width: int, height: int, title: str) -> Fl_GroupPtr extern def Fl_Group_new(x: int, y: int, width: int, height: int, title: str) -> Fl_GroupPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_image.vp — cfl_image.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_image.vp — extern FFI declarations for cfl_image.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Image (image_declare) ----- # ----- Fl_Image (image_declare) -----
extern def Fl_Image_draw(self: Fl_ImagePtr, X: int, Y: int, W: int, H: int) extern def Fl_Image_draw(self: Fl_ImagePtr, X: int, Y: int, W: int, H: int)
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_input.vp — cfl_input.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_input.vp — extern FFI declarations for cfl_input.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Input (widget_declare) ----- # ----- Fl_Input (widget_declare) -----
extern def Fl_Input_new(x: int, y: int, width: int, height: int, title: str) -> Fl_InputPtr extern def Fl_Input_new(x: int, y: int, width: int, height: int, title: str) -> Fl_InputPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_lock.vp — cfl_lock.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_lock.vp — extern FFI declarations for cfl_lock.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_mutex_lock() extern def Fl_mutex_lock()
extern def Fl_mutex_unlock() extern def Fl_mutex_unlock()
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_menu.vp — cfl_menu.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_menu.vp — extern FFI declarations for cfl_menu.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Menu_Bar (widget_declare) ----- # ----- Fl_Menu_Bar (widget_declare) -----
extern def Fl_Menu_Bar_new(x: int, y: int, width: int, height: int, title: str) -> Fl_Menu_BarPtr extern def Fl_Menu_Bar_new(x: int, y: int, width: int, height: int, title: str) -> Fl_Menu_BarPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_misc.vp — cfl_misc.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_misc.vp — extern FFI declarations for cfl_misc.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Spinner (widget_declare) ----- # ----- Fl_Spinner (widget_declare) -----
extern def Fl_Spinner_new(x: int, y: int, width: int, height: int, title: str) -> Fl_SpinnerPtr extern def Fl_Spinner_new(x: int, y: int, width: int, height: int, title: str) -> Fl_SpinnerPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_prefs.vp — cfl_prefs.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_prefs.vp — extern FFI declarations for cfl_prefs.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_Preferences_set_file_access(flags: uint32) extern def Fl_Preferences_set_file_access(flags: uint32)
extern def Fl_Preferences_file_access() -> uint32 extern def Fl_Preferences_file_access() -> uint32
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_printer.vp — cfl_printer.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_printer.vp — extern FFI declarations for cfl_printer.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_Printer_new() -> Fl_PrinterPtr extern def Fl_Printer_new() -> Fl_PrinterPtr
extern def Fl_Printer_delete(self: Fl_PrinterPtr) extern def Fl_Printer_delete(self: Fl_PrinterPtr)
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_surface.vp — cfl_surface.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_surface.vp — extern FFI declarations for cfl_surface.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_Surface_Device_set_current(self: Fl_Surface_DevicePtr) extern def Fl_Surface_Device_set_current(self: Fl_Surface_DevicePtr)
extern def Fl_Surface_Device_is_current(self: Fl_Surface_DevicePtr) -> int extern def Fl_Surface_Device_is_current(self: Fl_Surface_DevicePtr) -> int
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_table.vp — cfl_table.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_table.vp — extern FFI declarations for cfl_table.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Table (widget_declare) ----- # ----- Fl_Table (widget_declare) -----
extern def Fl_Table_new(x: int, y: int, width: int, height: int, title: str) -> Fl_TablePtr extern def Fl_Table_new(x: int, y: int, width: int, height: int, title: str) -> Fl_TablePtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_term.vp — cfl_term.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_term.vp — extern FFI declarations for cfl_term.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Terminal (widget_declare) ----- # ----- Fl_Terminal (widget_declare) -----
extern def Fl_Terminal_new(x: int, y: int, width: int, height: int, title: str) -> Fl_TerminalPtr extern def Fl_Terminal_new(x: int, y: int, width: int, height: int, title: str) -> Fl_TerminalPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_text.vp — cfl_text.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_text.vp — extern FFI declarations for cfl_text.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Text_Display (widget_declare) ----- # ----- Fl_Text_Display (widget_declare) -----
extern def Fl_Text_Display_new(x: int, y: int, width: int, height: int, title: str) -> Fl_Text_DisplayPtr extern def Fl_Text_Display_new(x: int, y: int, width: int, height: int, title: str) -> Fl_Text_DisplayPtr
+4 -4
View File
@@ -1,9 +1,9 @@
# ============================================================ # ============================================================
# fltk/_cfl_theme.vp — Fl_Theme(C++ 主题库)→ Viper extern FFI # fltk/_cfl_theme.vp — Fl_Theme (C++ theme library) -> Viper extern FFI
# #
# 对应 Fl_Theme/fltk_theme.cpp 导出的 C 函数(手工维护,gen_ffi 不覆盖)。 # Corresponds to the C functions exported by Fl_Theme/fltk_theme.cpp (hand-maintained, not overwritten by gen_ffi).
# 链接需 libfltk_theme.a(见 project.vpj linker.static_libs)+ # Linking requires libfltk_theme.a (see project.vpj linker.static_libs) +
# libcfltk2.a + libfltk_images.a(Fl_SVG_Image)。 # libcfltk2.a + libfltk_images.a (Fl_SVG_Image).
# ============================================================ # ============================================================
import t, c import t, c
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_tree.vp — cfl_tree.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_tree.vp — extern FFI declarations for cfl_tree.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Tree (widget_declare) ----- # ----- Fl_Tree (widget_declare) -----
extern def Fl_Tree_new(x: int, y: int, width: int, height: int, title: str) -> Fl_TreePtr extern def Fl_Tree_new(x: int, y: int, width: int, height: int, title: str) -> Fl_TreePtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_utils.vp — cfl_utils.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_utils.vp — extern FFI declarations for cfl_utils.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
extern def Fl_filename_expand(out: str, len_: int, in_: str) -> int extern def Fl_filename_expand(out: str, len_: int, in_: str) -> int
extern def Fl_type_name(w: bytes) -> str extern def Fl_type_name(w: bytes) -> str
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_valuator.vp — cfl_valuator.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_valuator.vp — extern FFI declarations for cfl_valuator.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Slider (widget_declare) ----- # ----- Fl_Slider (widget_declare) -----
extern def Fl_Slider_new(x: int, y: int, width: int, height: int, title: str) -> Fl_SliderPtr extern def Fl_Slider_new(x: int, y: int, width: int, height: int, title: str) -> Fl_SliderPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_widget.vp — cfl_widget.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_widget.vp — extern FFI declarations for cfl_widget.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Widget (widget_declare) ----- # ----- Fl_Widget (widget_declare) -----
extern def Fl_Widget_new(x: int, y: int, width: int, height: int, title: str) -> Fl_WidgetPtr extern def Fl_Widget_new(x: int, y: int, width: int, height: int, title: str) -> Fl_WidgetPtr
+4 -4
View File
@@ -2,12 +2,12 @@ import t, c
from fltk._types import * from fltk._types import *
# ============================================================ # ============================================================
# fltk/_cfl_window.vp — cfl_window.h 的 extern FFI 声明(对接 cfltk C API) # fltk/_cfl_window.vp — extern FFI declarations for cfl_window.h (cfltk C API)
# ============================================================ # ============================================================
# 所有函数直接对应 cfltk 生成的 C 符号(extern "C"),链接目标: cfltk2。 # Every function maps directly to a cfltk-generated C symbol (extern "C"); link target: cfltk2.
# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str; # Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;
# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。 # function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).
# ----- Fl_Window (widget_declare) ----- # ----- Fl_Window (widget_declare) -----
extern def Fl_Window_new(x: int, y: int, width: int, height: int, title: str) -> Fl_WindowPtr extern def Fl_Window_new(x: int, y: int, width: int, height: int, title: str) -> Fl_WindowPtr
+1 -1
View File
@@ -1,7 +1,7 @@
import t, c import t, c
# ============================================================ # ============================================================
# fltk/_enums.vp — cfltk 枚举常量(t.CInt) # fltk/_enums.vp — cfltk enum constants (t.CInt)
# ============================================================ # ============================================================
Fl_BrowserType_Normal: int = 0 Fl_BrowserType_Normal: int = 0
+3 -3
View File
@@ -1,11 +1,11 @@
import t, c import t, c
# ============================================================ # ============================================================
# fltk/_types.vp — FLTK 不透明对象指针 typedef(对接 cfltk C API) # fltk/_types.vp — opaque FLTK object pointer typedefs (cfltk C API)
# ============================================================ # ============================================================
# 所有 FLTK C++ 对象在 FFI 中一律按不透明指针处理,生命周期由 # Every FLTK C++ object is handled as an opaque pointer in FFI; its lifetime is owned
# cfltk/fltk 库管理(new/delete 见各 *_delete / *_free)。 # by the cfltk/fltk library (see each *_delete / *_free).
Fl_AdjusterPtr: t.CTypedef = t.CPtr Fl_AdjusterPtr: t.CTypedef = t.CPtr
Fl_Anim_GIF_ImagePtr: t.CTypedef = t.CPtr Fl_Anim_GIF_ImagePtr: t.CTypedef = t.CPtr
+57 -57
View File
@@ -1,20 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
fltk/gen_ffi.py — cfltk (FLTK C API) → Viper extern FFI 生成器 fltk/gen_ffi.py - cfltk (FLTK C API) -> Viper extern FFI generator
从 cfltk/include/cfltk2/*.h 读取全部 C 声明: Reads all C declarations from cfltk/include/cfltk2/*.h:
* 解析所有 `#define XXX_DECLARE(param) ...` 宏定义(WIDGET/GROUP/WINDOW/ * Parse all `#define XXX_DECLARE(param) ...` macro definitions (WIDGET/GROUP/WINDOW/
BUTTON/INPUT/BROWSER/IMAGE/TABLE/DISPLAY/VALUATOR/MENU), BUTTON/INPUT/BROWSER/IMAGE/TABLE/DISPLAY/VALUATOR/MENU),
* 按每个头文件里的 `XXX_DECLARE(Class)` 调用展开, * Expand each `XXX_DECLARE(Class)` call in every header,
* 连同头文件中的独立函数原型一起转成 extern def。 * Convert them, along with the standalone function prototypes in the headers, into extern def.
生成 includes/fltk 下的 .vp FFI 文件: Generates the .vp FFI files under includes/fltk:
_types.vp 不透明指针 typedef(每个 Fl_* 类一个 Fl_*Ptr) _types.vp opaque pointer typedefs (one Fl_*Ptr per Fl_* class)
_enums.vp cfl_enums.h 的全部枚举常量 _enums.vp all enum constants of cfl_enums.h
_cfl.vp 全局 Fl 函数(cfl.h) _cfl.vp global Fl functions (cfl.h)
_cfl_<header>.vp 各头文件对应 extern def _cfl_<header>.vp extern def per header file
用法: python includes/fltk/gen_ffi.py Usage: python includes/fltk/gen_ffi.py
""" """
import os import os
import re import re
@@ -23,15 +23,15 @@ HERE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(HERE, "cfltk", "include", "cfltk2") SRC = os.path.join(HERE, "cfltk", "include", "cfltk2")
# ============================================================ # ============================================================
# C → Viper 类型映射 # C -> Viper type mapping
# ============================================================ # ============================================================
# 函数指针类型(Viper 无法表达,一律映射为不透明 bytes) # Function pointer types (not expressible in Viper; always mapped to opaque bytes)
CALLBACK_TYPES = { CALLBACK_TYPES = {
"Fl_Callback", "custom_handler_callback", "custom_draw_callback", "Fl_Callback", "custom_handler_callback", "custom_draw_callback",
"Fl_Awake_Handler", "Fl_Text_Modify_Cb", "Fl_Text_Predelete_Cb", "Fl_Awake_Handler", "Fl_Text_Modify_Cb", "Fl_Text_Predelete_Cb",
} }
# Viper/Python 关键字不能作参数名(C 里 `int Fl_compose(int *del)` 之类) # Viper/Python keywords cannot be used as parameter names (e.g. C `int Fl_compose(int *del)`)
_KEYWORDS = { _KEYWORDS = {
"def", "class", "from", "import", "global", "nonlocal", "pass", "lambda", "def", "class", "from", "import", "global", "nonlocal", "pass", "lambda",
"del", "in", "is", "and", "or", "not", "for", "while", "if", "elif", "del", "in", "is", "and", "or", "not", "for", "while", "if", "elif",
@@ -40,8 +40,8 @@ _KEYWORDS = {
"int", "str", "float", "bool", "len", "int", "str", "float", "bool", "len",
} }
# C 类型关键字:作无名参数末尾单词时属于类型的一部分而非参数名 # C type keywords: when the last word of an unnamed parameter, they belong to the type, not the name
# (`unsigned int` → 末尾 int 不是参数名;`Fl_color_average(unsigned int, ...)`)。 # (`unsigned int` -> trailing int is not the name; `Fl_color_average(unsigned int, ...)`).
_C_TYPE_WORDS = { _C_TYPE_WORDS = {
"int", "char", "short", "long", "unsigned", "signed", "float", "double", "int", "char", "short", "long", "unsigned", "signed", "float", "double",
"void", "size_t", "uintptr_t", "void", "size_t", "uintptr_t",
@@ -49,21 +49,21 @@ _C_TYPE_WORDS = {
def map_type(t: str) -> str | None: def map_type(t: str) -> str | None:
"""返回 Viper 注解类型;void 返回 None。""" """Return the Viper annotation type; returns None for void."""
t = t.strip() t = t.strip()
t = re.sub(r"\bconst\b", "", t).strip() t = re.sub(r"\bconst\b", "", t).strip()
t = re.sub(r"\bstruct\b", "", t).strip() t = re.sub(r"\bstruct\b", "", t).strip()
t = re.sub(r"\s+", " ", t) t = re.sub(r"\s+", " ", t)
if t in CALLBACK_TYPES: if t in CALLBACK_TYPES:
return "bytes" return "bytes"
if "(" in t: # 函数指针(未命名的 typedef 形态) if "(" in t: # function pointer (unnamed typedef form)
return "bytes" return "bytes"
if t.endswith("[]"): if t.endswith("[]"):
t = t[:-2].strip() + "*" t = t[:-2].strip() + "*"
star_cnt = t.count("*") star_cnt = t.count("*")
if star_cnt: if star_cnt:
base = t.replace("*", "").strip() base = t.replace("*", "").strip()
if star_cnt >= 2: # 指针的指针(int** 等)一律按不透明指针 if star_cnt >= 2: # pointer-to-pointer (int** etc.) always treated as opaque pointer
return "bytes" return "bytes"
if base == "char": if base == "char":
return "str" return "str"
@@ -100,11 +100,11 @@ def map_type(t: str) -> str | None:
return "t.CSizeT" return "t.CSizeT"
if t.startswith("Fl_") or t.startswith("custom_"): if t.startswith("Fl_") or t.startswith("custom_"):
return t + "Ptr" return t + "Ptr"
return "bytes" # 未知类型一律按不透明指针处理 return "bytes" # unknown types always treated as opaque pointer
def split_args(args: str) -> list[tuple[str, str]]: def split_args(args: str) -> list[tuple[str, str]]:
"""拆分 C 参数串 → [(类型, 参数名)],函数指针/无名参数自动补名。""" """Split a C argument string -> [(type, name)]; function pointers / unnamed args get an auto name."""
if not args.strip(): if not args.strip():
return [] return []
parts: list[str] = [] parts: list[str] = []
@@ -125,19 +125,19 @@ def split_args(args: str) -> list[tuple[str, str]]:
out: list[tuple[str, str]] = [] out: list[tuple[str, str]] = []
for p in parts: for p in parts:
p = p.strip() p = p.strip()
if not p or p == "void": # C 的 (void) 表示无参数 if not p or p == "void": # C's (void) means no parameters
continue continue
if "(" in p and (")(" in p or "*)" in p or re.search(r"\(\s*\*", p)): if "(" in p and (")(" in p or "*)" in p or re.search(r"\(\s*\*", p)):
out.append((p, "cb%d" % len(out))) out.append((p, "cb%d" % len(out)))
continue continue
# C 参数形如 `Fl_Tree *self` / `const char *title`:'*' 紧贴参数名, # A C parameter looks like `Fl_Tree *self` / `const char *title`: the '*' sticks to the name,
# 末尾单词即为参数名,其余部分为类型 # so the last word is the name and the rest is the type
m = re.search(r"(\w+)\s*$", p) m = re.search(r"(\w+)\s*$", p)
if m: if m:
typ = p[:m.start()].strip() typ = p[:m.start()].strip()
nm = m.group(1) nm = m.group(1)
# 纯类型(无名)参数:无类型部分(`int`)或末尾单词本身是 C 类型 # Pure type (unnamed) parameter: no type part (`int`) or the last word is itself a C type
# 关键字(`unsigned int` → 末尾 int 是类型的一部分,非参数名)。 # keyword (`unsigned int` -> trailing int is part of the type, not the name).
if not typ or nm in _C_TYPE_WORDS: if not typ or nm in _C_TYPE_WORDS:
typ, nm = p, "a%d" % len(out) typ, nm = p, "a%d" % len(out)
if nm in _KEYWORDS: if nm in _KEYWORDS:
@@ -152,19 +152,19 @@ def emit_extern(name: str, params: list[tuple[str, str]], ret_t: str | None,
indent: str = "") -> str: indent: str = "") -> str:
ps = ", ".join("%s: %s" % (pn, map_type(pt)) for pt, pn in params) ps = ", ".join("%s: %s" % (pn, map_type(pt)) for pt, pn in params)
rt = map_type(ret_t) if ret_t else None rt = map_type(ret_t) if ret_t else None
if rt is None: # void / 无返回 if rt is None: # void / no return
return "%sextern def %s(%s)" % (indent, name, ps) return "%sextern def %s(%s)" % (indent, name, ps)
return "%sextern def %s(%s) -> %s" % (indent, name, ps, rt) return "%sextern def %s(%s) -> %s" % (indent, name, ps, rt)
# ============================================================ # ============================================================
# 头文件解析 # Header parsing
# ============================================================ # ============================================================
MACRO_CALL_RE = re.compile(r"(?m)^\s*(\w+_DECLARE)\((\w+)\)\s*$") MACRO_CALL_RE = re.compile(r"(?m)^\s*(\w+_DECLARE)\((\w+)\)\s*$")
def strip_macro_blocks(text: str) -> str: def strip_macro_blocks(text: str) -> str:
"""删除 #define 宏定义块(含全部反斜杠续行),逐行处理兼容 CRLF。""" """Remove #define macro definition blocks (including all backslash continuations); line-by-line to tolerate CRLF."""
out: list[str] = [] out: list[str] = []
skip = False skip = False
for line in text.split("\n"): for line in text.split("\n"):
@@ -183,19 +183,19 @@ def strip_macro_blocks(text: str) -> str:
def strip_preprocessor(text: str) -> str: def strip_preprocessor(text: str) -> str:
text = strip_macro_blocks(text) text = strip_macro_blocks(text)
text = re.sub(r"typedef[^;]*;", "", text, flags=re.S) # typedef text = re.sub(r"typedef[^;]*;", "", text, flags=re.S) # typedef
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) # 注释 text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) # comments
return text return text
def extract_protos(text: str) -> list[tuple[str, str, str]]: def extract_protos(text: str) -> list[tuple[str, str, str]]:
"""提取独立函数原型 → [(返回类型, 函数名, 参数串)]。 """Extract standalone function prototypes -> [(return type, function name, argument string)].
注意 C 的 `类型 *名字` 中 '*' 紧贴函数名、与类型之间才有空格, Note that in C `type *name` the '*' sticks to the function name and the space is before the type,
因此不能用空白分隔的 `类型 名字(` 拆分;改为取第一个 '(' 之前末尾的单词为 so it cannot be split by the whitespace-separated `type name(` form; instead take the last word
函数名,其余为返回类型。 before the first '(' as the function name and the rest as the return type.
""" """
text = strip_preprocessor(text) text = strip_preprocessor(text)
text = re.sub(r"(?m)^\s*#.*$", "", text) # 其余预处理行 text = re.sub(r"(?m)^\s*#.*$", "", text) # other preprocessor lines
text = re.sub(MACRO_CALL_RE, "", text) text = re.sub(MACRO_CALL_RE, "", text)
protos: list[tuple[str, str, str]] = [] protos: list[tuple[str, str, str]] = []
buf: list[str] = [] buf: list[str] = []
@@ -206,7 +206,7 @@ def extract_protos(text: str) -> list[tuple[str, str, str]]:
elif ch == ")": elif ch == ")":
depth -= 1 depth -= 1
if depth == 0 and ch in "{}": if depth == 0 and ch in "{}":
buf = [] # extern "C" { / 块花括号不进入原型缓冲 buf = [] # extern "C" { / block braces do not enter the prototype buffer
continue continue
if ch == ";" and depth == 0: if ch == ";" and depth == 0:
stmt = "".join(buf).strip() stmt = "".join(buf).strip()
@@ -227,11 +227,11 @@ def extract_protos(text: str) -> list[tuple[str, str, str]]:
def extract_macro_defs(text: str) -> dict: def extract_macro_defs(text: str) -> dict:
"""解析 `#define XXX_DECLARE(param)` 宏体 → 方法模板。 """Parse `#define XXX_DECLARE(param)` macro bodies -> method templates.
返回 {宏名: (param, [(方法后缀, 参数串, 返回类型, 'SELF'|'PARAMS'), ...])} Returns {macro name: (param, [(method suffix, argument string, return type, 'SELF'|'PARAMS'), ...])}
'SELF' 表示首参为该类自身指针(展开时替换为 self: <类>Ptr), 'SELF' means the first argument is the class's own pointer (replaced with self: <class>Ptr on expansion),
'PARAMS' 表示保留参数原样(new/from_dyn_ptr/super_draw 等)。 'PARAMS' means the arguments are kept as-is (new/from_dyn_ptr/super_draw etc.).
""" """
out: dict = {} out: dict = {}
lines = text.split("\n") lines = text.split("\n")
@@ -252,7 +252,7 @@ def extract_macro_defs(text: str) -> dict:
body.append(line) body.append(line)
break break
methods: list = [] methods: list = []
body_src = "\n".join(body).replace("##", "") # widget##_x → widget_x body_src = "\n".join(body).replace("##", "") # widget##_x -> widget_x
for ret, fname, args in extract_protos(body_src): for ret, fname, args in extract_protos(body_src):
fn = fname.replace("##", "") fn = fname.replace("##", "")
suffix = fn[len(param):].lstrip("_") suffix = fn[len(param):].lstrip("_")
@@ -268,7 +268,7 @@ def extract_macro_defs(text: str) -> dict:
def extract_typedefs(text: str) -> list[str]: def extract_typedefs(text: str) -> list[str]:
"""提取顶层 typedef 名称(先剥离 #define 宏体,避免误扫 widget 等宏形参)。""" """Extract top-level typedef names (strip #define macro bodies first to avoid scanning macro params like widget)."""
names = [] names = []
text = strip_macro_blocks(text) text = strip_macro_blocks(text)
for m in re.finditer(r"typedef\s+(?:struct\s+)?[\w\s\*\(\)]*?(\w+)\s*;", text): for m in re.finditer(r"typedef\s+(?:struct\s+)?[\w\s\*\(\)]*?(\w+)\s*;", text):
@@ -309,7 +309,7 @@ def extract_enums(text: str) -> list[tuple[str, int]]:
# ============================================================ # ============================================================
# 生成 # Generation
# ============================================================ # ============================================================
CLASS_PTRS: set[str] = set() CLASS_PTRS: set[str] = set()
@@ -320,7 +320,7 @@ def gen_class_methods(cls: str, param: str, methods) -> list[str]:
fname = "%s_%s" % (cls, suffix) fname = "%s_%s" % (cls, suffix)
ret_t = re.sub(r"\b%s\b" % re.escape(param), cls, ret) ret_t = re.sub(r"\b%s\b" % re.escape(param), cls, ret)
if kind == "SELF": if kind == "SELF":
rest = split_args(args)[1:] # 丢弃宏里的 self 参数 rest = split_args(args)[1:] # drop the self parameter inside the macro
ps = [(cls + " *", "self")] + rest ps = [(cls + " *", "self")] + rest
else: else:
ps = split_args(args) ps = split_args(args)
@@ -339,15 +339,15 @@ def gen_header(path: str, out_name: str, defs: dict) -> str:
"from fltk._types import *", "from fltk._types import *",
"", "",
"# ============================================================", "# ============================================================",
"# fltk/%s — %s 的 extern FFI 声明(对接 cfltk C API)" % (out_name, base), "# fltk/%s — extern FFI declarations for %s (cfltk C API)" % (out_name, base),
"# ============================================================", "# ============================================================",
"", "",
"# 所有函数直接对应 cfltk 生成的 C 符号(extern \"C\"),链接目标: cfltk2。", "# Every function maps directly to a cfltk-generated C symbol (extern \"C\"); link target: cfltk2.",
"# 类型约定: Fl_* 对象均为不透明指针 (Fl_XxxPtr);const char* → str;", "# Type conventions: Fl_* objects are opaque pointers (Fl_XxxPtr); const char* -> str;",
"# 函数指针/void* → bytes(Viper 端传 0/NULL 或不使用)。", "# function pointers / void* -> bytes (pass 0/NULL or leave unused on the Viper side).",
"", "",
] ]
# 按头文件中宏调用的顺序展开 # expand in the order of macro calls in the header
for mm in MACRO_CALL_RE.finditer(text): for mm in MACRO_CALL_RE.finditer(text):
kind, cls = mm.group(1), mm.group(2) kind, cls = mm.group(1), mm.group(2)
if kind in defs: if kind in defs:
@@ -355,7 +355,7 @@ def gen_header(path: str, out_name: str, defs: dict) -> str:
lines.append("# ----- %s (%s) -----" % (cls, kind.lower())) lines.append("# ----- %s (%s) -----" % (cls, kind.lower()))
lines.extend(gen_class_methods(cls, param, methods)) lines.extend(gen_class_methods(cls, param, methods))
lines.append("") lines.append("")
# 独立原型 # standalone prototypes
protos = extract_protos(text) protos = extract_protos(text)
for ret, name, args in protos: for ret, name, args in protos:
lines.append(emit_extern(name, split_args(args), ret)) lines.append(emit_extern(name, split_args(args), ret))
@@ -366,7 +366,7 @@ def main() -> None:
os.makedirs(HERE, exist_ok=True) os.makedirs(HERE, exist_ok=True)
headers = sorted(f for f in os.listdir(SRC) if f.endswith(".h")) headers = sorted(f for f in os.listdir(SRC) if f.endswith(".h"))
# 1. 收集全部宏定义 + 类指针 typedef # 1. Collect all macro definitions + class pointer typedefs
defs: dict = {} defs: dict = {}
for h in headers: for h in headers:
with open(os.path.join(SRC, h), "r", encoding="utf-8", with open(os.path.join(SRC, h), "r", encoding="utf-8",
@@ -395,11 +395,11 @@ def main() -> None:
"import t, c", "import t, c",
"", "",
"# ============================================================", "# ============================================================",
"# fltk/_types.vp — FLTK 不透明对象指针 typedef(对接 cfltk C API)", "# fltk/_types.vp — opaque FLTK object pointer typedefs (cfltk C API)",
"# ============================================================", "# ============================================================",
"", "",
"# 所有 FLTK C++ 对象在 FFI 中一律按不透明指针处理,生命周期由", "# Every FLTK C++ object is handled as an opaque pointer in FFI; its lifetime is owned",
"# cfltk/fltk 库管理(new/delete 见各 *_delete / *_free)。", "# by the cfltk/fltk library (see each *_delete / *_free).",
"", "",
] ]
for p in sorted_ptrs: for p in sorted_ptrs:
@@ -416,7 +416,7 @@ def main() -> None:
"import t, c", "import t, c",
"", "",
"# ============================================================", "# ============================================================",
"# fltk/_enums.vp — cfltk 枚举常量(t.CInt)", "# fltk/_enums.vp — cfltk enum constants (t.CInt)",
"# ============================================================", "# ============================================================",
"", "",
] ]
@@ -425,7 +425,7 @@ def main() -> None:
with open(os.path.join(HERE, "_enums.vp"), "w", encoding="utf-8") as f: with open(os.path.join(HERE, "_enums.vp"), "w", encoding="utf-8") as f:
f.write("\n".join(enum_lines) + "\n") f.write("\n".join(enum_lines) + "\n")
# 4. 各头文件 # 4. Per header file
for h in headers: for h in headers:
if h in ("cfl_macros.h", "cfl_enums.h"): if h in ("cfl_macros.h", "cfl_enums.h"):
continue continue
+7 -7
View File
@@ -1,18 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ============================================================ # ============================================================
# gzip - 单文件 gzip 解码器(RFC 1952 + RFC 1951 deflate) # gzip - single-file gzip decoder (RFC 1952 + RFC 1951 deflate)
# #
# OOP 范式对齐 zipfile: 数据由 GzipFile 对象持有并负责释放; # OOP paradigm aligned with zipfile: data is held and freed by the GzipFile object;
# 内部位流/Huffman 助手为模块级私有函数。 # internal bit-stream/Huffman helpers are module-level private functions.
# #
# 用法: # Usage:
# gz: rawptr[gzip.GzipFile] = gzip.GzipFile("train-images-idx3-ubyte.gz") # gz: rawptr[gzip.GzipFile] = gzip.GzipFile("train-images-idx3-ubyte.gz")
# if gz is None or gz.ok == 0: ... # if gz is None or gz.ok == 0: ...
# buf: bytes = gz.read_all() # 内部 malloc, read_all 返回即解压完成 # buf: bytes = gz.read_all() # internally malloc'd, decompression is complete when read_all returns
# n: sizet = gz.dst_len # 原始长度(由对象持有, close 时释放) # n: sizet = gz.dst_len # original length (held by the object, freed on close)
# gz.close() # gz.close()
# #
# 覆盖 stored/fixed/dynamic 三种 deflate 块; 不解 CRC。 # Covers the three deflate block types (stored/fixed/dynamic); does not decode CRC.
# ============================================================ # ============================================================
import t, c import t, c
import stdlib import stdlib
+1 -1
View File
@@ -5,7 +5,7 @@ import string
import viperio import viperio
# ============================================================ # ============================================================
# JSON 值类型枚举 # JSON value type enum
# ============================================================ # ============================================================
define JSON_NULL = 0 define JSON_NULL = 0
define JSON_BOOL = 1 define JSON_BOOL = 1
+10 -10
View File
@@ -10,16 +10,16 @@ from . import (
# ============================================================ # ============================================================
# 容量约定(cap 只由调用方显式传入,签名里没有默认值) # Capacity contract (cap is passed explicitly only by the caller; there is no default in the signature)
# write —— 三参入口,固定用 WRITE_CAP_DEFAULT(4096)作容量。 # write -- three-arg entry point; always uses WRITE_CAP_DEFAULT (4096) as the capacity.
# write_cap —— 四参入口,容量由调用方显式传入。 # write_cap -- four-arg entry point; the capacity is passed explicitly by the caller.
# cap = 输出缓冲总字节数(含结尾 NUL)。所有写入先判容量: # cap = total bytes of the output buffer (including the trailing NUL). Every write first checks capacity:
# 写 n 字节要求 pos + n <= cap - 1(末位留给 write 收尾的 '\0')。 # writing n bytes requires pos + n <= cap - 1 (the last byte is reserved for the '\0' that write appends).
# 放不下时不写,并把 pos 置为"溢出标志" cap——此后任何写入都以 # if it does not fit, nothing is written and pos is set to the "overflow marker" cap -- from then on any write returns early via
# `pos >= cap` 短路返回,最终 write 判 `end_pos >= cap` 返回 None。 # the `pos >= cap` short-circuit, and the final write returns None when `end_pos >= cap`.
# 即:内容超出容量时既不写穿缓冲,也不再继续写(绝不越界)。 # That is: when the content exceeds the capacity it neither writes through the buffer nor keeps writing (never out of bounds).
# cap 的合法区间是 [WRITE_CAP_MIN, WRITE_CAP_MAX]:越界一律返回 None, # The legal range of cap is [WRITE_CAP_MIN, WRITE_CAP_MAX]: anything out of range always returns None,
# 非法容量不进入 pool.alloc(不会按可疑值去申请内存)。 # and an illegal capacity does not enter pool.alloc (no memory is requested using a suspicious value).
# ============================================================ # ============================================================
define WRITE_CAP_MIN = 64# lower bound: shortest JSON doc (e.g. {"id":""}) incl NUL fits in 64 bytes define WRITE_CAP_MIN = 64# lower bound: shortest JSON doc (e.g. {"id":""}) incl NUL fits in 64 bytes
define WRITE_CAP_MAX = 67108864# upper bound: 64 MiB, not less than the largest output buffer a caller may request define WRITE_CAP_MAX = 67108864# upper bound: 64 MiB, not less than the largest output buffer a caller may request
+11 -11
View File
@@ -2017,19 +2017,19 @@ class MemBuddy(MemManager):
fb, self.free_count()) fb, self.free_count())
def dump_alive_orders(self): def dump_alive_orders(self):
"""OOM 归属画像:按 order 统计「记录在案且仍存活」的块,并给出整池账目。 """OOM attribution profile: per order, counts blocks that are "recorded and still alive" and gives whole-pool accounting.
数据全部来自既有状态,无需 --detection: All data comes from existing state; --detection is not required:
- `chk_stack[0, chk_depth)` 是 alloc 时压栈的块地址; - `chk_stack[0, chk_depth)` holds the block addresses pushed during alloc;
- 块头为 `(order << 1) | 1` 表示存活,偶数表示已被单独 free(跳过,与 - a block header of `(order << 1) | 1` means alive, an even value means it was separately freed (skipped, using the
reset_to / check_alive_blocks 同判据)。 same criteria as reset_to / check_alive_blocks).
块大小 = MEMBUDDY_MIN_BLOCK << order,故这份分布能直接区分 Block size = MEMBUDDY_MIN_BLOCK << order, so this distribution directly distinguishes
“大量小块” 与 “少量大块”,是定位池被谁吃满的关键画像。 "many small blocks" from "a few large blocks", and is the key profile for locating who filled the pool.
整池账目(关键判据):池内每一字节必然属于「存活块」或「空闲块」二者之一, Whole-pool accounting (key criterion): every byte in the pool necessarily belongs to either a "live block" or a "free block",
故 `usable_size - Σ存活 - Σ空闲链表` 若显著不为 0,说明有块既不在空闲链表、 so if `usable_size - sum(live) - sum(free lists)` is significantly non-zero, some blocks are neither in the free lists
也没被任何记录项持有 —— 即空闲链表被写穿后**链被静默截断**(`_fl_pop` 允许 nor held by any record entry -- i.e. after the free lists are written through, **the chain is silently truncated** (`_fl_pop` allows
next==0 与池内任意对齐地址,截断不会报错),这些块从此再也分配不到:池漏满。 next==0 and any in-pool aligned address, so truncation raises no error) and those blocks can never be allocated again: the pool leaks full.
""" """
if self.chk_stack is None or self.usable is None: if self.chk_stack is None or self.usable is None:
return return
+19 -19
View File
@@ -11,12 +11,12 @@ from .__values import Value, new_value, SSAValue
from .__function import Function, BasicBlock, block_append_text, block_set_terminated, block_get_terminated, function_move_block_to_end, _ll_name_needs_quote, block_insert_text_before_terminator from .__function import Function, BasicBlock, block_append_text, block_set_terminated, block_get_terminated, function_move_block_to_end, _ll_name_needs_quote, block_insert_text_before_terminator
# ============================================================ # ============================================================
# IRBuilder: 游标式 IR 指令发射器 # IRBuilder: cursor-style IR instruction emitter
# #
# 跟踪当前 BasicBlock,将指令文本追加到块的指令链表。 # Tracks the current BasicBlock and appends instruction text to the block's instruction list.
# 维护递增的 SSA 名计数器(%0, %1, %2, ...)。 # Maintains an incrementing SSA name counter (%0, %1, %2, ...).
# #
# 用法: # Usage:
# builder = new_builder(pool, func) # builder = new_builder(pool, func)
# builder.PositionAtEnd(entry_block) # builder.PositionAtEnd(entry_block)
# a = builder.build_alloca(i32_ty) # a = builder.build_alloca(i32_ty)
@@ -25,24 +25,24 @@ from .__function import Function, BasicBlock, block_append_text, block_set_termi
# r = builder.build_load(i32_ty, a) # r = builder.build_load(i32_ty, a)
# builder.build_ret(r) # builder.build_ret(r)
# #
# 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=40 保持一致 # Note: add @t.NoVTable to stay consistent with the hardcoded BUILDER_SIZE=40
# (方法以实例方法实现,无虚表;文件末尾的同名顶层函数为一行委托)。 # (methods are implemented as instance methods, with no vtable; the same-named top-level function at the end of the file is a one-line delegate).
# ============================================================ # ============================================================
# 指令文本缓冲尺寸 # Instruction text buffer sizing
# #
# MLIR 形态的类型文本会**内联具名结构体的完整字段体** # An MLIR-form type text **inlines the full field body of a named struct**
# (如 `!llvm.struct<"sha1.Cls", (i32, !llvm.ptr, …)>`),单个类型可达数百字节, # (e.g. `!llvm.struct<"sha1.Cls", (i32, !llvm.ptr, ...)>`), so a single type can reach hundreds of bytes,
# 远长于 LLVM 形态的 `%"sha1.Cls"` 引用(十几字节)。故指令行缓冲必须按 # far longer than the LLVM-form `%"sha1.Cls"` reference (a dozen-odd bytes). Therefore the instruction line buffer must be sized by
# "一行最多内嵌几个类型文本"给足:_LINE_CAP 覆盖 5 个类型文本 + 固定部分。 # "how many type texts can be embedded at most in one line": _LINE_CAP covers 5 type texts + the fixed part.
# 渲染侧 TypePrint/MlirTypeText 已加溢出护栏:一旦截断即响亮报错,不再静默 # The rendering side (TypePrint/MlirTypeText) already has an overflow guard: any truncation errors loudly instead of silently
# 产出畸形 MLIR(历史故障:alloca 行类型被 128 字节缓冲截断 → mlir-opt 报 # producing malformed MLIR (historical failure: the alloca line type was truncated by a 128-byte buffer -> mlir-opt reported
# "unbalanced '(' character in pretty dialect name")。 # "unbalanced '(' character in pretty dialect name").
# ============================================================ # ============================================================
# 单个类型文本的容量(builders 内嵌类型经 TypePrint 渲染为 MLIR 形态)。 # Capacity of a single type text (types embedded by the builders are rendered to MLIR form by TypePrint).
# 内联具名结构体在使用处展开**完整字段体**,类型文本可达数百字节(TransPyV # An inlined named struct expands its **full field body** at the use site, so a type text can reach hundreds of bytes (TransPyV
# 自身类名/嵌套更深)⇒ 512 偏小会触发 TypePrint 的"类型文本超出输出缓冲被 # class names / nesting are deeper) => 512 is too small and would trigger TypePrint's "type text exceeds the output buffer and is
# 截断"响亮报错。提到 2048;注意 arg_cap 由本常量推导 # truncated" loud error. Raised to 2048; note that arg_cap is derived from this constant
# ((arg_count+1)*(_TY_TEXT_CAP+32)),过高会让多参调用单次分配逼近模块池单块上限。 # ((arg_count+1)*(_TY_TEXT_CAP+32)), so a too-high value makes a single multi-arg call allocation approach the module pool's per-block limit.
_TY_TEXT_CAP: sizet = 2048 _TY_TEXT_CAP: sizet = 2048
_LINE_CAP: sizet = 2560 _LINE_CAP: sizet = 2560
+8 -8
View File
@@ -5,15 +5,15 @@ import string
import viperlib import viperlib
# ============================================================ # ============================================================
# mlirlite —— 与 llvmlite 逐名同形的 MLIR(llvm 方言) 文本发射库 # mlirlite -- an MLIR (llvm dialect) text emission library, isomorphic to llvmlite name for name
# #
# 设计原则: # Design principles:
# * 内部模型(LLVMType/Value/Function/BasicBlock/IRBuilder/Module…)与 # * The internal model (LLVMType/Value/Function/BasicBlock/IRBuilder/Module...) is
# includes/llvmlite **逐名逐签名同形**,因此 TPV 侧只需把 # **isomorphic to includes/llvmlite name for name and signature for signature**, so on the TPV side it suffices to rename
# `llvmlite.` 重命名为 `mlirlite.` 即可编译; # `llvmlite.` to `mlirlite.` in order to compile;
# * 差异只在"打印"阶段:TypePrint / ValuePrint / FunctionPrint / # * The difference lies only in the "printing" stage: TypePrint / ValuePrint / FunctionPrint /
# LLVMModulePrint 输出 MLIR 文本,而 builders 仍按原样生成中间指令文本; # LLVMModulePrint emit MLIR text, while the builders still generate the intermediate instruction text as before;
# * 名字保留 LLVMxxx(同形优先)。语义差异见 README.md。 # * Names keep the LLVMxxx form (isomorphism first). See README.md for semantic differences.
# ============================================================ # ============================================================
# Import submodules # Import submodules
+5 -5
View File
@@ -9,13 +9,13 @@ from .__types import LLVMType, TypePrint
# ============================================================ # ============================================================
# Value: SSA 值表示 # Value: SSA value representation
# #
# 表示一个 LLVM IR 值:SSA 临时值(%0/%result)或常量字面量(42/0)。 # Represents an LLVM IR value: an SSA temporary (%0/%result) or a constant literal (42/0).
# 每个值有类型指针、名字、是否常量标志。 # Each value has a type pointer, a name, and an is-constant flag.
# 继承 GSListNode[Value] 获取强类型 Next: Value|CPtr(值池链表) # Inherits GSListNode[Value] to get a strongly-typed Next: Value|CPtr (the value pool list)
# #
# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 # Note: @t.NoVTable must be added (see the Line comment in __function.py for the reason).
# ============================================================ # ============================================================
@t.NoVTable @t.NoVTable
class Value(GSListNode[Value]): class Value(GSListNode[Value]):
+9 -9
View File
@@ -9,17 +9,17 @@ from .__module import LLVMModule
# ============================================================ # ============================================================
# SSA 验证器 # SSA verifier
# #
# 与 llvmlite **逐名同形、语义相同**:验证的是**结构化内部模型 + 中间指令 # Isomorphic to llvmlite **name for name and semantically identical**: it verifies the **structured internal model + intermediate instruction
# 文本**(builders 生成的 %N 定义/使用、块终止、phi 位置), # text** (the %N definitions/uses produced by the builders, block termination, phi placement),
# 与 MLIR 渲染文本无关 —— MLIR 的合法性由 mlir-opt/mlir-translate 负责。 # independent of the rendered MLIR text -- MLIR validity is the responsibility of mlir-opt/mlir-translate.
# #
# 检查项: # Checks:
# 1. 块终止: 每个基本块必须以终止指令结尾 (ret/br/switch/unreachable) # 1. Block termination: every basic block must end with a terminator (ret/br/switch/unreachable)
# 2. SSA 唯一性: 每个 %name 只能被定义一次 # 2. SSA uniqueness: every %name may be defined only once
# 3. 未定义引用: 所有 %name 使用必须有对应定义 # 3. Undefined references: every %name use must have a matching definition
# 4. Phi 位置: phi 指令只能出现在块首(非 phi 指令之前) # 4. Phi placement: a phi instruction may appear only at the head of a block (before any non-phi instruction)
# ============================================================ # ============================================================
# Error codes # Error codes
+11 -11
View File
@@ -1,19 +1,19 @@
# ============================================================ # ============================================================
# openssl — Viper 的 OpenSSL 3.x FFI 绑定包 # openssl — Viper FFI binding package for OpenSSL 3.x
# #
# 用户用法: # Usage:
# import openssl # import openssl
# v = openssl.OpenSSL_version(openssl.OpenSslVersion) # 版本文本 # v = openssl.OpenSSL_version(openssl.OpenSslVersion) # version text
# openssl.OPENSSL_init_ssl(0, None) # openssl.OPENSSL_init_ssl(0, None)
# #
# 说明: # Notes:
# * 全部为 extern FFI(extern def,直连 libcrypto/libssl 导出符号), # * Everything is extern FFI (extern def, directly bound to libcrypto/libssl
# 无 C++ 桥接层;对象均为不透明指针(openssl.types 里 *Ptr typedef)。 # exported symbols), with no C++ bridge layer; objects are all opaque pointers (the *Ptr typedefs in openssl.types).
# * 头文件按 openssl 官方头分组:crypto/err/rand/sha/md5/hmac/objects/ # * Headers are grouped by the official openssl headers: crypto/err/rand/sha/md5/hmac/objects/
# evp/bio/pem/x509/ssl。 # evp/bio/pem/x509/ssl.
# * 返回指针的函数返回 *Ptr,拿到对象后按需调用对应 *_free/SSL_free 等释放。 # * Functions that return pointers return *Ptr; after acquiring an object, call the corresponding *_free/SSL_free etc. to release it as needed.
# * 密码回调类参数一律 bytes 占位,不使用时传 t.CPtr(0)/None。 # * Password-callback parameters are always placeholder bytes; pass t.CPtr(0)/None when unused.
# * 链接库:libcrypto + libssl(Windows 上 mingw 对应 -lcrypto -lssl)。 # * Linked libraries: libcrypto + libssl (on Windows with mingw, this means -lcrypto -lssl).
# ============================================================ # ============================================================
import t import t
+3 -3
View File
@@ -2,10 +2,10 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/bio.vp — BIO(内存 / 文件 / Socket / Connect 抽象 IO) # openssl/bio.vp — BIO (memory / file / Socket / Connect abstract IO)
# 对应头文件: bio.h # Corresponding header: bio.h
# #
# 含 BIO_ADDR/BIO_ADDRINFO:主机名解析(getaddrinfo 封装)+ TCP 连接/读写。 # Includes BIO_ADDR/BIO_ADDRINFO: hostname resolution (getaddrinfo wrapper) + TCP connect/read/write.
# ============================================================ # ============================================================
# ---- BIO_METHOD accessors ---- # ---- BIO_METHOD accessors ----
+2 -2
View File
@@ -2,8 +2,8 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/crypto.vp — OpenSSL 全局初始化 / 版本 / 内存工具 # openssl/crypto.vp — OpenSSL global initialization / version / memory utilities
# 对应头文件: crypto.h / opensslv.h # Corresponding headers: crypto.h / opensslv.h
# ============================================================ # ============================================================
# ---- Version query types (type arg of OpenSSL_version(type)) ---- # ---- Version query types (type arg of OpenSSL_version(type)) ----
+2 -2
View File
@@ -2,8 +2,8 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/err.vp — OpenSSL 错误队列 # openssl/err.vp — OpenSSL error queue
# 对应头文件: err.h # Corresponding header: err.h
# ============================================================ # ============================================================
# unsigned long ERR_get_error(void) -- pop queue head and return error code # unsigned long ERR_get_error(void) -- pop queue head and return error code
+4 -4
View File
@@ -2,11 +2,11 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/evp.vp — EVP 高层密码 API # openssl/evp.vp — EVP high-level crypto API
# 对应头文件: evp.h # Corresponding header: evp.h
# #
# 包含:消息摘要(EVP_Digest*)、对称加解密(EVP_Cipher*/Encrypt*/Decrypt*)、 # Includes: message digests (EVP_Digest*), symmetric encryption/decryption (EVP_Cipher*/Encrypt*/Decrypt*),
# Base64 编解码(EVP_EncodeBlock/DecodeBlock)、非对称(EVP_PKEY* 签名/验签/加解密) # Base64 encoding/decoding (EVP_EncodeBlock/DecodeBlock), asymmetric (EVP_PKEY* sign/verify/encrypt/decrypt)
# ============================================================ # ============================================================
define EvpMaxMdSize = 64 define EvpMaxMdSize = 64
+2 -2
View File
@@ -2,8 +2,8 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/hmac.vp — HMAC(一次性 + 上下文) # openssl/hmac.vp — HMAC (one-shot + context)
# 对应头文件: hmac.h # Corresponding header: hmac.h
# ============================================================ # ============================================================
# ---- One-shot HMAC ---- # ---- One-shot HMAC ----
+2 -2
View File
@@ -2,8 +2,8 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/md5.vp — MD5 一次性摘要(兼容用,非密码学安全) # openssl/md5.vp — MD5 one-shot digest (for compatibility, not cryptographically secure)
# 对应头文件: md5.h # Corresponding header: md5.h
# ============================================================ # ============================================================
define Md5DigestLength = 16 define Md5DigestLength = 16
+2 -2
View File
@@ -2,8 +2,8 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/objects.vp — ASN.1 对象(OID)与 NID # openssl/objects.vp — ASN.1 objects (OID) and NID
# 对应头文件: objects.h / obj_mac.h # Corresponding headers: objects.h / obj_mac.h
# ============================================================ # ============================================================
# ---- Common NIDs (obj_mac.h constants) ---- # ---- Common NIDs (obj_mac.h constants) ----
+4 -4
View File
@@ -2,11 +2,11 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/pem.vp — PEM 编解码 # openssl/pem.vp — PEM encoding/decoding
# 对应头文件: pem.h # Corresponding header: pem.h
# #
# 全部走 BIO 输入(PEM_read_bio_*);证书/密钥的 x 出参可为 NULL # Everything takes BIO input (PEM_read_bio_*); the x out-parameter for certificates/keys may be NULL
# (函数返回新对象)。回调 cb 用 bytes(Viper 传 t.CPtr(0))。 # (the function returns a new object). Callback cb uses bytes (Viper passes t.CPtr(0)).
# ============================================================ # ============================================================
# ---- X509 certificate ---- # ---- X509 certificate ----
+2 -2
View File
@@ -2,8 +2,8 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/rand.vp — 安全随机数 # openssl/rand.vp — secure random numbers
# 对应头文件: rand.h # Corresponding header: rand.h
# ============================================================ # ============================================================
# int RAND_bytes(unsigned char *buf, int num) -- cryptographically secure random # int RAND_bytes(unsigned char *buf, int num) -- cryptographically secure random
+4 -4
View File
@@ -2,11 +2,11 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/sha.vp — SHA-1 / SHA-2 一次性摘要 # openssl/sha.vp — SHA-1 / SHA-2 one-shot digests
# 对应头文件: sha.h # Corresponding header: sha.h
# #
# OpenSSL 3.x 推荐走 EVP(见 evp.vp: EVP_DigestInit_ex/Update/Final_ex), # OpenSSL 3.x recommends using EVP (see evp.vp: EVP_DigestInit_ex/Update/Final_ex);
# 这里保留传统一次性接口,方便快速算摘要。 # the legacy one-shot interfaces are kept here for quick digest computation.
# ============================================================ # ============================================================
define ShaDigestLength = 20 define ShaDigestLength = 20
+6 -6
View File
@@ -2,17 +2,17 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/ssl.vp — SSL/TLS 连接层(握手 + SocketIO + 校验证书) # openssl/ssl.vp — SSL/TLS connection layer (handshake + SocketIO + certificate verification)
# 对应头文件: ssl.h # Corresponding header: ssl.h
# #
# 典型客户端流程: # Typical client flow:
# OPENSSL_init_ssl(...) # OPENSSL_init_ssl(...)
# ctx = SSL_CTX_new(TLS_client_method()) # ctx = SSL_CTX_new(TLS_client_method())
# SSL_CTX_set_verify(ctx, SslVerifyPeer, 0) # 校验对端 # SSL_CTX_set_verify(ctx, SslVerifyPeer, 0) # verify peer
# SSL_CTX_load_verify_locations(ctx, cafile, None) # SSL_CTX_load_verify_locations(ctx, cafile, None)
# ssl = SSL_new(ctx) # ssl = SSL_new(ctx)
# SSL_set_fd(ssl, fd) # 底层已连好的 TCP socket # SSL_set_fd(ssl, fd) # underlying TCP socket already connected
# SSL_connect(ssl) # 握手 # SSL_connect(ssl) # handshake
# SSL_write/SSL_read # SSL_write/SSL_read
# ============================================================ # ============================================================
+9 -9
View File
@@ -1,17 +1,17 @@
import t, c import t, c
# ============================================================ # ============================================================
# openssl/types.vp — OpenSSL 3.x 不透明对象句柄 typedef # openssl/types.vp — opaque object handle typedefs for OpenSSL 3.x
# #
# 对接 libcrypto / libssl(OpenSSL 3.x 导出符号)。 # Interfaces with libcrypto / libssl (OpenSSL 3.x exported symbols).
# 对象一律按不透明指针 (void*) 处理,生命周期由 OpenSSL 库管理 # Objects are always treated as opaque pointers (void*); their lifetime is
# (X509_free/SSL_free/BIO_free/CRYPTO_free 等对应释放函数)。 # managed by the OpenSSL library (corresponding X509_free/SSL_free/BIO_free/CRYPTO_free etc. release functions).
# 链接目标:ViperOpenSSLProject 的 project.vpj 链接 openssl_358_C # Link target: project.vpj of ViperOpenSSLProject links openssl_358_C
# 自编译静态库(libcrypto.a / libssl.a)。 # self-compiled static libraries (libcrypto.a / libssl.a).
# #
# 注意:typedef 用 `t.CPtr` 表达(与 vqt6 一致),生成 i8* # Note: typedefs are expressed with `t.CPtr` (consistent with vqt6), which
# 指针参数,可被 ASan 插桩;纯 `t.CPtr` 会生成 opaque struct 参数, # generates i8* pointer parameters that can be instrumented by ASan; a plain `t.CPtr` generates opaque struct parameters,
# opt -passes=asan 会报 invalid type for function argument。 # and opt -passes=asan reports invalid type for function argument.
# ============================================================ # ============================================================
# ---- Base contexts ---- # ---- Base contexts ----
+3 -3
View File
@@ -2,10 +2,10 @@ import t, c
from openssl.types import * from openssl.types import *
# ============================================================ # ============================================================
# openssl/x509.vp — X509 证书解析 / 校验 + 必要 ASN.1 助手 # openssl/x509.vp — X509 certificate parsing / verification + required ASN.1 helpers
# 对应头文件: x509.h / x509_vfy.h / asn1.h # Corresponding headers: x509.h / x509_vfy.h / asn1.h
# #
# 证书网络解析场景:读取 subject/issuer/SAN/有效期/公钥、链式校验。 # Certificate network-parsing scenario: read subject/issuer/SAN/validity/public key, chain verification.
# ============================================================ # ============================================================
define X509VOk = 0 define X509VOk = 0
+4 -4
View File
@@ -9,11 +9,11 @@ import zlib.pyzlib as pyzlib
# ============================================================ # ============================================================
# requests/__init__.vp — HTTP(S) 客户端库包入口(Python requests 风格) # requests/__init__.vp - HTTP(S) client library package entry point (Python requests style)
# #
# 包结构: # Package structure:
# requests HTTP 客户端(纯 socket 实现,本文件) # requests HTTP client (pure socket implementation, this file)
# requests.https HTTPS 客户端(openssl SSL/TLS + 证书核验,见 https.vp) # requests.https HTTPS client (openssl SSL/TLS + certificate verification, see https.vp)
# #
# Usage: # Usage:
# requests._mbuddy = my_mbuddy # requests._mbuddy = my_mbuddy
+10 -10
View File
@@ -12,24 +12,24 @@ import w32.win32sync as wsync
# ============================================================ # ============================================================
# requests/download.vp — 大文件切片下载(Range + 串行/多线程) # requests/download.vp - large-file ranged download (Range + serial/multi-threaded)
# #
# 用法: # Usage:
# import requests.download as rd # import requests.download as rd
# rc: INT = rd.download("https://host/big.bin", "big.bin", threads=4, # rc: INT = rd.download("https://host/big.bin", "big.bin", threads=4,
# chunk_size=262144, verify=True, ca_file="ca.crt") # chunk_size=262144, verify=True, ca_file="ca.crt")
# if rc != 0: # if rc != 0:
# print(rd.last_error()) # print(rd.last_error())
# #
# 策略: # Strategy:
# threads == 1 串行 Range 切片(顺序拉取并写入文件) # threads == 1 serial Range chunks (fetched and written to the file in order)
# threads > 1 多线程:文件按线程数均分为若干带(band),每线程一个 # threads > 1 multi-threaded: the file is split into bands by thread count, each thread uses one
# 连接顺序下载自己带的切片,写入同一文件的对应偏移。 # connection to download its band's chunks in order, writing at the corresponding offsets of the same file.
# #
# 说明: # Notes:
# - 通过 Content-Range 探测总大小与服务器 Range 支持; # - probes total size and server Range support via Content-Range;
# - http:// 走 requests(纯 socket),https:// 走 requests.https(openssl); # - http:// goes through requests (pure socket), https:// goes through requests.https (openssl);
# - 每片请求不带 Accept-Encoding,响应为原始字节,直接落盘。 # - each chunk request omits Accept-Encoding, the response is raw bytes and is written straight to disk.
# ============================================================ # ============================================================
define DL_MAX_THREADS = 8 define DL_MAX_THREADS = 8
+8 -8
View File
@@ -11,9 +11,9 @@ import memhub
# ============================================================ # ============================================================
# requests/https.vp — HTTPS 客户端(openssl SSL/TLS + 证书核验) # requests/https.vp - HTTPS client (openssl SSL/TLS + certificate verification)
# #
# 与 requests(HTTP)同名 API,但走 TLS 并默认校验对端证书: # Same API names as requests (HTTP), but over TLS and by default it verifies the peer certificate:
# import requests.https as rhttps # import requests.https as rhttps
# resp = rhttps.get("https://example.com/", verify=True) # resp = rhttps.get("https://example.com/", verify=True)
# if resp is not None: # if resp is not None:
@@ -21,12 +21,12 @@ import memhub
# print(resp.text) # print(resp.text)
# requests.release(resp) # requests.release(resp)
# #
# 说明: # Notes:
# - verify=True 时校验证书链 + 主机名(hostname 校验 SSL_set1_host), # - with verify=True it verifies the certificate chain + hostname (hostname check via SSL_set1_host),
# 提供 ca_file 指向 PEM CA bundle;不提供则走系统默认路径 # provide ca_file pointing to a PEM CA bundle; if not provided it uses the system default path
# (Windows 上通常为空,真实站点会因"无法验证证书链"失败返回 None)。 # (on Windows this is usually empty, so real sites fail with "unable to verify certificate chain" and return None).
# - 失败时返回 None,可用 last_error() 获取原因文本。 # - on failure returns None; use last_error() to get the reason text.
# - 握手成功后可用 last_tls_info() 查看 TLS 版本 / 加密套件 / 对端证书主题。 # - after a successful handshake, use last_tls_info() to view the TLS version / cipher suite / peer certificate subject.
# ============================================================ # ============================================================
define HTTPS_PORT = 443 define HTTPS_PORT = 443
+14 -14
View File
@@ -1,20 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ============================================================ # ============================================================
# torch-lite: 仿 PyTorch 的极简张量/神经网络库(Viper) # torch-lite: a minimal PyTorch-like tensor/neural-network library (Viper)
# #
# 设计原则: # Design principles:
# * "足够用就行": 只覆盖 MNIST 两层 MLP 训练/推理所需的原语。 # * "good enough": covers only the primitives needed to train/infer a two-layer MNIST MLP.
# * API 命名贴近 PyTorch: Tensor / linear_fwd(bias 融合) / relu / # * API names follow PyTorch closely: Tensor / linear_fwd(fused bias) / relu /
# relu_backward / softmax_crossentropy(前向+梯度一体) / sgd_step。 # relu_backward / softmax_crossentropy(forward+gradient combined) / sgd_step.
# * 张量为 2D 稠密 double (rows x cols, 行主序), 权重形状同 PyTorch: # * Tensors are 2D dense double (rows x cols, row-major), weight shapes match PyTorch:
# Linear(in, out) -> weight (out, in) / bias (out,), forward = x @ W^T + b。 # Linear(in, out) -> weight (out, in) / bias (out,), forward = x @ W^T + b.
# * 计算热点结合 vipersimd: # * Compute hotspots use vipersimd:
# - linear_fwd 内积按 k%4 用 simd_dot4d 累加(纯 AVX2 点积); # - linear_fwd dot product accumulates with simd_dot4d over k%4 (pure AVX2 dot product);
# - relu_fwd 用 simd_max4d(x, 0) 四路取大; # - relu_fwd uses simd_max4d(x, 0) for 4-wide maximum;
# - 向量乘标量用 simd_mul_scalar4d(归一化 /255 等)。 # - vector-times-scalar uses simd_mul_scalar4d (normalization /255, etc.).
# backward / softmax / sgd 等按标量写(数据量小, 可读性优先)。 # backward / softmax / sgd, etc. are written scalar-wise (small data volume, readability first).
# * 内存: 所有 Tensor 及 data/grad 都从调用方传入的 memhub.MemBuddy # * Memory: all Tensors and their data/grad are allocated from a caller-provided memhub.MemBuddy
# 分配(与 includes 既有约定一致, pool.alloc 免手动 free)。 # (consistent with the existing includes convention, pool.alloc avoids manual free).
# ============================================================ # ============================================================
from stdint import * from stdint import *
import t, c import t, c
+352 -352
View File
@@ -10,420 +10,420 @@ import w32.win32process as wp
import w32.win32memory as wm import w32.win32memory as wm
import w32.win32dbghelp as dbg import w32.win32dbghelp as dbg
# 转储重入保护(见 DumpMainThread)**不使用 InterlockedCompareExchange**:该名字在 x64 上是 # Dump reentrancy protection (see DumpMainThread) does **NOT use InterlockedCompareExchange**: on x64 that
# winnt.h 的编译器内部函数(映射到 lock cmpxchg 指令),**不是可链接符号**,直接引用会在链接期 # name is a winnt.h compiler intrinsic (mapped to the lock cmpxchg instruction), **not a linkable symbol**, and
# 报 undefined symbol(实测:ld.lld: undefined symbol: InterlockedCompareExchange)。 # referring to it directly fails at link time with an undefined symbol (observed: ld.lld: undefined symbol: InterlockedCompareExchange).
# 本模块改用普通 32 位标志做「检查并占用」:并发回调最多导致一次重复转储(诊断工具可接受), # This module instead uses a plain 32-bit flag for test-and-set: concurrent callbacks cause at most one duplicate
# 且不引入任何链接期依赖。 # dump (acceptable for a diagnostic facility) and introduce no link-time dependency.
# ============================================================================= # =============================================================================
# traceback — 运行时「按键取栈」模块(Ctrl+C / Ctrl+Break 时转储主线程调用栈) # traceback -- runtime "press-key-to-capture-stack" module (dumps the main-thread call stack on Ctrl+C / Ctrl+Break)
# #
# 解决的问题:编译器(或任何长任务)卡死时,用户按一个键就能拿到「现在卡在哪一行」 # Problem solved: when the compiler (or any long task) hangs, one keypress gives the user "where it is stuck right now"
# 的完整调用栈——从 CRT 入口(mainCRTStartup / __tmainCRTStartup)到当前帧。 # as a complete call stack -- from the CRT entry (mainCRTStartup / __tmainCRTStartup) down to the current frame.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 一、触发语义(SetConsoleCtrlHandler 回调) # 1. Trigger semantics (SetConsoleCtrlHandler callback)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Ctrl+C (CTRL_C_EVENT) → 打印调用栈后**返回 FALSE**(交还系统默认处理 ⇒ 进程终止) # Ctrl+C (CTRL_C_EVENT) -> prints the call stack, then **returns FALSE** (back to default handling => process terminates)
# Ctrl+Break (CTRL_BREAK_EVENT)→ 打印调用栈后**返回 TRUE**(已处理 ⇒ 进程继续运行) # Ctrl+Break (CTRL_BREAK_EVENT) -> prints the call stack, then **returns TRUE** (handled => process keeps running)
# 其它控制事件(关窗/注销/关机,CTRL_CLOSE_EVENT 等)→ 一律返回 FALSE,不转储、不干预。 # Other control events (close/logoff/shutdown, CTRL_CLOSE_EVENT, etc.) -> always return FALSE, no dump, no intervention.
# **崩溃**(未处理异常,经 SetUnhandledExceptionFilter 捕获)→ 在**出错线程**上打印调用栈后 # **Crash** (unhandled exception, caught via SetUnhandledExceptionFilter) -> prints the call stack on the **faulting
# 返回 EXCEPTION_CONTINUE_SEARCH(异常交还系统 ⇒ 退出码 / WER / ASan 报告行为原样保留), # thread**, then returns EXCEPTION_CONTINUE_SEARCH (exception handed back to the system => exit code / WER / ASan report
# 见第十四节。 # behavior is preserved); see section 14.
# 回调由系统在**新建的线程**上执行,不是主线程——所以「当前执行到哪」必须靠 # The callback runs on a **newly created thread**, not the main thread -- so "where execution currently is" must come
# SuspendThread(主线程) + GetThreadContext 取得主线程寄存器,再回溯主线程的栈。 # from SuspendThread(main thread) + GetThreadContext to obtain the main-thread registers, then walking its stack.
# 同一时刻**只允许一次转储**:连续按键会由系统在多个新线程上并发回调,而它们共用 # **Only one dump at a time** is allowed: repeated keypresses make the system invoke the callback concurrently on
# 同一个 LineBuf 与输出句柄 ⇒ 输出互相撕裂(实测出现过"降级:"行重复且交织)。 # several new threads, and they share the same LineBuf and output handle => output is torn apart (observed: duplicated,
# 故 DumpMainThread 入口用内联汇编 `lock cmpxchg`(AtomicTryEnterDump)对 _DumpLock # interleaved "degrade:" lines). So DumpMainThread's entry uses one inline-asm `lock cmpxchg` (AtomicTryEnterDump) to
# 做原子"检查并占用":抢占失败的调用直接返回(不挂起主线程、不输出任何内容)。 # test-and-set _DumpLock atomically: a call that loses the race returns immediately (does not suspend the main
# 每次转储还会打印自增 Seq 与回调线程 id:若两条转储记录的 Seq 不同 ⇒ 顺序两次; # thread, outputs nothing). Each dump also prints an auto-incrementing Seq and the callback thread id: two dump records
# 同一 Seq 的行却互相截断 ⇒ 说明仍存在共享缓冲的并发写(据此定位,不再靠猜)。 # with different Seqs => two sequential dumps; lines with the same Seq that truncate each other => concurrent writes to the shared buffer still exist (locate by this, not by guessing).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 二、安装时机与总开关(本模块的开关落地点) # 2. Install timing and master switch (where this module's switch lands)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 约定:**旁文件存在** ⟺ 编译时用了 TPC 的 `--traceback`。 # Convention: **the sidecar file exists** <=> the build used TPC's `--traceback`.
# Install() 先求 <当前 exe 完整路径> + ".stackmap";该文件不存在(CreateFileA 报 # Install() first forms <current exe full path> + ".stackmap"; if that file is absent (CreateFileA reports
# ERROR_FILE_NOT_FOUND)则**立刻返回 0,零副作用**(不分配、不注册 handler); # ERROR_FILE_NOT_FOUND) it **returns 0 immediately with zero side effects** (no allocation, no handler registered);
# 存在才注册 SetConsoleCtrlHandler(并挂上未处理异常过滤器,见第十四节)。故 TPC 侧无需 # only when present does it register SetConsoleCtrlHandler (and attach the unhandled-exception filter, see
# 生成任何其它开关代码,只需在启用该选项时让产物链接 dbghelp(见第五节)并调用一次 # section 14). So the TPC side needs no other switch code: it only has to make the artifact link dbghelp (see section 5)
# Install()(见第六节)。 # and call Install() once (see section 6) when the option is enabled.
# 文件存在但其头部/记录区非法时:打印可定位的警告并把映射标记为不可用——调用栈仍 # If the file exists but its header/record area is invalid: print a locatable warning and mark the mapping unusable -- the
# 照常转储(只有地址),不会静默退出(诊断工具取不到信息时给降级信息,不掩盖错误)。 # call stack is still dumped (addresses only); it never exits silently (a diagnostic gives degraded info instead of hiding errors).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 三、旁文件(<exe>.stackmap)格式 —— 小端 # 3. Sidecar (<exe>.stackmap) format -- little-endian
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 头部 20 字节:magic="TPCS"(4B) + u32 version=1 + u32 entry_count + u32 str_count # Header 20 bytes: magic="TPCS"(4B) + u32 version=1 + u32 entry_count + u32 str_count
# + u32 str_bytes # + u32 str_bytes
# 记录区 entry_count × 20B(**按 rva 升序**):u64 rva; u32 file_off; u32 line; u32 func_off # Record area entry_count x 20B (**sorted by ascending rva**): u64 rva; u32 file_off; u32 line; u32 func_off
# 字符串池 str_bytes 字节:\0 结尾 UTF-8 串;file_off/func_off 是池内偏移 # String pool str_bytes bytes: NUL-terminated UTF-8 strings; file_off/func_off are pool offsets
# (func_off = 0 表示无函数名) # (func_off = 0 means no function name)
# 运行时:GetModuleHandleA(NULL) 取映像基址,帧地址 - 基址 = rva,再二分查找记录区。 # Runtime: GetModuleHandleA(NULL) gets the image base, frame address - base = rva, then binary-search the record area.
# 命中 → 帧行 " File \"<文件>\", line <行>, in <函数名>",其下再打一行 4 空格缩进的源码 # Hit -> frame line " File \"<file>\", line <line>, in <function>", followed by one more line of 4-space-indented source
# (运行期重开该文件读第 <行> 行;读不到就整行不打,见第十三节); # (at runtime the file is reopened and line <line> is read; if it cannot be read the whole line is omitted, see section 13);
# 未命中 → " File \"<0x<地址> +0x<rva> no source line mapping>\", line 0, in <unknown>"; # Miss -> " File \"<0x<address> +0x<rva> no source line mapping>\", line 0, in <unknown>";
# 地址落在主模块之外(更低,典型为 DLL/系统帧)→ " File \"<0x<地址> external>\", line 0, in <unknown>"。 # Address below the main module (typically a DLL/system frame) -> " File \"<0x<address> external>\", line 0, in <unknown>".
# 三种形态都把地址/RVA 保留在同一行(见第十三节)。 # All three forms keep the address/RVA on the same line (see section 13).
# 装载时做一次全表校验(rva 严格升序、字符串偏移落在池内),不合法即禁用映射—— # Load time does one full-table validation (rva strictly ascending, string offsets inside the pool); if invalid the mapping is disabled --
# 二分查找依赖升序,破损的表会给出**错行号**,比"只打地址"更坏。 # the binary search relies on ascending order, and a corrupt table yields **wrong line numbers**, worse than "addresses only".
# 校验失败一律**逐项打印挂在哪一项**(含头部实读的 magic/version/count/str_bytes # A validation failure always **prints which item it failed on** (including the actually-read header magic/version/count/str_bytes
# 与文件实际大小),失败原因不外泄为一句"旁文件非法"。 # and the real file size); the reason is never collapsed into a single "sidecar invalid".
# ⚠ 读字节必须逐个 & 0xFF:Viper 的 Buf[i] 是**有符号** char,>= 0x80 的字节若直接 # NOTE: each byte read must be masked with 0xFF: Viper's Buf[i] is a **signed** char, so a byte >= 0x80 cast
# 转 u32 会符号扩展(例:记录数 1000=0x3E8 的首字节 0xE8 → 0xFFFFFFE8),使头部的 # to u32 sign-extends (e.g. record count 1000=0x3E8, first byte 0xE8 -> 0xFFFFFFE8), making the header's
# entry_count/str_bytes 变成天文数字、`20+count*20+str_bytes > 文件大小` 必然成立 # entry_count/str_bytes astronomical and `20+count*20+str_bytes > file size` always true
# ⇒ 映射永远装载失败(这正是"源行映射=无"的真因,详见 ReadU32)。 # => the mapping can never load (this is the real cause of "source line mapping = none"; see ReadU32).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 四、回溯两条路径与降级条件(**本轮已主副互换,见第十一节 B**) # 4. The two walk paths and degrade conditions (**main/aux swapped this round, see section 11 B**)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# ⚠ 先读第十一节 B:本轮起**主路径 = RBP 链**(零 dbghelp 依赖),dbghelp 降为 # NOTE: read section 11 B first: from this round **the main path = the RBP chain** (zero dbghelp dependency) and dbghelp is demoted
# "可选加强"。下面这段 dbghelp 说明仍有效,只是它描述的是**加强路径**的历史细节。 # to "optional augmentation". The dbghelp notes below still hold; they describe the historical detail of the **augmentation path**.
# 加强路径(可选):dbghelp!StackWalk64(MachineType=IMAGE_FILE_MACHINE_AMD64)。 # Augmentation path (optional): dbghelp!StackWalk64 (MachineType=IMAGE_FILE_MACHINE_AMD64).
# SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_UNDNAME|SYMOPT_NO_PROMPTS) + # SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_UNDNAME|SYMOPT_NO_PROMPTS) +
# SymInitialize(GetCurrentProcess(), NULL, **TRUE**) —— TRUE 是硬要求:它让 dbghelp # SymInitialize(GetCurrentProcess(), NULL, **TRUE**) -- TRUE is a hard requirement: it makes dbghelp
# 枚举已加载模块填进自己的模块表;置 FALSE 时模块表为空,SymFunctionTableAccess64 # enumerate loaded modules into its own module table; with FALSE the table is empty, SymFunctionTableAccess64
# 取不到 RUNTIME_FUNCTION、SymGetModuleBase64 返回 0,StackWalk64 **首次调用即失败** # cannot get a RUNTIME_FUNCTION, SymGetModuleBase64 returns 0, and StackWalk64 **fails on its very first call**
# (表现为"符号回溯=可用但 0 帧")。配合 DEFERRED_LOADS 不会真的去加载 PDB。 # (seen as "symbol walk = available but 0 frames"). Combined with DEFERRED_LOADS it does not actually load PDBs.
# 三个回调地址里 FunctionTableAccess/GetModuleBase 仍在 Install 时用 GetProcAddress # Of the three callback addresses, FunctionTableAccess/GetModuleBase are still fetched in Install via GetProcAddress
# 取(本仓既有的动态解析写法,先例 VET/App/lib/VGuard.vp:852-867):避免依赖 # (this repo's existing dynamic-resolution style; precedent VET/App/lib/VGuard.vp:852-867): this avoids relying on
# 「跨模块导入的函数名作为函数指针实参」这一未经本仓验证的用法,取不到就传 NULL。 # "a cross-module imported function name as a function-pointer argument", unverified in this repo; pass NULL if unavailable.
# ReadProcessMemory 回调改用**本模块内定义**的 ReadMemCb(同模块内"函数名作 # The ReadProcessMemory callback uses ReadMemCb **defined inside this module** (an in-module "function name as
# 函数指针实参"已由 CtrlHandler 先例验证):直接传 kernel32!ReadProcessMemory # function-pointer argument" is already proven by the CtrlHandler precedent): passing kernel32!ReadProcessMemory
# 本体时拿不到失败原因,而回调返回 0 会让 StackWalk64 立即终止(恰好是"0 帧")。 # itself gives no failure reason, and a 0 return makes StackWalk64 stop immediately (exactly the "0 frames").
# 主路径(**RBP 链手工回溯**):无条件先走它。它拿不到更多帧(<=1)时**先走第二兜底 # Main path (**manual RBP-chain walk**): always tried first. When it yields no more frames (<=1) it **first runs the second
# = 栈扫描(ScanStack,不依赖帧指针)**;再之后才轮到 dbghelp 加强(编译期开关为 1、 # fallback = stack scan (ScanStack, no frame-pointer dependency)**; only after that comes the dbghelp augmentation (compile-time switch = 1,
# Install 时 SymInitialize/SymRefreshModuleList 成功、两个回调地址与帧缓冲齐备、且 # SymInitialize/SymRefreshModuleList succeeded at Install, both callback addresses and the frame buffer are ready, and
# SymGetModuleBase64(主模块) 探针通过;**ASan 运行时存在只表示 DbgHelp 状态共享,不再 # the SymGetModuleBase64(main module) probe passes; **the presence of an ASan runtime only indicates DbgHelp state sharing, no longer
# 一票否决**);任一前提不满足都会打印明确原因(`[diag] symwalk=disabled reason=…`), # a veto**); any unsatisfied precondition prints an explicit reason (`[diag] symwalk=disabled reason=...`),
# **不静默 0 帧**。 # **never a silent 0 frames**.
# 本轮新增:每步无条件打 `[diag] rbp:` 原始读数 + 判定、以及 `walk: fallback=stack_scan` # New this round: every step unconditionally prints `[diag] rbp:` raw readings + verdict, and `walk: fallback=stack_scan`
# 扫描推断帧(`~` 前缀)——详见第十二节。 # plus scanned inferred frames (`~` prefix) -- see section 12.
# 主路径实现:打印当前 RIP,然后沿主线程 RBP 链读 [rbp](调用者 rbp)与 [rbp+8] # Main-path implementation: print the current RIP, then walk the main-thread RBP chain reading [rbp] (caller rbp) and [rbp+8]
# (返回地址)逐帧回溯;要求 rbp 8 字节对齐、严格向高地址增长(`[rbp] > rbp`)、 # (return address) frame by frame; requires rbp to be 8-byte aligned, strictly increasing towards higher addresses (`[rbp] > rbp`),
# 单步跨度 <= TRACEBACK_RBP_MAX_STEP、返回地址落在主模块基址区间内,否则立即停止 # a single-step span <= TRACEBACK_RBP_MAX_STEP, and the return address within the main-module base range, else it stops at once
# (防野指针把回溯带飞);**读内存一律经 ReadProcessMemory,绝不直接解引用野 rbp**。 # (to keep a wild pointer from flying the walk off track); **all memory reads go through ReadProcessMemory, never a direct dereference of a wild rbp**.
# RBP 链只覆盖保持帧指针的函数;帧指针一旦未保留([rbp+8] 不是返回地址)链即在第 1 # The RBP chain only covers functions that keep a frame pointer; once the frame pointer is not preserved ([rbp+8] is not the return address) the chain
# 步断——此时由 ScanStack 兜底,另叠一层 dbghelp 加强(可用时)。 # breaks at step 1 -- then ScanStack is the fallback, with a further dbghelp augmentation layer on top (when available).
# --traceback 时 TPC 给 llc 加 `-frame-pointer=all`(Phase2Translator.py:2187-2193); # With --traceback, TPC adds `-frame-pointer=all` to llc (Phase2Translator.py:2187-2193);
# 该 flag 是否真正生效见第十二节 C 的调查结论与修法落点。 # whether that flag actually takes effect is covered by the investigation result and fix location in section 12 C.
# 【取证:先取证,再决策】每次转储在 banner 之后打印一组前置诊断(**TRACEBACK_DIAG=1 时**)—— # [Evidence first: gather evidence, then decide] After the banner each dump prints a set of preflight diagnostics (**only when TRACEBACK_DIAG=1**) --
# Seq / 回调线程 id / 主线程 id / Dumping 进入前值 / MapOk / SymReady / ModInited / # Seq / callback thread id / main thread id / pre-entry Dumping value / MapOk / SymReady / ModInited /
# SymGetModuleBase64(主模块) 的**实际返回值** / 主模块基址 / 帧缓冲与两个回调地址 # the **actual return value** of SymGetModuleBase64(main module) / main-module base / whether the frame buffer and the two callback addresses
# 是否就位;进入 dbghelp 主路径后,**首帧**再打印 StackWalk64 的返回码、传入的 # are in place; after entering the dbghelp main path the **first frame** additionally prints StackWalk64's return code, the
# machine/RIP/RSP/RBP、返回的 AddrPC,以及 SymFunctionTableAccess64(proc, AddrPC) 的 # machine/RIP/RSP/RBP passed in, the returned AddrPC, and the return value of SymFunctionTableAccess64(proc, AddrPC).
# 返回值。这些行**先于任何降级判断**产出,使"0 帧"的根因可直接判定(见第九节)。 # These lines are produced **before any degrade decision**, so the root cause of "0 frames" can be judged directly (see section 9).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 五、链接期依赖(TPC 侧必须知道) # 5. Link-time dependencies (the TPC side must know this)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# dbghelp(-ldbghelp)+ kernel32(既有)。SymInitialize / StackWalk64 / SymSetOptions / # dbghelp (-ldbghelp) + kernel32 (existing). SymInitialize / StackWalk64 / SymSetOptions /
# SymGetModuleBase64 / SymFunctionTableAccess64 是**静态**引用(后两个用于首帧取证诊断), # SymGetModuleBase64 / SymFunctionTableAccess64 are **static** references (the latter two are used for first-frame evidence),
# 产物必须能解析 dbghelp 的导入库,否则链接失败。若不便新增 -ldbghelp, # so the artifact must resolve dbghelp's import library or the link fails. If adding -ldbghelp is inconvenient,
# 把 TRACEBACK_USE_DBGHELP 置 0:该常量比较会在编译期把唯一含 dbghelp 调用的分支 # set TRACEBACK_USE_DBGHELP to 0: that constant comparison folds away at compile time the only branch containing
# 整体去掉(见 TRACEBACK_USE_DBGHELP 的注释),只保留 RBP 链回溯。 # dbghelp calls (see the TRACEBACK_USE_DBGHELP comment), leaving only the RBP-chain walk.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 六、TPC 侧调用入口(符号与签名) # 6. TPC-side call entry (symbol and signature)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# import traceback # import traceback
# traceback.Install() # 返回 int:1=handler 已注册;0=未安装(无旁文件/失败) # traceback.Install() # returns int: 1 = handler registered; 0 = not installed (no sidecar/failure)
# # 注册两个:SetConsoleCtrlHandler(Ctrl+C/Break)+ # # registers two: SetConsoleCtrlHandler (Ctrl+C/Break) +
# # SetUnhandledExceptionFilter(崩溃),见第一节/第十四节 # # SetUnhandledExceptionFilter (crash), see section 1 / section 14
# 调用时机:进程启动期一次即可。两种等价做法—— # Call timing: once during process startup. Two equivalent ways --
# (a) 在入口 main 的第一条语句调用; # (a) call it as the first statement of entry main;
# (b) 注入一个 @llvm.global_ctors(.CRT$XCU 语义)构造器调用该符号(本仓先例: # (b) inject an @llvm.global_ctors (.CRT$XCU semantics) constructor that calls this symbol (repo precedent:
# TransPyC/lib/core/LLVMCG/BaseGen.py:2061-2097 用 llvm.global_ctors 注册 # TransPyC/lib/core/LLVMCG/BaseGen.py:2061-2097 registers a Ctrl+C handler via llvm.global_ctors);
# Ctrl+C 处理器)。构造器在 CRT 初始化期执行,能覆盖「main 之前就卡住」的场景。 # the constructor runs during CRT initialization, covering the "stuck before main" case.
# 本模块自身**没有**模块级构造器(状态对象由 Install 懒分配),故与 TPC 注入的 # This module itself has **no** module-level constructor (the state object is lazily allocated by Install), so it has
# 构造器不存在初始化顺序依赖,重复调用是幂等的。 # no initialization-order dependency on TPC's injected constructor, and repeated calls are idempotent.
# ⚠ 本模块把「主线程」定义为**调用 Install() 的那个线程**(Install 里 GetCurrentThreadId # NOTE: this module defines the "main thread" as **the thread that called Install()** (Install records its id
# 记录其 id,回调时挂起/回溯的就是它)。故必须在真正的入口线程调用(main 或 CRT # via GetCurrentThreadId; that is the thread suspended/walked on callback). So it must be called on the real entry
# 构造器所在线程);若在启动期的工作线程里调用,转储的将是那个线程的栈。 # thread (main, or the thread running a CRT constructor); if called on a startup worker thread, the dump is that thread's stack.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 七、为什么直接 WriteFile 而不是 printf # 7. Why WriteFile directly instead of printf
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 转储发生在「主线程已被挂起」的时刻:若主线程恰好停在 CRT 的 stdout 锁内,回调线程 # The dump happens at a moment when **the main thread is already suspended**: if the main thread happens to be stopped inside
# 再调用 printf 会**永久等待同一把锁**,把"卡死"变成"按了键彻底不动"。 # the CRT's stdout lock, the callback thread calling printf would **wait forever on the same lock**, turning "hang" into "key press and nothing moves".
# 故输出走 kernel32!WriteFile(GetStdHandle(STD_ERROR_HANDLE))(无用户态 CRT 流锁), # So output goes through kernel32!WriteFile(GetStdHandle(STD_ERROR_HANDLE)) (no user-mode CRT stream lock),
# 格式化走 CRT snprintf(只写内存缓冲,不碰 FILE 锁)。stderr 的另一好处:stdout 常被 # and formatting goes through CRT snprintf (writes only to a memory buffer, never touching FILE locks). Another benefit of stderr: stdout is often
# 管道占用(如被父工具读取),stderr 一般直达终端。句柄与全部缓冲都在 Install 时备好, # taken by a pipe (e.g. read by a parent tool) while stderr usually goes straight to the terminal. Handles and all buffers are prepared at Install,
# 回调线程**零分配**(MemBuddy 非线程安全,回调里绝不触碰池)。 # and the callback thread does **zero allocation** (MemBuddy is not thread-safe; the callback never touches the pool).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 七之二、转储**同时落文件** + 控制台纯 ASCII(本轮的两次修正) # 7b. Dump **also written to a file** + pure-ASCII console (two fixes this round)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# (a) 落文件(解决"没有 Break 键"与"控制台代码页吞字"):Install 成功时以 # (a) File output (solves "no Break key" and "console code page eats characters"): on successful Install, open an
# `<exe 完整路径> + ".stackdump.txt"` 打开一个**追加写**句柄(OPEN_ALWAYS + # **append** handle for `<exe full path> + ".stackdump.txt"` (OPEN_ALWAYS +
# FILE_APPEND_DATA,不存在则创建),EmitLine 把**同一份内容**既写控制台又追加落盘。 # FILE_APPEND_DATA, creating it if absent); EmitLine writes **the same content** both to the console and appending to disk.
# 文件是 UTF-8 字节原样,不受控制台代码页影响;每次转储在段前追加一小段分隔头 # The file is raw UTF-8 bytes, unaffected by the console code page; each dump prepends a small separator header
# (本地时间戳/pid/触发键/主线程 id/Seq,中文标签,仅文件)。文件写失败**不阻断** # (local timestamp/pid/trigger key/main thread id/Seq, Chinese labels, file only). A file write failure **does not block**
# 控制台,且只提示一次(见 EmitToFile)。Install 成功时打印一行 # the console and is reported only once (see EmitToFile). On successful Install it prints a line
# `[traceback] stack dump also written to: <路径>` 便于确认去向。 # `[traceback] stack dump also written to: <path>` to confirm the destination.
# (b) 控制台纯 ASCII:原先的运行时文本是中文(UTF-8 字节),而 cmd 的控制台输出代码页 # (b) Pure-ASCII console: the runtime text was originally Chinese (UTF-8 bytes), while cmd's console output code page
# 默认为 936(GBK)——WriteFile 到控制台会按 936 解释 UTF-8 字节,一段多字节在多次 # defaults to 936 (GBK) -- WriteFile to the console interprets UTF-8 bytes as 936, and a multi-byte sequence split/rejoined
# WriteFile 之间被拆开/重组,表现为**整行消失**(最长的"首帧"行首当其冲)或相邻行错位、 # across several WriteFile calls appears as **whole lines disappearing** (the longest "first frame" line first) or adjacent lines shifting,
# 看似"重复且残缺"(含 '�')。故**运行时输出到控制台的文本一律改英文/ASCII** # looking "duplicated and mangled" (including the U+FFFD replacement character). So **all runtime text sent to the console is changed to English/ASCII**
# (banner / [diag] / Trigger= / exe base= / walk: primary=rbp_chain / frames=N 等), # (banner / [diag] / Trigger= / exe base= / walk: primary=rbp_chain / frames=N, etc.),
# 任何代码页下都可读、可 grep;` File "<文件>", line <行>, in <函数名>` 帧行保留源路径 # readable and greppable under any code page; the ` File "<file>", line <line>, in <function>` frame line keeps the source path
# 原文(分隔符/新增说明文字一律 ASCII)。 # text as-is (separators/added explanatory text are always ASCII).
# 中文仅保留在:本文件的注释/文档串、以及**只写文件**的分隔头。 # Chinese is kept only in: this file's comments/docstrings, and the **file-only** separator header.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 八、依赖的既有声明(未改动)与本模块新增声明 # 8. Existing declarations relied on (unchanged) and declarations added by this module
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 既有(`from w32.win32base import *` 取裸名):GetModuleHandleA / GetProcAddress / # Existing (`from w32.win32base import *` brings bare names): GetModuleHandleA / GetProcAddress /
# CloseHandle / GetLastError / INVALID_HANDLE_VALUE / ERROR_FILE_NOT_FOUND / # CloseHandle / GetLastError / INVALID_HANDLE_VALUE / ERROR_FILE_NOT_FOUND /
# LARGE_INTEGER / SYSTEMTIME / GetLocalTime / HANDLE / BOOL / ULONG / VOIDPTR; # LARGE_INTEGER / SYSTEMTIME / GetLocalTime / HANDLE / BOOL / ULONG / VOIDPTR;
# wf.CreateFileA / wf.ReadFile / wf.WriteFile / wf.GetFileSizeEx / wf.GetStdHandle / # wf.CreateFileA / wf.ReadFile / wf.WriteFile / wf.GetFileSizeEx / wf.GetStdHandle /
# wf.GENERIC_READ / wf.FILE_SHARE_READ / wf.FILE_SHARE_WRITE / wf.OPEN_EXISTING / # wf.GENERIC_READ / wf.FILE_SHARE_READ / wf.FILE_SHARE_WRITE / wf.OPEN_EXISTING /
# wf.OPEN_ALWAYS / wf.FILE_ATTRIBUTE_NORMAL / # wf.OPEN_ALWAYS / wf.FILE_ATTRIBUTE_NORMAL /
# wf.STD_ERROR_HANDLE / wf.STD_OUTPUT_HANDLE; # wf.STD_ERROR_HANDLE / wf.STD_OUTPUT_HANDLE;
# wp.OpenThread / wp.SuspendThread / wp.ResumeThread / wp.GetCurrentThreadId / # wp.OpenThread / wp.SuspendThread / wp.ResumeThread / wp.GetCurrentThreadId /
# wp.GetCurrentProcess / wp.GetCurrentProcessId / wp.THREAD_SUSPEND_RESUME / # wp.GetCurrentProcess / wp.GetCurrentProcessId / wp.THREAD_SUSPEND_RESUME /
# wp.THREAD_GET_CONTEXT; # wp.THREAD_GET_CONTEXT;
# string.memset / string.strcpy / stdlib.malloc / free / c.Addr。 # string.memset / string.strcpy / stdlib.malloc / free / c.Addr.
# 本轮新增:wc.SetConsoleCtrlHandler / wc.CTRL_C_EVENT / wc.CTRL_BREAK_EVENT; # New this round: wc.SetConsoleCtrlHandler / wc.CTRL_C_EVENT / wc.CTRL_BREAK_EVENT;
# wp.CONTEXT(x64 完整布局)/ wp.CONTEXT_FULL / wp.GetThreadContext; # wp.CONTEXT (full x64 layout) / wp.CONTEXT_FULL / wp.GetThreadContext;
# wm.ReadProcessMemory(由本模块 ReadMemCb 包装后作 StackWalk64 回调); # wm.ReadProcessMemory (wrapped by this module's ReadMemCb as the StackWalk64 callback);
# w32/win32dbghelp.vp(STACKFRAME64/ADDRESS64/KDHELP64 + SymSetOptions/ # w32/win32dbghelp.vp (STACKFRAME64/ADDRESS64/KDHELP64 + SymSetOptions/
# SYMOPT_* + SymInitialize/SymFunctionTableAccess64/SymGetModuleBase64/ # SYMOPT_* + SymInitialize/SymFunctionTableAccess64/SymGetModuleBase64/
# StackWalk64); # StackWalk64);
# 转储文件:wf.WriteFile 追加写 <exe>.stackdump.txt + 自备常量 # Dump file: wf.WriteFile appends to <exe>.stackdump.txt + this module's own constant
# TRACEBACK_FILE_APPEND_DATA(FILE_APPEND_DATA,wf 未定义); # TRACEBACK_FILE_APPEND_DATA (FILE_APPEND_DATA, not defined by wf);
# 转储重入保护用内联汇编的一条 `lock cmpxchg`(AtomicTryEnterDump)对模块级 # Dump reentrancy protection uses one inline-asm `lock cmpxchg` (AtomicTryEnterDump) on the module-level
# 32 位 _DumpLock 做原子"检查并占用"(不用 InterlockedCompareExchange:x64 上是 # 32-bit _DumpLock for atomic test-and-set (not InterlockedCompareExchange: on x64 it is
# 编译器内部函数、无导出符号、不可链接)。 # a compiler intrinsic, has no exported symbol, and is not linkable).
# 本轮新增(真 Python traceback 输出):kernel32!GetEnvironmentVariableA 的**本模块自声明** # New this round (real Python traceback output): **this module's own declaration** of kernel32!GetEnvironmentVariableA
# (win32base 未声明,仓库仅 VET/App/lib/VDirs.vp:45 有;kernel32 本就链接,TPC/TPV 均 # (not declared by win32base; the repo declares it only in VET/App/lib/VDirs.vp:45; kernel32 is linked anyway, so both TPC and
# 可解析),用于 TRACEBACK_DIAG 开关;其余仅用既有 API(见第十三节)。 # TPV can resolve it), used for the TRACEBACK_DIAG switch; everything else uses only existing APIs (see section 13).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 九、"0 帧"的可执行结论(哪种情形 → 看哪个现场字段) # 9. Actionable conclusions for "0 frames" (which case -> which live field to look at)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# ⚠ 本节所有取证行都是 `[diag]` 行,故**只在 TRACEBACK_DIAG=1 时产出**(见第十三节 C); # NOTE: every evidence line in this section is a `[diag]` line, so it is **produced only when TRACEBACK_DIAG=1** (see section 13 C);
# 排查"0 帧"时请先用 `set TRACEBACK_DIAG=1` 启动目标进程。 # when investigating "0 frames" first start the target process with `set TRACEBACK_DIAG=1`.
# 首帧取证两行(" [diag] StackWalk64 first: ret=… addrpc=… fta=…" 与紧随其后的 # The two first-frame evidence lines (" [diag] StackWalk64 first: ret=... addrpc=... fta=..." and the one immediately after,
# " [diag] StackWalk64 in: machine=… rip=… rsp=… rbp=…"——原为单条 143 字符长行, # " [diag] StackWalk64 in: machine=... rip=... rsp=... rbp=..." -- originally one 143-char line,
# 拆成两条短行以消除窄终端折行造成的"显示残缺"假象,字段一个不减) # split into two short lines to remove the "mangled display" illusion caused by wrapping in a narrow terminal, with no field dropped)
# 与前置诊断行("[diag] Seq=… … modinited=…" / "SymGetModuleBase64(main)=…")合起来直接给出结论: # together with the preflight lines ("[diag] Seq=... ... modinited=..." / "SymGetModuleBase64(main)=...") directly give the conclusion:
# (1) modinited == 0 且 SymGetModuleBase64(main) == 0 ⇒ dbghelp 的模块表不含主模块。 # (1) modinited == 0 and SymGetModuleBase64(main) == 0 => dbghelp's module table does not contain the main module.
# 根因是 SymInitialize 的 fInvadeProcess 参数(必须 TRUE)或调用时机(要在进程启动期、 # Root cause is SymInitialize's fInvadeProcess argument (must be TRUE) or the call timing (it must be called during process
# 模块枚举可用时调用,本模块放在 Install 里)。修法:确认 Install 里 SymInitialize 的 # startup while module enumeration is available; this module does it in Install). Fix: confirm Install's SymInitialize
# 第三个实参为 1,且 Install 在入口线程、模块全部加载后调用;必要时改用 # third argument is 1, and that Install is called on the entry thread after all modules are loaded; if needed use
# SymRefreshModuleList 重新枚举。 # SymRefreshModuleList to re-enumerate.
# (2) 首帧 ret == 0,且紧邻有 "[diag] readmem failed addr=… size=… GetLastError=…" # (2) first frame ret == 0 with an adjacent "[diag] readmem failed addr=... size=... GetLastError=..."
# ⇒ 读栈内存失败。`ReadProcessMemory` 在回调里返回 0 会让 StackWalk64 立刻终止(=0 帧)。 # => reading stack memory failed. A `ReadProcessMemory` returning 0 in the callback makes StackWalk64 stop at once (= 0 frames).
# 修法:核对 ReadMemCb 的宽度口径(dbghelp 侧是 DWORD nSize + LPDWORD,kernel32 侧是 # Fix: check ReadMemCb's width convention (dbghelp side is DWORD nSize + LPDWORD, kernel32 side is
# SIZE_T,本模块已用本地 sizet 承接再写回 DWORD)与进程句柄/权限。 # SIZE_T; this module receives into a local sizet then writes back as DWORD) and the process handle/privileges.
# (3) 首帧 ret != 0 但返回 addrpc == 0,或 fta(SymFunctionTableAccess64(addrpc))== 0 # (3) first frame ret != 0 but returned addrpc == 0, or fta (SymFunctionTableAccess64(addrpc)) == 0
# ⇒ 上下文/模块基址口径问题(传给 StackWalk64 的 RIP/RSP/RBP 或模块基址不对)。 # => a context/module-base convention problem (the RIP/RSP/RBP passed to StackWalk64 or the module base is wrong).
# 修法:核对 CONTEXT 的 Rip/Rsp/Rbp 取值与 GetModuleHandleA(NULL) 基址。 # Fix: check CONTEXT's Rip/Rsp/Rbp values against the GetModuleHandleA(NULL) base.
# 补充:首帧行里 `ret=not-called` 表示 SymInitialize/回调地址/帧缓冲前提不全、根本没调用 # Note: `ret=not-called` in the first-frame line means the SymInitialize/callback-address/frame-buffer preconditions are incomplete and
# StackWalk64(此时该行仍会产出,保证"转 RBP 链"之前永远有首帧证据)。 # StackWalk64 was never called (the line is still produced then, guaranteeing first-frame evidence always exists before "switching to the RBP chain").
# 三者互斥且可判:只要把上面两组打印字段与判据对照,就能**当场**确定属于哪一类。 # The three cases are mutually exclusive and decidable: just compare the two printed field groups above against the criteria to determine **on the spot** which case it is.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 十、本轮新增的两组定位打印(应答"walk 到底跑到没有"与"重复残缺真因") # 10. Two groups of locating prints added this round (answering "did the walk run at all" and "the real cause of duplicate/mangled")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# A. walk 是否被调用、走了哪条出口(全部走 DumpMainThread / WalkViaDbgHelp 的既有输出通道; # A. Whether the walk was called and which exit it took (all via DumpMainThread / WalkViaDbgHelp's existing output channels;
# 这些行**只在 TRACEBACK_DIAG=1 时输出**): # these lines are **output only when TRACEBACK_DIAG=1**):
# * DumpMainThread 在**调用前**打 " [diag] walk: enter use_dbghelp=… symready=… # * DumpMainThread prints, **before the call**, " [diag] walk: enter use_dbghelp=... symready=...
# modinited=… frame_buf=… mapok=… asan=…",**调用后**打 " [diag] walk: leave # modinited=... frame_buf=... mapok=... asan=...", and, **after the call**, " [diag] walk: leave
# frames=… mapok=…"。两行都在 ⇒ walk 一定进了 RBP 主路径(dbghelp 加强可选)。 # frames=... mapok=...". Both lines present => the walk did enter the RBP main path (dbghelp augmentation optional).
# * 若 **连 walk: enter 都不出现**(且 TRACEBACK_DIAG=1)⇒ DumpMainThread 在本行之前就返回: # * If **even walk: enter does not appear** (and TRACEBACK_DIAG=1) => DumpMainThread returned before this line:
# 只可能是三处 fallback(OpenThread / SuspendThread / GetThreadContext 失败,各自都有 # it can only be one of three fallbacks (OpenThread / SuspendThread / GetThreadContext failing, each with its own
# "fallback: … cannot dump stack" 行)——据该行即可定位被跳过的分支。 # "fallback: ... cannot dump stack" line) -- that line locates the skipped branch.
# * WalkViaDbgHelp 的**每一条 return** 都先打 " [diag] walk: return reason=<…> frames=…", # * **Every return** in WalkViaDbgHelp first prints " [diag] walk: return reason=<...> frames=...",
# reason ∈ {null_state, disabled, null_frame_buf, null_context, stackwalk_first_failed, # with reason in {null_state, disabled, null_frame_buf, null_context, stackwalk_first_failed,
# stackwalk_failed, addrpc_zero, frame_limit}(含义见函数内注释),故 walk 的走向 # stackwalk_failed, addrpc_zero, frame_limit} (meanings in the in-function comments), so the walk's path
# 必可判定、不再靠猜。本轮起主路径是 RBP 链,DumpMainThread 另打 # is always decidable, never guessed. Since this round the main path is the RBP chain; DumpMainThread additionally prints
# `walk: primary=rbp_chain` / `walk: rbp_chain frames=…` / `walk: dbghelp enhancement engaged`; # `walk: primary=rbp_chain` / `walk: rbp_chain frames=...` / `walk: dbghelp enhancement engaged`;
# dbghelp 不可用时另打 `[diag] symwalk=disabled reason=…`(见第十一节 B)。 # when dbghelp is unavailable it additionally prints `[diag] symwalk=disabled reason=...` (see section 11 B).
# B. "重复残缺"真因的判别凭据(唯一凭据 = 行号是否重复): # B. Evidence for the real cause of "duplicate/mangled" (the only evidence = whether line numbers repeat):
# * EmitLine 在 **TRACEBACK_DIAG=1** 时给**每一行**加全局递增前缀 " [dump#<k>] " # * EmitLine adds a globally increasing prefix " [dump#<k>] " to **every line** when **TRACEBACK_DIAG=1**
# (k 由 _DumpLineNo 每次 +1);关闭(默认)时**不加前缀**,使帧行保持纯 Python # (k is incremented per call via _DumpLineNo); when off (default) it adds **no prefix**, keeping frame lines as plain Python
# traceback 形态(见第十三节)。 # traceback form (see section 13).
# * 同一段文本出现多份且**序号重复**(同一 k 出现两次)⇒ 该文本被写了两次,即**并发写 # * The same text appearing several times with **repeated numbers** (the same k twice) => that text was written twice, i.e. **concurrent writes to
# 同一 LineBuf**;此时才需要"输出互斥"。同一序号不可能出自单线程的一次 EmitLine。 # the same LineBuf**; only then is "output mutual exclusion" needed. The same number cannot come from a single-threaded EmitLine.
# * 文本被**截断/换行错位**但**序号严格递增、无重复** ⇒ 内容只写了一次(乱不了号), # * Text **truncated / line-wrapped out of place** but with **strictly increasing, non-repeating numbers** => the content was written once (numbers cannot be scrambled),
# 只可能是**终端长行折行/回显**层的显示效果;修法是缩短长行(本轮已把三条最长行拆短), # so it can only be a **terminal long-line wrap/echo** display effect; the fix is to shorten long lines (this round split the three longest),
# **不动锁**。 # **without touching the lock**.
# * 本轮补打:`[dump#skip]` 仅在使用输出互斥时才需要;本模块判定为"显示层", # * Added this round: `[dump#skip]` is only needed when output mutual exclusion is used; this module judged it a "display-layer"
# 故未加互斥、未打印该标记(判据与结论见交付说明)。 # issue, so no mutual exclusion was added and that marker is not printed (criteria and conclusion in the delivery notes).
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 十一、本轮两根因修复(崩溃 = 属性访问被降级;0 帧 = dbghelp 与环境互斥) # 11. Two root-cause fixes this round (crash = attribute access silently demoted; 0 frames = dbghelp conflicting with the environment)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# A. **崩溃根因不是分配失败,而是 TPC 把 `_State.<字段>` 静默降级成常量 null。** # A. **The crash root cause is not an allocation failure but TPC silently demoting `_State.<field>` to the null constant.**
# 实测证据(构建产物 IR,模块前缀 = 本文件 sha1 前 16 位 9b4ee486e08837c5): # Evidence (build-artifact IR; module prefix = first 16 hex digits of this file's sha1, 9b4ee486e08837c5):
# * 源码 `string.memset(_State.FrameBuf, 0, …)` 被生成为 # * The source `string.memset(_State.FrameBuf, 0, ...)` was generated as
# `call i8* @memset(i8* null, i32 0, i64 248)` —— 参数就是字面 null; # `call i8* @memset(i8* null, i32 0, i64 248)` -- the argument is the literal null;
# 同一函数里 `_State.LineBuf`(snprintf)、`_State.FuncTableAddr / ModuleBaseAddr` # in the same function `_State.LineBuf` (snprintf), `_State.FuncTableAddr / ModuleBaseAddr`
# (StackWalk64 的两个回调槽)同样全被折成 null。 # (StackWalk64's two callback slots) were likewise all folded to null.
# * `Install` 里的 `calloc(1,248)` 与其"分配失败即 return 0"的判空**都在且正确** # * `Install`'s `calloc(1,248)` and its null check "return 0 if allocation failed" **are both present and correct**
# (IR 里 `@calloc` 返回值被正常 store 到字段 20),故**不是** calloc 返回 NULL。 # (in the IR `@calloc`'s return value is stored to field 20 normally), so it is **not** calloc returning NULL.
# 机理(TPC 侧,只报不改):`TransPyC/lib/core/Handles/HandlesExprAttr.py` # Mechanism (TPC side, report only, no change): `TransPyC/lib/core/Handles/HandlesExprAttr.py`
# `_resolve_var_classname`(约 L285-293)对 `rawptr[X]` 形态的模块级全局只看 # `_resolve_var_classname` (approx. L285-293) looks only for a module-level global of the form `rawptr[X]` at
# `GVar.type.pointee`(= `X*`,仍是指针)⇒ 认不出 `X`,返回 None;兜底的 # `GVar.type.pointee` (= `X*`, still a pointer) => it fails to recognize `X` and returns None; the fallback
# `_recover_struct_class`(约 L723-740)虽能多解一层指针,但要求 `X` 已进入 # `_recover_struct_class` (approx. L723-740) can peel one more pointer level, but requires `X` to already be in
# **函数级**的 `Gen.variables/global_vars`(`HandlesFunctions.py:1848` 每函数清空), # the **function-level** `Gen.variables/global_vars` (`HandlesFunctions.py:1848` clears it per function),
# 而该登记只发生在"裸名 `X` 被求值一次"时(`HandlesExpr.py:479/487`)。 # and that registration only happens when "the bare name `X` is evaluated once" (`HandlesExpr.py:479/487`).
# ⇒ 凡**一上手就直接读写 `_State.<字段>`、且函数内从未单独求值过裸名 `_State` # => For any function that **reads/writes `_State.<field>` right at the start and never separately evaluates the bare name `_State`
# 的函数**,`_handle_attr_name` 在 L507-508 返回 None: # inside the function**, `_handle_attr_name` returns None at L507-508:
# - 作为调用实参 ⇒ 被静默替换为 null(→ memset(null) 崩溃); # - as a call argument => silently replaced with null (-> memset(null) crash);
# - 作为 `if X == None:` 的条件 ⇒ 整个 `if` 被 `HandlesIf` L170-171 丢弃 # - as the condition of `if X == None:` => the whole `if` is dropped by `HandlesIf` L170-171
# (→ 我们原来写的 `if _State.FrameBuf == None: …` **根本没被生成**)。 # (-> the `if _State.FrameBuf == None: ...` we originally wrote **was never generated**).
# 受影响函数(IR 佐证:这些函数体内 `@_State` 的 load 次数为 0): # Affected functions (IR corroboration: these function bodies load `@_State` 0 times):
# `EntryRva` / `LookupRva` / `EmitFrame` / `WalkViaDbgHelp`。 # `EntryRva` / `LookupRva` / `EmitFrame` / `WalkViaDbgHelp`.
# 其余函数(`EmitLine` / `DumpMainThread` / `ReadMemCb` / `Install` …)入口都有 # Other functions (`EmitLine` / `DumpMainThread` / `ReadMemCb` / `Install` ...) all have a
# `_State == None` 判空 ⇒ 裸名已登记 ⇒ 字段访问正常(这就是为什么 # `_State == None` null check at entry => the bare name is registered => field access works (this is why
# `DumpMainThread` 打出的 `frame_buf=1`、`modinited=1` 都是真的)。 # the `frame_buf=1`, `modinited=1` printed by `DumpMainThread` are real).
# 本模块的修法(**不去改 TPC**):凡直接使用 `_State.<字段>` 的函数,入口 # This module's fix (**without changing TPC**): every function that uses `_State.<field>` directly gets
# **第一句就写 `if _State == None: …`**(即使是冗余判空)——它既是真正的兜底, # **`if _State == None: ...` as its very first statement** (even if redundant) -- it is both a genuine fallback
# 也把裸名 `_State` 登记进 `Gen.variables`,使其后的字段访问被正常解析。 # and registers the bare name `_State` into `Gen.variables`, so later field accesses resolve normally.
# 崩溃点再加一道局部量兜底:`FBuf: VOIDPTR = _State.FrameBuf` → 判 `FBuf == None` # At the crash point add one more local-variable fallback: `FBuf: VOIDPTR = _State.FrameBuf` -> check `FBuf == None`
# → `memset(FBuf, …)`,把"局部变量"这条最可靠的通道用在最危险的一行上。 # -> `memset(FBuf, ...)`, using the most reliable channel (a local variable) on the most dangerous line.
# B. **0 帧 / DbgHelp 状态共享**:ASan 运行时会自己注册 DbgHelp 符号化器,本模块的 # B. **0 frames / DbgHelp state sharing**: the ASan runtime registers its own DbgHelp symbolizer, so this module's
# SymInitialize 因此被拒(DbgHelp 进程上下文是**进程级**状态、按句柄唯一)。但这**不是 # SymInitialize is rejected (the DbgHelp process context is a **process-level** state, unique per handle). But this is **not
# 冲突**:状态是共享的,用同一个 GetCurrentProcess() 伪句柄即可复用其模块表。故 Install # a conflict**: the state is shared, and using the same GetCurrentProcess() pseudo-handle reuses its module table. So Install's
# 的判定改为**取证式**三步:SymInitialize 成功 → SymReady=1;失败 → SymRefreshModuleList # decision becomes an **evidence-based** three-step one: SymInitialize succeeds -> SymReady=1; fails -> SymRefreshModuleList
# 刷新共享模块表(官方"外部初始化后再刷新"写法);再统一用 SymGetModuleBase64(主模块) # refreshes the shared module table (the official "refresh after external init" idiom); then a SymGetModuleBase64(main module)
# 探针裁决——探针为 0 则 SymReady 回退 0 并打印原因,绝不静默 0 帧。回溯路径顺序: # probe arbitrates -- if the probe is 0, SymReady falls back to 0 and the reason is printed, never a silent 0 frames. Walk-path order:
# * 主路径 = `WalkRbpChain`(纯 RBP 链,**零 dbghelp 依赖**); # * main path = `WalkRbpChain` (pure RBP chain, **zero dbghelp dependency**);
# * **第二兜底 = `ScanStack` 栈扫描**(零 dbghelp、且**不依赖 RBP / 帧指针**): # * **second fallback = `ScanStack` stack scan** (zero dbghelp and **no RBP / frame-pointer dependency**):
# RBP 链只拿到当前 RIP(<=1 帧)时启用(`walk: fallback=stack_scan`),见第十二节; # enabled when the RBP chain yields only the current RIP (<=1 frame) (`walk: fallback=stack_scan`), see section 12;
# * dbghelp 加强:只有 RBP 链拿不到更多帧(<=1)且 DbgUsable != 0(= 探针通过)时才走 # * dbghelp augmentation: only when the RBP chain yields no more frames (<=1) and DbgUsable != 0 (= probe passed) does it run
# `WalkViaDbgHelp`。它**清空已收帧并整段重建**(顺带丢掉扫描推断帧),是唯一能用 PE # `WalkViaDbgHelp`. It **clears the collected frames and rebuilds the whole stack** (dropping scan-inferred frames too) and is the only path that can use the PE
# 展开表(.pdata)穿过**无帧指针的系统帧**、回到 .vp 栈的路径——Ctrl+C 的典型现场 # unwind table (.pdata) to cross **frame-pointer-less system frames** back to the .vp stack -- the typical Ctrl+C scene
# (主线程阻塞在系统 DLL 里、RBP=0)只有它能还原真实调用链。 # (main thread blocked in a system DLL, RBP=0) can only have its real call chain recovered by it.
# * 降级**必须给原因**(不静默 0 帧):`[diag] symwalk=disabled reason=…`, # * A degrade **must give a reason** (no silent 0 frames): `[diag] symwalk=disabled reason=...`,
# reason ∈ {compile_time_switch, syminitialize_failed_or_main_module_missing, # reason in {compile_time_switch, syminitialize_failed_or_main_module_missing,
# callback_missing, null_frame_buf};ASan 探测用 GetModuleHandleA 两个常见 # callback_missing, null_frame_buf}; the ASan probe uses GetModuleHandleA with two common
# ASan 运行时名(clang_rt / libclang_rt 前缀),**只用于诊断行**,不再参与裁决。 # ASan runtime names (clang_rt / libclang_rt prefixes), **for diagnostic lines only**, no longer part of the decision.
# `WalkRbpChain` 同步加固(升级为主路径后它必须"绝不把卡死变成崩溃"): # `WalkRbpChain` is hardened in step (after becoming the main path it must "never turn a hang into a crash"):
# * 读 [rbp]/[rbp+8] 一律走 `wm.ReadProcessMemory`(**不直接解引用**野 rbp); # * reads of [rbp]/[rbp+8] always go through `wm.ReadProcessMemory` (**no direct dereference** of a wild rbp);
# * **每一步无条件打 `[diag] rbp:` 原始读数 + 判定**(先取证后决策,含被拒步;见第十二节); # * **every step unconditionally prints `[diag] rbp:` raw readings + verdict** (evidence before decision, including rejected steps; see section 12);
# * 自洽校验:非 0 / 8 字节对齐 / `[rbp] > rbp` / 单步跨度 <= RBP_MAX_STEP / # * self-consistency checks: non-zero / 8-byte aligned / `[rbp] > rbp` / single-step span <= RBP_MAX_STEP /
# 返回地址落在主模块基址区间 [Base, Base+TRACEBACK_MODULE_MAX) 内才**接受**该帧。 # return address within the main-module base range [Base, Base+TRACEBACK_MODULE_MAX) -- only then is the frame **accepted**.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 十二、本轮新增:RBP 链逐步原始读数 + 栈扫描兜底(应答"栈走不长") # 12. New this round: per-step RBP-chain raw readings + stack-scan fallback (answering "the walk is too short")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 现场(用户实测,`#0` 已正确解析:RIP 在主模块、映射/查表/帧 #0 全对):`#1 = 0x121fa` # Live case (user measurement, `#0` resolved correctly: RIP in the main module, mapping/lookup/frame #0 all correct): `#1 = 0x121fa`
# ——远小于主模块基址、也不是栈地址 ⇒ 链在第 1 步即断。⇒ 先把"断在哪一步、为什么断" # -- far below the main-module base and not a stack address => the chain breaks at step 1. => First turn "where it broke and why"
# 变成可读数据,再补一条**不依赖 RBP**的路径。 # into readable data, then add a path that **does not depend on RBP**.
# A. **逐步原始读数**(`WalkRbpChain` 内,前缀 `[diag] rbp:`,**先于任何 reject 早退**, # A. **Per-step raw readings** (inside `WalkRbpChain`, prefix `[diag] rbp:`, **before any reject early-return**,
# TRACEBACK_DIAG=1 时才输出): # output only when TRACEBACK_DIAG=1):
# frame=<i> rbp=0x… [rbp]=0x… [rbp+8]=0x… ← 读到的原始 8 字节值 # frame=<i> rbp=0x... [rbp]=0x... [rbp+8]=0x... <- raw 8-byte values read
# frame=<i> rpm_ok=<0/1> rpm_err=<GetLastError> accept=<0/1> reject_reason=<…> # frame=<i> rpm_ok=<0/1> rpm_err=<GetLastError> accept=<0/1> reject_reason=<...>
# reject_reason ∈ {rbp_zero, not_aligned, rpm_failed, ret_zero, ret_out_of_module, # reject_reason in {rbp_zero, not_aligned, rpm_failed, ret_zero, ret_out_of_module,
# not_growing, step_too_large};被接受时 reason=`accept`、accept=1。 # not_growing, step_too_large}; when accepted, reason=`accept`, accept=1.
# 仅被接受的步才 FramePush 入帧缓冲(旧实现在校验前就输出,会把 `0x121fa` 这类垃圾值当成 # Only accepted steps are FramePush-ed into the frame buffer (the old implementation output before validating, printing garbage values like `0x121fa`
# 一帧 external 打出来,误导读者)。三种断链根因据此一眼可分:读失败 / 值本身不是返回 # as an external frame, misleading the reader). The three chain-break root causes are distinguishable at a glance: read failure / the value itself is not a return
# 地址(帧指针未保留)/ 被自洽校验档掉。 # address (frame pointer not preserved) / rejected by the self-consistency checks.
# B. **栈扫描兜底**(`ScanStack`):RBP 链 <=1 帧时启用(`walk: fallback=stack_scan`)。 # B. **Stack-scan fallback** (`ScanStack`): enabled when the RBP chain yields <=1 frame (`walk: fallback=stack_scan`).
# 在 [RSP, RSP+2MiB) 内按 8 字节步长遍历,**4 KiB/块整块读**(失败即停并打地址/err); # Scans [RSP, RSP+2MiB) in 8-byte steps, **reading 4 KiB per chunk whole** (stops on failure and prints address/err);
# 候选 = 落在主模块区间、且**相邻 8 字节字(A-8 或 A+8)像栈地址**的字;按值去重、 # a candidate = a word within the main-module range whose **adjacent 8-byte word (A-8 or A+8) looks like a stack address**; de-duplicated by value,
# 上限 64(诊断打 `scan_candidates=<N>`);推断帧入缓冲时带 Scanned=1,打印时在帧行末尾加 # capped at 64 (diagnostic prints `scan_candidates=<N>`); inferred frames enter the buffer with Scanned=1 and append
# ` [inferred]`(` File "…", line 0, in <unknown> [inferred]`)明确标注**扫描推断** # ` [inferred]` (` File "...", line 0, in <unknown> [inferred]`) to the frame line to mark them clearly as **scan inferences**
# (可含误报,不与权威帧混淆,见第十三节)。全部读取只经 ReadProcessMemory, # (may contain false positives; never confused with authoritative frames; see section 13). All reads go only through ReadProcessMemory,
# 绝不直接解引用主线程内存(诊断设施不许把"卡死"变成"崩溃")。 # never a direct dereference of main-thread memory (a diagnostic facility must not turn a "hang" into a "crash").
# C. **帧指针是否保留**(调查结论与 TPC 侧修法落点,本模块只报不改): # C. **Whether the frame pointer is preserved** (investigation conclusion and TPC-side fix location; this module reports only, no change):
# * 单一 llc 调用点 = `TransPyC/lib/Projectrans/Phase2Translator.py:2160 _llc_compile`, # * The single llc call site = `TransPyC/lib/Projectrans/Phase2Translator.py:2160 _llc_compile`,
# 它在 `--traceback` 打开时把 `-frame-pointer=all` 追加到命令(2187-2193); # which appends `-frame-pointer=all` to the command when `--traceback` is on (2187-2193);
# App 模块(如 `TransPyV/App/lib/core/Gen/Expr/ExprGenOps.vp`,模块 sha1 # the App module (e.g. `TransPyV/App/lib/core/Gen/Expr/ExprGenOps.vp`, module sha1
# `8a96a2885bae09ca`)走的是**同一个**调用点(2608 的 _dso.ll / 2319 的内联合并), # `8a96a2885bae09ca`) goes through the **same** call site (2608's _dso.ll / 2319's inlining merge),
# 故 flag 的落点本身没问题。 # so the flag's location itself is fine.
# * 缓存失效:App 模块 obj = `output/{sha1}.obj`,`_compile_ll_files` 见其存在即跳过 # * Cache invalidation: App module obj = `output/{sha1}.obj`, `_compile_ll_files` skips it if it exists
# (2621),但 `--traceback` 时 `hotspot_on`(1144)为真 ⇒ 1196-1204 会删除旧 obj # (2621), but with `--traceback` `hotspot_on` (1144) is true => 1196-1204 deletes the old obj
# 强制重编;include 模块另有隔离目录 `includes.binary_traceback`(3097-3099)+ # to force recompilation; the include module has a separate isolated dir `includes.binary_traceback` (3097-3099) +
# 旧 obj 删除(3181-3190)。⇒ App/include 都不会静默复用"无帧指针的旧 obj"。 # old-obj deletion (3181-3190). => Neither App nor include silently reuses a "frame-pointer-less old obj".
# * 交付结论:若 `[diag] rbp:` 显示 `[rbp+8]` 仍不是返回地址(`ret_out_of_module`), # * Delivery conclusion: if `[diag] rbp:` shows `[rbp+8]` is still not a return address (`ret_out_of_module`),
# 则说明 `-frame-pointer=all`(llc 默认值级别的开关)没有生效,最稳的改法是在**IR 里 # then `-frame-pointer=all` (a flag at the level of an llc default) did not take effect; the most robust fix is to annotate
# 按函数显式标注** `"frame-pointer"="all"` —— 落点 # `"frame-pointer"="all"` explicitly per function **in the IR** -- location
# `TransPyV/App/lib/core/Gen/Func/FuncGenBase.vp`(属性拼装 `_append_llvm_attr`,154-181) # `TransPyV/App/lib/core/Gen/Func/FuncGenBase.vp` (attribute assembly `_append_llvm_attr`, 154-181)
# 与 `TransPyC/lib/core/Handles/HandlesFunctions.py:1735`(`func.attributes.add('noredzone')` # and `TransPyC/lib/core/Handles/HandlesFunctions.py:1735` (next to `func.attributes.add('noredzone')`
# 旁)—— 让帧指针成为**函数属性**而非 llc 命令行默认值,从根上免疫"llc 是否采纳默认值"。 # ) -- making the frame pointer a **function attribute** rather than an llc command-line default, immune at the root to "whether llc honors the default".
# * 【补充实测,Test/LLvmLiteTest Ctrl+C】`RBP=0x0`、RIP 落在系统 DLL:拒绝原因是 # * [Extra measurement, Test/LLvmLiteTest Ctrl+C] `RBP=0x0`, RIP in a system DLL: the reject reason is
# `rbp_zero`(不是 `ret_out_of_module`)⇒ 断链点**不在 .vp 函数**,而在被中断的那一层 # `rbp_zero` (not `ret_out_of_module`) => the break is **not in a .vp function** but in the interrupted
# 系统帧。RBP 是 callee-saved,但系统/CRT 的免帧指针代码会把它当**临时寄存器**用 # system frame. RBP is callee-saved, but frame-pointer-less system/CRT code uses it as a **scratch register**
# (`xor ebp,ebp` 是常见的清零写法),故中断现场的 RBP 与"最近一个维护帧指针的祖先" # (`xor ebp,ebp` is a common zeroing idiom), so the interrupted site's RBP is unrelated to "the nearest ancestor
# 无关。⇒ 结论:**给 .vp 函数保留帧指针无法救这条路**(链在更内层就已断),只有基于 # that maintains a frame pointer". => Conclusion: **keeping a frame pointer for .vp functions cannot save this path** (the chain is already broken further in); only
# PE 展开表(.pdata)的回溯(dbghelp!StackWalk64,见第十一节 B)才能从这种现场还原 # PE unwind-table (.pdata) based walking (dbghelp!StackWalk64, see section 11 B) can recover
# 真实调用链。帧指针开关因此**不是**本轮的修法落点,保留在上仅作历史记录。 # the real call chain from such a site. The frame-pointer switch is therefore **not** this round's fix location and is kept above only as a historical record.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 十三、本轮:真 Python traceback 风格输出 + TRACEBACK_DIAG 开关 # 13. This round: real Python-traceback-style output + the TRACEBACK_DIAG switch
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# A. 输出形状(默认,TRACEBACK_DIAG 未开)——整段 traceback 形如: # A. Output shape (default, TRACEBACK_DIAG off) -- the whole traceback looks like:
# Traceback (most recent call last): # Traceback (most recent call last):
# File "D:\...\App\main.vp", line 238, in main # File "D:\...\App\main.vp", line 238, in main
# w_upper: str = w_str.upper() # w_upper: str = w_str.upper()
# File "<0x7ff6... +0x1a2b no source line mapping>", line 0, in <unknown> # File "<0x7ff6... +0x1a2b no source line mapping>", line 0, in <unknown>
# File "<0x7ffb... external>", line 0, in <unknown> [inferred] # File "<0x7ffb... external>", line 0, in <unknown> [inferred]
# * **帧序反过来了**:回溯从最内层(当前 RIP)向调用者走,收集顺序 = 从内到外,打印时 # * **Frame order is reversed**: the walk goes from the innermost (current RIP) outward to callers, so collection order = inner to outer, and printing reverses it
# 逆序 ⇒ **最外层(CRT/调用者)在前、最内层最后**(与 Python traceback 一致)。故必须 # => **the outermost (CRT/caller) first, the innermost last** (matching Python traceback). So frames must
# 先把帧收集进缓冲(FramePush),走栈结束后再逆序打印(DumpMainThread)。 # first be collected into a buffer (FramePush), then printed in reverse after the walk ends (DumpMainThread).
# * 命中源位置的帧打两行:第一行 2 空格缩进 `File "路径", line N, in 函数名`(函数名取不到 # * A frame that hits a source location prints two lines: the first is 2-space-indented `File "path", line N, in function` (function name
# 写 `<unknown>`);第二行 4 空格缩进 + 该行源码——**运行期重开该文件**顺序读第 N 行 # is `<unknown>` if unavailable); the second is 4-space-indented + that source line -- **the file is reopened at runtime** and line N read sequentially
# (去行首尾空白、超长按 TRACEBACK_LINE_CAP 截断);**打不开 / 行号越界 / 读失败则整行 # (leading/trailing whitespace stripped, over-long truncated to TRACEBACK_LINE_CAP); **if it cannot be opened / the line number is out of range / the read fails, the whole line is
# 不打**(无占位、无报错,静默跳过),实现见 EmitSourceText(栈上缓冲,回调线程零堆分配)。 # omitted** (no placeholder, no error, silently skipped); see EmitSourceText (stack buffer, zero heap allocation from the callback thread).
# * ScanStack 推断帧在帧行末尾加 ` [inferred]`(保留"可能是误报"的信息,不再用 `~` 前缀)。 # * A ScanStack inferred frame appends ` [inferred]` to the frame line (keeping "may be a false positive" information, no longer a `~` prefix).
# * 未命中映射的帧 → `File "<0x<地址> +0x<rva> no source line>", line 0, in <unknown>` # * A frame missing from the mapping -> `File "<0x<address> +0x<rva> no source line>", line 0, in <unknown>`
# (地址+RVA 保留在同一行);落在主模块之外的帧 → `File "<0x<地址> external>", line 0, in <unknown>`。 # (address+RVA kept on the same line); a frame below the main module -> `File "<0x<address> external>", line 0, in <unknown>`.
# B. 帧缓冲:TracebackState.FrameAddr / FrameScan(`t.CArray[uint64/int32, TRACEBACK_FRAME_BUF]`, # B. Frame buffer: TracebackState.FrameAddr / FrameScan (`t.CArray[uint64/int32, TRACEBACK_FRAME_BUF]`,
# `TRACEBACK_FRAME_BUF = TRACEBACK_MAX_FRAMES + TRACEBACK_SCAN_MAX_HITS = 128`), # `TRACEBACK_FRAME_BUF = TRACEBACK_MAX_FRAMES + TRACEBACK_SCAN_MAX_HITS = 128`),
# **内嵌于状态对象** ⇒ 由 Install 的 `TracebackState.__sizeof__()` 分配覆盖,无需独立 malloc、 # **inlined into the state object** => covered by Install's `TracebackState.__sizeof__()` allocation, no separate malloc,
# 回调线程零分配。缓冲满时新帧直接丢弃(不越界),最终 `frames=` 以实际收集数为准。 # zero allocation from the callback thread. When full, new frames are simply dropped (no overflow); the final `frames=` reflects the actual collected count.
# C. **TRACEBACK_DIAG 开关**:读环境变量 `TRACEBACK_DIAG`,值 == "1" 才开;**每进程只读一次 # C. **TRACEBACK_DIAG switch**: reads env var `TRACEBACK_DIAG`, enabled only when the value == "1"; **read once per process
# 并缓存**(_DiagFlag -1/0/1,见 DiagEnabled)。关闭(默认)时不输出任何 `[diag] ...` / # and cached** (_DiagFlag -1/0/1, see DiagEnabled). When off (default) it emits no `[diag] ...` /
# `srcline map: ...` 行,也不加 ` [dump#<k>] ` 行号前缀(见第十节 B),使 dump 成为干净的 # `srcline map: ...` lines and adds no ` [dump#<k>] ` line-number prefix (see section 10 B), making the dump a clean
# traceback;打开时诊断信息量与既有一致(含逐帧 `[diag] rbp:` / `[diag] scan:` / symwalk 行)。 # traceback; when on, the diagnostic detail matches the previous amount (including per-frame `[diag] rbp:` / `[diag] scan:` / symwalk lines).
# 取值 API = kernel32!GetEnvironmentVariableA(本模块顶部自声明,见第八节)。 # The read API = kernel32!GetEnvironmentVariableA (declared at the top of this module, see section 8).
# D. 转储序号与落文件提示移到整段 traceback **之后**(` [traceback] dump seq=… also appended # D. The dump sequence number and the file-output notice move to **after** the whole traceback (
# to <exe>.stackdump.txt` 行),不再插在帧中间;`Ctrl+C` 转储后返回 FALSE(进程退出)、 # ` [traceback] dump seq=... also appended to <exe>.stackdump.txt`), no longer inserted between frames; `Ctrl+C` dumping returns FALSE (process exits),
# `Ctrl+Break` 转储后返回 TRUE(继续运行)语义不变。 # `Ctrl+Break` dumping returns TRUE (keeps running) -- semantics unchanged.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# 十四、崩溃转储(未处理异常;应答"崩溃无错误堆栈") # 14. Crash dump (unhandled exception; answering "crash has no error stack")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# A. 动机:Ctrl+C 有栈,**崩溃没有**——ASan 一旦过了它的报告窗口就什么都不打印,OS 默认只留一个 # A. Motivation: Ctrl+C has a stack, **a crash does not** -- once ASan passes its report window it prints nothing, and the OS leaves only
# 退出码(同一处崩溃实测:开 ASan = 0x80000003 STATUS_BREAKPOINT(int3),关 ASan = # an exit code (measurements of the same crash: ASan on = 0x80000003 STATUS_BREAKPOINT(int3), ASan off =
# 0xC0000005 ACCESS_VIOLATION,两者都**没有堆栈**)。故 Install 追加注册 # 0xC0000005 ACCESS_VIOLATION; neither has **a stack**). So Install additionally registers
# kernel32!SetUnhandledExceptionFilter(CrashHandler),由 CrashHandler → DumpCrashThread # kernel32!SetUnhandledExceptionFilter(CrashHandler), and CrashHandler -> DumpCrashThread
# 在**出错线程**上补出这条缺失的栈。 # produces the missing stack on the **faulting thread**.
# B. DumpCrashThread 的三条纪律: # B. DumpCrashThread's three rules:
# * **绝不吞异常**:过滤器恒定返回 EXCEPTION_CONTINUE_SEARCH(0),退出码 / WER / ASan 报告 # * **Never swallow the exception**: the filter always returns EXCEPTION_CONTINUE_SEARCH(0); exit code / WER / ASan report
# 的行为原样保留,本模块只加堆栈; # behavior is preserved; this module only adds the stack;
# * **不解引用 OS 指针**:EXCEPTION_POINTERS({ExceptionRecord, ContextRecord})与 # * **Never dereference OS pointers**: EXCEPTION_POINTERS ({ExceptionRecord, ContextRecord}) and
# EXCEPTION_RECORD 头部(u32 ExceptionCode / u32[…] ExceptionAddress)一律经 # the EXCEPTION_RECORD header (u32 ExceptionCode / u32[...] ExceptionAddress) are always read via
# ReadProcessMemory 读取; # ReadProcessMemory;
# * **不写 OS 的 CONTEXT**:StackWalk64 每走一帧都会回写它遍历的那条记录,故先把 OS 给的 # * **Never write the OS's CONTEXT**: StackWalk64 writes back to the record it walks on every frame, so first copy the OS-provided
# CONTEXT 拷进 _State.ContextBuf(尺寸已与 winnt.h 对齐 0x4D0)再从副本走栈。 # CONTEXT into _State.ContextBuf (size already aligned with winnt.h, 0x4D0) and walk from the copy.
# C. 走栈顺序与打印:dbghelp 优先(崩溃点通常在系统 DLL 里,RBP 链必然无从下手)→ RBP 链 → # C. Walk order and printing: dbghelp first (the crash point is usually in a system DLL, where the RBP chain is hopeless) -> RBP chain ->
# 栈扫描,与 Ctrl+C 路径同一套 WalkViaDbgHelp / WalkRbpChain / ScanStack 与 PrintFrames; # stack scan, the same WalkViaDbgHelp / WalkRbpChain / ScanStack and PrintFrames as the Ctrl+C path;
# 与 Ctrl+C 转储**共用同一把重入锁**(AtomicTryEnterDump),故一次只可能有一份 dump: # it **shares the same reentrancy lock** with the Ctrl+C dump (AtomicTryEnterDump), so only one dump can exist at a time:
# 崩溃若发生在转储进行中,本次崩溃不转储(锁被占),避免二次伤害。 # if a crash happens while a dump is in progress, this crash is not dumped (lock held), avoiding a second hit.
# 输出形状:`=== TPC crash dump (unhandled exception; the process keeps terminating) ===` # Output shape: `=== TPC crash dump (unhandled exception; the process keeps terminating) ===`
# → ` Trigger=unhandled-exception code=0x… addr=0x… av_kind=… av_addr=0x… crashthread=…` # -> ` Trigger=unhandled-exception code=0x... addr=0x... av_kind=... av_addr=0x... crashthread=...`
# → ` exe base=… RIP=… RSP=… RBP=…` → ` regs: rcx=… rdx=… r8=… r9=… r12=…` # -> ` exe base=... RIP=... RSP=... RBP=...` -> ` regs: rcx=... rdx=... r8=... r9=... r12=...`
# → 与 Ctrl+C 逐字同形的 Traceback 帧(含源码行回读)→ ` frames=N` → 落文件提示 # -> Traceback frames word-for-word identical to Ctrl+C (including source-line re-read) -> ` frames=N` -> file-output notice
# → `=== crash dump end ===`。 # -> `=== crash dump end ===`.
# * `av_kind`/`av_addr` 取自 EXCEPTION_RECORD 的 ExceptionInformation[0/1](0=读 1=写 8=执行; # * `av_kind`/`av_addr` come from EXCEPTION_RECORD's ExceptionInformation[0/1] (0=read 1=write 8=execute;
# 数据地址),只在 0xC0000005 类访问违规时有效——崩溃现场正是"原始数字胜过猜测"的场合; # data address) and are valid only for 0xC0000005 access-violation-type faults -- a crash site is exactly where "raw numbers beat guessing";
# * `regs` 给的是**出错帧的实参现场**(Win64 前 4 个整型实参 = rcx/rdx/r8/r9;r12 是该帧 # * `regs` gives **the argument state of the faulting frame** (Win64's first 4 integer arguments = rcx/rdx/r8/r9; r12 is the register that frame
# 用来解引用指针的寄存器):一条就能区分「实参传错」与「实参对、指针所指内容错」, # uses to dereference a pointer): one line distinguishes "wrong argument passed" from "argument correct, pointed-to content wrong",
# 不必再来一轮重编。Ctrl+C 路径不打印它(那里没有"出错指令的算子"可看)。 # with no need for another rebuild. The Ctrl+C path does not print it (there is no "operand of the faulting instruction" to see there).
# * **不打印 `[RBP+8]` 当返回地址**:.vp 帧的 RBP 是 locals base(`leaq N(%rsp), %rbp`), # * **Does not print `[RBP+8]` as a return address**: a .vp frame's RBP is a locals base (`leaq N(%rsp), %rbp`),
# 返回地址在 RBP + 帧字节数 - 8(逐函数不同;实测 _TypePrintDepth 是 RBP+0x38), # and the return address is at RBP + frame size - 8 (varies per function; measured _TypePrintDepth is RBP+0x38),
# `[RBP+8]` 只是无关局部量。调用者一律以 .stackmap 走栈结果为准。 # `[RBP+8]` is just an unrelated local. Callers always rely on the .stackmap walk result.
# 中文件头(栈转储/触发=)只属于 Ctrl+C 路径,崩溃路径不动它。 # The Chinese file header (stack dump/trigger=) belongs only to the Ctrl+C path; the crash path leaves it alone.
# D. 已知边界:ASan 的 VEH 优先于未处理异常过滤器——若某次崩溃由 ASan 正常报告(它自带堆栈), # D. Known boundaries: ASan's VEH takes priority over the unhandled-exception filter -- if a crash is reported normally by ASan (which has its own stack),
# 本路径不会被调用;本路径覆盖的正是「ASan 报不出来 / 没开 ASan」那两类(实测的两个退出码 # this path is not called; this path covers exactly the two cases where "ASan cannot report it / ASan is off" (the two measured exit codes
# 都在其中)。栈耗尽类崩溃(本路径自身也需要栈)不在保障范围内。 # are both among them). Stack-exhaustion crashes (this path itself needs stack) are out of scope.
# ============================================================================= # =============================================================================
# This module needs kernel32!GetEnvironmentVariableA for the TRACEBACK_DIAG switch, but # This module needs kernel32!GetEnvironmentVariableA for the TRACEBACK_DIAG switch, but
# includes/w32/win32base.vp does not declare it (the repository declares it only in # includes/w32/win32base.vp does not declare it (the repository declares it only in
+3 -3
View File
@@ -1,8 +1,8 @@
# ============================================================ # ============================================================
# vqt6 — Viper 的 Qt6 绑定包 # vqt6 - Viper's Qt6 binding package
# #
# 公共 Pythonic API(基于 C 桥接层) # Public Pythonic API (built on the C bridge layer)
# 用户用法: # Usage:
# import vqt6 # import vqt6
# app = vqt6.QApplication([]) # app = vqt6.QApplication([])
# win = vqt6.QWidget() # win = vqt6.QWidget()
+3 -3
View File
@@ -3,10 +3,10 @@ from stdint import *
from vqt6._types import * from vqt6._types import *
# ============================================================ # ============================================================
# vqt6/_qtcore.py — QtCore 的 t.State FFI 声明 # vqt6/_qtcore.py - t.State FFI declarations for QtCore
# #
# 所有函数都是 C 桥接层 (libvqt6_bridge.a) 的 extern "C" 函数 # All functions are extern "C" functions of the C bridge layer (libvqt6_bridge.a)
# 命名规范: vqt6_<class>_<action> # Naming convention: vqt6_<class>_<action>
# ============================================================ # ============================================================
# ============================================================ # ============================================================
+1 -1
View File
@@ -3,7 +3,7 @@ from stdint import *
from vqt6._types import * from vqt6._types import *
# ============================================================ # ============================================================
# vqt6/_qtgui.py — QtGui 的 t.State FFI 声明 # vqt6/_qtgui.py - t.State FFI declarations for QtGui
# ============================================================ # ============================================================
# ============================================================ # ============================================================
+1 -1
View File
@@ -3,7 +3,7 @@ from stdint import *
from vqt6._types import * from vqt6._types import *
# ============================================================ # ============================================================
# vqt6/_qtnetwork.py — QtNetwork 的 t.State FFI 声明 # vqt6/_qtnetwork.py - t.State FFI declarations for QtNetwork
# ============================================================ # ============================================================
# ============================================================ # ============================================================
+1 -1
View File
@@ -3,7 +3,7 @@ from stdint import *
from vqt6._types import * from vqt6._types import *
# ============================================================ # ============================================================
# vqt6/_qtwidgets.py — QtWidgets 的 t.State FFI 声明 # vqt6/_qtwidgets.py - t.State FFI declarations for QtWidgets
# ============================================================ # ============================================================
# ============================================================ # ============================================================
+3 -3
View File
@@ -2,10 +2,10 @@ import t
from stdint import * from stdint import *
# ============================================================ # ============================================================
# vqt6/_types.py — 公共类型定义 # vqt6/_types.py - common type definitions
# #
# Qt6 C++ 类在 Viper 端都是不透明指针 (void*) # Qt6 C++ classes are all opaque pointers (void*) on the Viper side
# 不暴露 C++ 类的内部布局,只暴露指针 # The internal layout of C++ classes is not exposed, only pointers are
# ============================================================ # ============================================================
# Opaque pointer typedefs (Viper-side representation of Qt6 C++ classes) # Opaque pointer typedefs (Viper-side representation of Qt6 C++ classes)
+23 -23
View File
@@ -3,33 +3,33 @@ from stdint import *
from w32.win32base import * from w32.win32base import *
# ============================================================================= # =============================================================================
# dbghelp / StackWalk64 声明(供 includes/traceback.vp 的 Ctrl+C 栈转储使用) # dbghelp / StackWalk64 declarations (used by the Ctrl+C stack dump in includes/traceback.vp)
# #
# 声明本仓用到的 dbghelp 栈回溯 API 与三个栈帧结构体: # Declares the dbghelp stack-walking APIs used by this repo plus three stack frame structs:
# SymInitialize 初始化 dbghelp 的进程上下文(fInvadeProcess=TRUE 只枚举模块、 # SymInitialize initializes the dbghelp process context (fInvadeProcess=TRUE enumerates modules only,
# 配合 SYMOPT_DEFERRED_LOADS 不加载 PDB) # combined with SYMOPT_DEFERRED_LOADS it does not load PDBs)
# SymRefreshModuleList 本进程的 DbgHelp 已被其它组件初始化(本模块的 SymInitialize 被拒) # SymRefreshModuleList when this process's DbgHelp was already initialized by another component (this module's SymInitialize is refused),
# 时,按其既有进程上下文重新枚举模块表("外部初始化后再刷新") # re-enumerates the module table using its existing process context ("refresh after external init")
# SymSetOptions 设定 SymInitialize 之后固定的选项集 # SymSetOptions sets the option set fixed by SymInitialize
# SymFunctionTableAccess64 按 RIP 查 RUNTIME_FUNCTION(PE 异常目录;不需要调试符号) # SymFunctionTableAccess64 looks up the RUNTIME_FUNCTION by RIP (PE exception directory; does not need debug symbols)
# SymGetModuleBase64 按 RIP 取模块基址 # SymGetModuleBase64 gets the module base address by RIP
# StackWalk64 逐帧回溯(MachineType=IMAGE_FILE_MACHINE_AMD64) # StackWalk64 walks frame by frame (MachineType=IMAGE_FILE_MACHINE_AMD64)
# #
# 其中 SymInitialize / SymSetOptions / StackWalk64 由 traceback.vp **直接调用**(静态引用); # Among these, SymInitialize / SymSetOptions / StackWalk64 are **called directly** by traceback.vp (static references);
# SymFunctionTableAccess64 / SymGetModuleBase64 既作为 StackWalk64 的回调原型在此登记,也被 # SymFunctionTableAccess64 / SymGetModuleBase64 are both registered here as StackWalk64 callback prototypes and
# traceback.vp **直接调用**做首帧取证诊断(首帧返回码/AddrPC/SymFunctionTableAccess64 返回值, # **called directly** by traceback.vp for first-frame forensic diagnostics (first-frame return code / AddrPC / SymFunctionTableAccess64 return value,
# 用于判定 StackWalk64 "0 帧"的根因)——同样属静态引用。traceback.vp 仍用 GetProcAddress 取它们 # used to determine the root cause of StackWalk64 "0 frames") -- also static references. traceback.vp still uses GetProcAddress to get their
# 的地址以填入 StackWalk64 的回调槽,为的是避开「跨模块函数名 → 函数指针」的隐式转换 # addresses to fill StackWalk64's callback slots, in order to avoid the implicit conversion of "cross-module function name -> function pointer"
# (该动态解析写法见 VET/App/lib/VGuard.vp:852-867)。 # (see VET/App/lib/VGuard.vp:852-867 for that dynamic-resolution idiom).
# #
# 链接期依赖:dbghelp.lib(MinGW: libdbghelp.a,-ldbghelp);ReadProcessMemory 在 # Link-time dependencies: dbghelp.lib (MinGW: libdbghelp.a, -ldbghelp); ReadProcessMemory is in
# kernel32(includes/w32/win32memory.vp:72)。本文件不引入其它链接期符号。 # kernel32 (includes/w32/win32memory.vp:72). This file introduces no other link-time symbols.
# #
# 结构体布局(x64,自然对齐;逐字段核对 winnt.h / dbghelp.h): # Struct layouts (x64, natural alignment; verified field by field against winnt.h / dbghelp.h):
# ADDRESS64 0x00 Offset(u64) 0x08 Segment(u16) 0x0C Mode(enum=i32) → 16 字节 # ADDRESS64 0x00 Offset(u64) 0x08 Segment(u16) 0x0C Mode(enum=i32) -> 16 bytes
# KDHELP64 96 字节(见字段注释) # KDHELP64 96 bytes (see field comments)
# STACKFRAME64 248 字节(见字段注释) # STACKFRAME64 248 bytes (see field comments)
# 三者只用于传给 StackWalk64(它按自身定义写入),故尺寸/偏移必须与 C 侧逐字节一致。 # All three are only passed to StackWalk64 (which writes per its own definition), so sizes/offsets must match the C side byte for byte.
# ============================================================================= # =============================================================================
# IMAGE_FILE_MACHINE_* (winnt.h; x64 = 0x8664) # IMAGE_FILE_MACHINE_* (winnt.h; x64 = 0x8664)
+1 -1
View File
@@ -4,7 +4,7 @@ from w32.win32base import *
# ============================================================ # ============================================================
# w32/winsock2.py — WinSock2 API 声明 (ws2_32.dll) # w32/winsock2.py - WinSock2 API declarations (ws2_32.dll)
# ============================================================ # ============================================================
# Version constants # Version constants
+10 -10
View File
@@ -1,23 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# zipfile.vp — ZIP 归档读写模块(Python zipfile 子集) # zipfile.vp - ZIP archive read/write module (subset of Python zipfile)
# #
# 支持: # Supports:
# - 写入成员: ZIP_STORED / ZIP_DEFLATED(raw deflate,wbits=-15,CRC32 校验) # - writing members: ZIP_STORED / ZIP_DEFLATED (raw deflate, wbits=-15, CRC32 check)
# - 读取成员: 自动识别 ZIP_STORED / ZIP_DEFLATED,解压后做 CRC32 校验 # - reading members: auto-detects ZIP_STORED / ZIP_DEFLATED, checks CRC32 after decompression
# - 读取时从文件尾部回扫 EOCD(容忍注释/前导数据) # - on read, scans back from the end of the file for EOCD (tolerates a comment/leading data)
# #
# 不支持: zip64(>4GB/成员数>65535)、加密、分卷、数据描述符(flag bit 3)等。 # Not supported: zip64 (>4GB / more than 65535 members), encryption, spanning, data descriptors (flag bit 3), etc.
# #
# 成员名为原始 UTF-8 字节(写入时置 flag bit 11,读取时不自动解码)。 # Member names are raw UTF-8 bytes (flag bit 11 is set when writing, not auto-decoded when reading).
# #
# 用法(对齐 Python 语义,只读模式自动把整个 zip 读入内存): # Usage (aligned with Python semantics; read-only mode reads the whole zip into memory automatically):
# w: zipfile.ZipFile* = zipfile.ZipFile("out.zip", "w") # w: zipfile.ZipFile* = zipfile.ZipFile("out.zip", "w")
# w.write("a.txt", buf, buf_len) # 默认 ZIP_DEFLATED # w.write("a.txt", buf, buf_len) # defaults to ZIP_DEFLATED
# w.close() # w.close()
# #
# r: zipfile.ZipFile* = zipfile.ZipFile("out.zip", "r") # r: zipfile.ZipFile* = zipfile.ZipFile("out.zip", "r")
# olen: sizet = 0 # olen: sizet = 0
# data: bytes = r.read("a.txt", &olen) # malloc 缓冲,用后 free # data: bytes = r.read("a.txt", &olen) # malloc buffer, free after use
# err = zipfile.error_code() / zipfile.error() # err = zipfile.error_code() / zipfile.error()
import stdlib import stdlib
import string import string