增加 JIT REPL

This commit is contained in:
TermiNexus committed 2026-09-21 16:17:19 +08:00
1 parent 9910e05760
commit a5ceb3d4eb
56 files changed
+7621 -584

No files matched your search

+52 -1
View File
@@ -115,7 +115,10 @@ def TranslateFileGetTrans(mb: rawptr[memhub.MemBuddy], file_path: str,
pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_buf[0] = 0
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(mb, tree, None, pyi_buf, PYI_BUF_SIZE)
# rel_path 传 file_path(本文件绝对路径):vpi 生成期间 fatal_error
# 的报错归因需指向本源文件(Phase A 预注册/翻译时 file_path 已可用),
# 且绝对路径可被 fatal_error 的 _read_source_line 直接打开打印源码行。
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(mb, tree, file_path, pyi_buf, PYI_BUF_SIZE)
if pyi_pos > 0:
# 写入 {sha1}.vpi 到 temp_dir(切片路径)
td_len_vpi: sizet = len(temp_dir)
@@ -709,6 +712,37 @@ def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: sizet)
return out_pos
def _append_host_export_implib(linker_flags: str, implib_path: str) -> str:
"""宿主导出模式:为链接参数追加 `--out-implib=<绝对路径>`(REPL 片段导入宿主用)
触发条件 = linker_flags 显式含 "--export-all-symbols"(工程级开关,宿主的
project.vpj 里声明)。此时 exe 会带导出表,REPL 片段 DLL 需要一份 import lib
才能把 IR 里的跨模块引用(形如 `<16位hex>.<函数名>`,见 .text.ll 的
`define i32 @"<sha1>.utf8_is_valid"`)解析到宿主的导出符号。
为什么路径由本层计算而不写进 project.vpj:Config 对 linker.flags 既不做
%VAR% 展开、也不按 project_dir 解析(Config.vpj:180-230 的 _join_flags_array
只做拼接;:570-632 只重写 -T 后的脚本路径;env 展开只发生在 static_libs/
dynamic_libs/includes/build_dir 等字段),而 out-implib 的值是相对
clang 进程 cwd 解析的 ⇒ 只有本层能从 output_dir + linker_output 算出绝对路径。
未开启工程的 linker_flags 不含该标志 ⇒ 原样返回,链接命令逐字节不变。
"""
if linker_flags is None or implib_path is None:
return linker_flags
if string.strstr(linker_flags, "--export-all-symbols") is None:
return linker_flags
extra: sizet = string.strlen(" -Wl,--out-implib=") + string.strlen(implib_path) + 1
buf: str = _mbuddy.alloc(string.strlen(linker_flags) + extra)
if buf is None:
VLogger.warning("宿主导出:import lib 参数缓冲分配失败(片段将无法导入宿主符号)", "link")
return linker_flags
viperlib.snprintf(buf, string.strlen(linker_flags) + extra,
"%s -Wl,--out-implib=%s", linker_flags, implib_path)
VLogger.info(f"宿主导出:产出 import lib {implib_path}", "link")
return buf
def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_flags: str, linker_output: str,
includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int:
"""调用 clang++ 链接 .obj 为 .exe
@@ -734,6 +768,16 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
if obj_path is None:
return 1
# 宿主 exe 导出(工程 linker.flags 含 --export-all-symbols 时)同步产出 import lib:
# {output_dir}/{linker_output}.dll.a —— REPL 片段链接时把它加进命令行即可导入
# 宿主(同一进程/同一份 includes 代码与数据)。未开启的工程不受影响。
if linker_output is not None:
implib_len: sizet = string.strlen(output_dir) + string.strlen(linker_output) + 8
implib_path: str = _mbuddy.alloc(implib_len)
if implib_path is not None:
viperlib.snprintf(implib_path, implib_len, "%s/%s.dll.a", output_dir, linker_output)
linker_flags = _append_host_export_implib(linker_flags, implib_path)
# 收集 includes.binary 的 .obj 文件路径
EXTRA_BUF_SIZE: sizet = 32768
extra_objs: bytes = _mbuddy.alloc(EXTRA_BUF_SIZE)
@@ -988,6 +1032,13 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: sizet,
# 构造命令: clang++ obj_paths extra_objs -o linker_output linker_flags
# ASan 运行库同样不再由 TPV 注入(在 link_obj_to_exe 里已有说明):
# 由 project.vpj 声明,--asan 下由 main.vp 校验。
# 宿主导出:linker_output 是完整输出路径 ⇒ import lib 取名 <linker_output>.dll.a
if linker_output is not None:
implib_len2: sizet = string.strlen(linker_output) + 8
implib_path2: str = _mbuddy.alloc(implib_len2)
if implib_path2 is not None:
viperlib.snprintf(implib_path2, implib_len2, "%s.dll.a", linker_output)
linker_flags = _append_host_export_implib(linker_flags, implib_path2)
use_flags: str = linker_flags
static_libs2: str = linker_static_libs
if static_libs2 is None:
+1 -1
View File
@@ -1007,7 +1007,7 @@ def RunPhase1(mb: rawptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
pyi_buf: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(
mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE)
mb, tree, entry.Path, pyi_buf, Phase1.PYI_BUF_SIZE)
if pyi_pos > 0:
pf_1b: rawptr[fileio.File] = fileio.File(vpi_path_1b, fileio.MODE.W)
if not pf_1b.closed:
+11 -11
View File
@@ -133,7 +133,7 @@ def _ScanClassInheritance(mb: rawptr[memhub.MemBuddy],
continue
# 遍历 AST 顶层节点,收集 ClassDef
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn: sizet = len(ch)
for ci in range(cn):
@@ -305,7 +305,7 @@ def _PreRegisterAllGenericTemplates(mb: rawptr[memhub.MemBuddy],
stdlib.free(src_buf_pg)
continue
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
ch_pg: rawptr[list[rawptr[ast.AST]]] = tree_pg.children
ch_pg: list[ast.AST*]* = tree_pg.children
if ch_pg is not None:
cn_pg: sizet = len(ch_pg)
for ci_pg in range(cn_pg):
@@ -405,13 +405,13 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
elif k_s == ast.ASTKind.Call:
cl_s2: rawptr[ast.Call] = rawptr[ast.Call](node)
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1)
cl_args_s: rawptr[list[rawptr[ast.AST]]] = cl_s2.args
cl_args_s: list[ast.AST*]* = cl_s2.args
if cl_args_s is not None:
na_s: sizet = len(cl_args_s)
i_a2: sizet
for i_a2 in range(na_s):
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
cl_kws_s: rawptr[list[rawptr[ast.AST]]] = cl_s2.keywords
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
if cl_kws_s is not None:
nk_s: sizet = len(cl_kws_s)
i_k2: sizet
@@ -422,7 +422,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
elif k_s == ast.ASTKind.Assign:
asn_s: rawptr[ast.Assign] = rawptr[ast.Assign](node)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
tgts_s: rawptr[list[rawptr[ast.AST]]] = asn_s.targets
tgts_s: list[ast.AST*]* = asn_s.targets
if tgts_s is not None:
nt_s: sizet = len(tgts_s)
i_t2: sizet
@@ -437,7 +437,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1)
fn_args_s: rawptr[ast.Arguments] = rawptr[ast.Arguments](fn_s.args)
if fn_args_s is not None:
fa_s: rawptr[list[rawptr[ast.AST]]] = fn_args_s.args
fa_s: list[ast.AST*]* = fn_args_s.args
if fa_s is not None:
nf_s: sizet = len(fa_s)
i_f2: sizet
@@ -447,7 +447,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.ClassDef:
cd_s: rawptr[ast.ClassDef] = rawptr[ast.ClassDef](node)
bases_s: rawptr[list[rawptr[ast.AST]]] = cd_s.bases
bases_s: list[ast.AST*]* = cd_s.bases
if bases_s is not None:
nb_s: sizet = len(bases_s)
i_b2: sizet
@@ -458,7 +458,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Tuple:
tu_s: rawptr[ast.Tuple] = rawptr[ast.Tuple](node)
elts_s: rawptr[list[rawptr[ast.AST]]] = tu_s.elts
elts_s: list[ast.AST*]* = tu_s.elts
if elts_s is not None:
ne_s: sizet = len(elts_s)
i_e2: sizet
@@ -476,7 +476,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
elif k_s == ast.ASTKind.Compare:
cp_s: rawptr[ast.Compare] = rawptr[ast.Compare](node)
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
cps_s: rawptr[list[rawptr[ast.AST]]] = cp_s.comparators
cps_s: list[ast.AST*]* = cp_s.comparators
if cps_s is not None:
nc_s: sizet = len(cps_s)
i_c2: sizet
@@ -484,7 +484,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BoolOp:
bo_s: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
bvs_s: rawptr[list[rawptr[ast.AST]]] = bo_s.values
bvs_s: list[ast.AST*]* = bo_s.values
if bvs_s is not None:
nv_s: sizet = len(bvs_s)
i_v2: sizet
@@ -498,7 +498,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
# need_dict_box[0] = 1
# 递归语句块子节点
if node.children is not None:
ch_s: rawptr[list[rawptr[ast.AST]]] = node.children
ch_s: list[ast.AST*]* = node.children
n_s: sizet = len(ch_s)
ci_s: sizet
for ci_s in range(n_s):
+4 -3
View File
@@ -304,7 +304,7 @@ def _PreRegisterAppGenericTemplates(mb: rawptr[memhub.MemBuddy],
stdlib.free(src_buf_pa)
continue
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
ch_pa: rawptr[list[rawptr[ast.AST]]] = tree_pa.children
ch_pa: list[ast.AST*]* = tree_pa.children
if ch_pa is not None:
cn_pa: sizet = len(ch_pa)
for ci_pa in range(cn_pa):
@@ -765,7 +765,8 @@ def RunMultiFileProject(mb: rawptr[memhub.MemBuddy],
continue
stdlib.free(vpi_path_vp2)
# 读取源文件 → 解析 AST → 生成 vpi
# (rel_path 传 None:与 TranslateFileGetTrans 后续覆盖写的内容一致)
# (rel_path 传 ent_vp2.Path:vpi 内容不依赖它——与 TranslateFileGetTrans
# 后续覆盖写的内容一致;此处仅用于 fatal_error 的报错归因,指向本源文件)
f_vp2: rawptr[fileio.File] = fileio.File(ent_vp2.Path, fileio.MODE.R)
if f_vp2.closed:
vpi_failed_p2 += 1
@@ -812,7 +813,7 @@ def RunMultiFileProject(mb: rawptr[memhub.MemBuddy],
pyi_buf_vp2: bytes = stdlib.malloc(PYI_BUF_SIZE_P2)
if pyi_buf_vp2 is not None:
pyi_pos_vp2: sizet = StubConverter._GeneratePyiFromAst(
mb, tree_vp2, None, pyi_buf_vp2, PYI_BUF_SIZE_P2)
mb, tree_vp2, ent_vp2.Path, pyi_buf_vp2, PYI_BUF_SIZE_P2)
if pyi_pos_vp2 > 0:
pyi_path_vp2: str = StubMergerCollect._sliced_path(temp_dir, td_len_vpa, ent_vp2.Sha1, "vpi")
if pyi_path_vp2 is not None: