feat(overload): vpi 写入端把签名后缀写进 def 名,签名 hash 与上下文无关
_overload_sig_text 算 hash 时固定传 imported_modules/from_imports/trans = None,只靠全局结构体表解析,使定义端、vpi 写入端(translator 尚未创建)、读取端天然算出同一个 hash,无需跨阶段搬运 import 上下文;解析不到即 fail-fast。Converter 新增 _PyiOverloadSuffix:同一作用域同名 def >=2 时追加 _v{hash}(方法跳过 self,@staticmethod 不跳;extern/state/export/main 不加)。读取端 _inject_function_declare 用 fd.name 拼符号,自动与定义端一致,返回/形参注解缓存也按后缀分组不再互相覆盖。
This commit is contained in:
1 parent
20e31b8dbd
commit
f54c365b20
2 files changed
+362
-59
No files matched your search
@@ -13,7 +13,7 @@ import sys
|
||||
|
||||
|
||||
# _eval_binop_int / _eval_binop_valid - recursively evaluate BinOp constant values.
|
||||
# Handles BitOr/BitAnd/Add/Sub and nested Name refs to registered CDefine constants.
|
||||
# Handles BitOr/BitAnd/Add/Sub and nested Name refs to registered DefineT constants.
|
||||
def _eval_binop_valid(node: ast.AST*) -> int:
|
||||
"""Return 1 if a BinOp node is fully computable from constants or registered CDefines"""
|
||||
if node is None: return 0
|
||||
@@ -33,10 +33,10 @@ def _eval_binop_valid(node: ast.AST*) -> int:
|
||||
if _eval_binop_valid(bop.right) != 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
# Name reference: check whether it is a registered CDefine constant
|
||||
# Name reference: check whether it is a registered DefineT constant
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None:
|
||||
# Look up in the local CDefine table
|
||||
# Look up in the local DefineT table
|
||||
val: int64 = _lookup_cdefine_for_pyi(nm.id)
|
||||
if _pyi_cdefine_found() != 0: return 1
|
||||
return 0
|
||||
@@ -81,11 +81,11 @@ def _eval_binop_int(node: ast.AST*) -> int64:
|
||||
return 0
|
||||
|
||||
|
||||
# Forward declaration for CDefine lookup (real impl in HandlesType);
|
||||
# Forward declaration for DefineT lookup (real impl in HandlesType);
|
||||
# weak refs avoid circular dependencies.
|
||||
_g_pyi_cdefine_found: int = 0
|
||||
|
||||
# Per-file CDefine value cache (vpi only): vpi runs before the translate phase, so
|
||||
# Per-file DefineT value cache (vpi only): vpi runs before the translate phase, so
|
||||
# _GeneratePyiFromAst registers in order; C arrays dodge list.get misdispatch.
|
||||
_g_pyi_cdef_names: str* = None
|
||||
_g_pyi_cdef_vals: int64* = None
|
||||
@@ -93,12 +93,12 @@ _g_pyi_cdef_count: int = 0
|
||||
|
||||
|
||||
def _lookup_cdefine_for_pyi(name: str) -> int64:
|
||||
"""Look up a constant value from this file's CDefine cache or the local CDefine table"""
|
||||
"""Look up a constant value from this file's DefineT cache or the local DefineT table"""
|
||||
global _g_pyi_cdefine_found
|
||||
# Look up this file's CDefine cache first (globals are empty during vpi gen)
|
||||
# Look up this file's DefineT cache first (globals are empty during vpi gen)
|
||||
if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None:
|
||||
# Bound loop by _g_pyi_cdef_count; never len()/.get on i8** (len falls back
|
||||
# to strlen, .get misdispatches to HashTable.get) -- index via t.CPtr(base + i*8).
|
||||
# to strlen, .get misdispatches to HashTable.get) -- index via rawptr(base + i*8).
|
||||
pn: sizet = sizet(_g_pyi_cdef_count)
|
||||
for pi in range(int(pn)):
|
||||
entry_pc: str* = (str*)(uint64(_g_pyi_cdef_names) + pi * 8)
|
||||
@@ -115,7 +115,7 @@ def _lookup_cdefine_for_pyi(name: str) -> int64:
|
||||
|
||||
|
||||
def _eval_cdefine_value_cache(node: ast.AST*) -> int64:
|
||||
"""Evaluate a CDefine value for preprocessing registration (Constant/UnaryOp/BinOp/Call/Name)"""
|
||||
"""Evaluate a DefineT value for preprocessing registration (Constant/UnaryOp/BinOp/Call/Name)"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
@@ -160,6 +160,30 @@ def _PyiEmit(buf: bytes, buf_size: sizet, pos: sizet, text: str) -> sizet:
|
||||
return pos + tlen
|
||||
|
||||
|
||||
# _PyiEmitEnumValue - 把枚举成员值写成 " = N",与 ClassGenEnum._extract_int_value 同口径。
|
||||
# 值为字面量(含负号)时写真实值;其余形态定义端也算作 0,这里同样写 0。
|
||||
# 为什么必须写:vpi 是跨模块唯一的签名来源,枚举成员值丢了就没法在依赖方重建 EnumTable。
|
||||
def _PyiEmitEnumValue(buf: bytes, buf_size: sizet, pos: sizet, value_node: ast.AST*) -> sizet:
|
||||
"""Emit " = <int>" for an enum member value (mirrors ClassGenEnum._extract_int_value)."""
|
||||
if value_node is None: return pos
|
||||
val: int64 = 0
|
||||
k: int = value_node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
c: ast.Constant* = ast.Constant*(value_node)
|
||||
if c.const_kind == ast.CONST_INT: val = c.int_val
|
||||
elif k == ast.ASTKind.UnaryOp:
|
||||
u: ast.UnaryOp* = ast.UnaryOp*(value_node)
|
||||
if u.op == ast.OpKind.USub and u.operand is not None and u.operand.kind() == ast.ASTKind.Constant:
|
||||
c2: ast.Constant* = ast.Constant*(u.operand)
|
||||
if c2.const_kind == ast.CONST_INT: val = -c2.int_val
|
||||
num_buf: str = stdlib.malloc(32)
|
||||
if num_buf is None: return pos
|
||||
viperlib.snprintf(num_buf, 32, " = %lld", val)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf)
|
||||
stdlib.free(num_buf)
|
||||
return pos
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiIndent - append level*4 spaces to buf at pos
|
||||
def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet, level: int) -> sizet:
|
||||
@@ -169,6 +193,59 @@ def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet, level: int) -> sizet:
|
||||
return pos
|
||||
|
||||
|
||||
# _PyiMarkerName - bare name of a marker node (Name / Attribute / Call), else None.
|
||||
def _PyiMarkerName(node: ast.AST*) -> str:
|
||||
"""Return the bare marker name of Name / Attribute / Call(node), else None."""
|
||||
if node is None: return None
|
||||
inner: ast.AST* = node
|
||||
if inner.kind() == ast.ASTKind.Call:
|
||||
cl_m: ast.Call* = ast.Call*(inner)
|
||||
if cl_m.func is None: return None
|
||||
inner = cl_m.func
|
||||
if inner.kind() == ast.ASTKind.Name:
|
||||
nm_m: ast.Name* = ast.Name*(inner)
|
||||
return nm_m.id
|
||||
if inner.kind() == ast.ASTKind.Attribute:
|
||||
at_m: ast.Attribute* = ast.Attribute*(inner)
|
||||
return at_m.attr
|
||||
return None
|
||||
|
||||
|
||||
# _PyiMarkerKind - 0=not the marker / 1=bare marker / 2=marker[T] wrapper.
|
||||
def _PyiMarkerKind(node: ast.AST*, marker: str) -> int:
|
||||
"""Whether node is `marker` (bare) or `marker[T]` (wrapper)."""
|
||||
if node is None or marker is None: return 0
|
||||
if node.kind() == ast.ASTKind.Subscript:
|
||||
sub_m: ast.Subscript* = ast.Subscript*(node)
|
||||
sub_nm: str = _PyiMarkerName(sub_m.value)
|
||||
if sub_nm is not None and string.strcmp(sub_nm, marker) == 0: return 2
|
||||
return 0
|
||||
bare_nm: str = _PyiMarkerName(node)
|
||||
if bare_nm is not None and string.strcmp(bare_nm, marker) == 0: return 1
|
||||
return 0
|
||||
|
||||
|
||||
# _PyiMarkerPayload - inner type of `marker[T]`; None for a bare marker or a non-match.
|
||||
def _PyiMarkerPayload(node: ast.AST*, marker: str) -> ast.AST*:
|
||||
"""Return marker[T]'s T; None when node is not that wrapper."""
|
||||
if _PyiMarkerKind(node, marker) != 2: return None
|
||||
sub_p: ast.Subscript* = ast.Subscript*(node)
|
||||
return sub_p.slice
|
||||
|
||||
|
||||
# _PyiHasDecorator - whether decorator_list contains the given bare marker name.
|
||||
def _PyiHasDecorator(decors: list[ast.AST*]*, marker: str) -> int:
|
||||
"""1 when any decorator is `marker` (bare / Attribute / Call form)."""
|
||||
if decors is None or marker is None: return 0
|
||||
dn_d: sizet = len(decors)
|
||||
for di_d in range(int(dn_d)):
|
||||
dn_node: ast.AST* = decors.get(di_d)
|
||||
if dn_node is None: continue
|
||||
dn_name: str = _PyiMarkerName(dn_node)
|
||||
if dn_name is not None and string.strcmp(dn_name, marker) == 0: return 1
|
||||
return 0
|
||||
|
||||
|
||||
# _PyiBinOpOpText - emit the " OP " text for a binary operator.
|
||||
# Kept byte-identical to TPC's BinOp operator table; unknown ops fail fast.
|
||||
def _PyiBinOpOpText(buf: bytes, buf_size: sizet, pos: sizet, op: int, node: ast.AST*) -> sizet:
|
||||
@@ -201,12 +278,18 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
# Name: simple type name (e.g. int, str, MyClass)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm.id is not None: return _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
if nm.id is not None:
|
||||
# Retired bare markers map to their source spellings.
|
||||
if string.strcmp(nm.id, "PtrT") == 0: return _PyiEmit(buf, buf_size, pos, "rawptr")
|
||||
if string.strcmp(nm.id, "CVoid") == 0: return _PyiEmit(buf, buf_size, pos, "void")
|
||||
return _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
return _PyiEmit(buf, buf_size, pos, "int")
|
||||
|
||||
# Attribute: attribute reference (e.g. int, memhub.MemBuddy)
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(annot)
|
||||
if at.attr is not None and string.strcmp(at.attr, "PtrT") == 0: return _PyiEmit(buf, buf_size, pos, "rawptr")
|
||||
if at.attr is not None and string.strcmp(at.attr, "CVoid") == 0: return _PyiEmit(buf, buf_size, pos, "void")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, at.value, depth + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ".")
|
||||
if at.attr is not None: pos = _PyiEmit(buf, buf_size, pos, at.attr)
|
||||
@@ -225,16 +308,59 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
return pos
|
||||
|
||||
# Subscript: subscripted type (e.g. list[int], CArray[elem, count])
|
||||
# Subscript: subscripted type (e.g. list[int], ArrayT[elem, count])
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(annot)
|
||||
# Builtin markers carry source syntax in a stub: `T*` / `[N]T` / `const T` /
|
||||
# `volatile T` / `&T` / `&temp T`. The retired t.* C-names must never be written.
|
||||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
|
||||
mk_n: ast.Name* = ast.Name*(sub.value)
|
||||
if mk_n.id is not None:
|
||||
if string.strcmp(mk_n.id, "PtrT") == 0:
|
||||
# One trailing '*' per pointer layer (nested PtrT recurses into `T**`).
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
return _PyiEmit(buf, buf_size, pos, "*")
|
||||
if string.strcmp(mk_n.id, "ConstT") == 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "const ")
|
||||
return _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
if string.strcmp(mk_n.id, "VolatileT") == 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "volatile ")
|
||||
return _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
if string.strcmp(mk_n.id, "RefT") == 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "&")
|
||||
return _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
if string.strcmp(mk_n.id, "TempRefT") == 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "&temp ")
|
||||
return _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
if string.strcmp(mk_n.id, "ArrayT") == 0:
|
||||
# `[N]T` (or `[]T` when the count is None).
|
||||
if sub.slice is not None and sub.slice.kind() == ast.ASTKind.Tuple:
|
||||
tup_a: ast.Tuple* = ast.Tuple*(sub.slice)
|
||||
if tup_a.elts is not None and len(tup_a.elts) >= 2:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "[")
|
||||
cnt_a: ast.AST* = tup_a.elts.get(1)
|
||||
cnt_none_a: int = 0
|
||||
if cnt_a is not None and cnt_a.kind() == ast.ASTKind.Constant:
|
||||
cnt_ca: ast.Constant* = ast.Constant*(cnt_a)
|
||||
if cnt_ca.const_kind == ast.CONST_NONE: cnt_none_a = 1
|
||||
if cnt_none_a == 0: pos = _PyiTypeStr(buf, buf_size, pos, cnt_a, depth + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "]")
|
||||
return _PyiTypeStr(buf, buf_size, pos, tup_a.elts.get(0), depth + 1)
|
||||
if string.strcmp(mk_n.id, "DefineT") == 0:
|
||||
# `define NAME: T = value`: only the wrapped T is a type here.
|
||||
return _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
# Storage-class wrappers carry no value type; never write them into a stub.
|
||||
if string.strcmp(mk_n.id, "ExportT") == 0 or string.strcmp(mk_n.id, "ExternT") == 0 \
|
||||
or string.strcmp(mk_n.id, "StaticT") == 0 or string.strcmp(mk_n.id, "State") == 0 \
|
||||
or string.strcmp(mk_n.id, "AutoPtrT") == 0:
|
||||
return _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, sub.value, depth + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "[")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "]")
|
||||
return pos
|
||||
|
||||
# BinOp: union types and CArray count expressions (MAX_ORDER + 1,
|
||||
# BinOp: union types and ArrayT count expressions (MAX_ORDER + 1,
|
||||
# 2 * ZHUFF_MAX_CODES); operator text via _PyiBinOpOpText, byte-identical to TPC.
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
@@ -301,7 +427,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "'")
|
||||
return pos
|
||||
if cn.const_kind == ast.CONST_NONE: return _PyiEmit(buf, buf_size, pos, "None")
|
||||
# Numeric constants (e.g. the count in t.CArray[uint32, 64]): emit the value
|
||||
# Numeric constants (e.g. the count in t.ArrayT[uint32, 64]): emit the value
|
||||
# itself; falling back to int makes stub loading raise TypeError.
|
||||
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR:
|
||||
num_buf_ct: str = stdlib.malloc(24)
|
||||
@@ -312,8 +438,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
return pos
|
||||
return _PyiEmit(buf, buf_size, pos, "int")
|
||||
|
||||
# List: function pointer parameter table (e.g. the inner [t.CPtr, ULONG] of
|
||||
# t.Callable[[t.CPtr, ULONG], BOOL]); must emit "[a, b]" or it fails fast.
|
||||
# List: function pointer parameter table (e.g. the inner [rawptr, ULONG] of
|
||||
# t.Callable[[rawptr, ULONG], BOOL]); must emit "[a, b]" or it fails fast.
|
||||
if k == ast.ASTKind.List:
|
||||
lst: ast.List* = ast.List*(annot)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "[")
|
||||
@@ -390,7 +516,7 @@ def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet, node: ast.AST*, dept
|
||||
if cn.const_kind == ast.CONST_NONE: return _PyiEmit(buf, buf_size, pos, "None")
|
||||
return pos
|
||||
|
||||
# Name: CDefine constant name (e.g. share: ULONG = SHARE_READ)
|
||||
# Name: DefineT constant name (e.g. share: ULONG = SHARE_READ)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None: return _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
@@ -453,8 +579,58 @@ def _PyiEnsureEscScratch() -> int:
|
||||
|
||||
|
||||
# _PyiFunction - emit a function signature (def name(...) -> Ret: pass)
|
||||
def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*, indent_level: int, class_name: str) -> sizet:
|
||||
# _PyiIsStaticMethod - 是否 @staticmethod(重载签名需跳过 self/cls 时用)
|
||||
def _PyiIsStaticMethod(fn: ast.FunctionDef*) -> int:
|
||||
"""方法带 @staticmethod 时返回 1"""
|
||||
if fn is None or fn.decorator_list is None: return 0
|
||||
dn: sizet = len(fn.decorator_list)
|
||||
for i in range(int(dn)):
|
||||
d: ast.AST* = fn.decorator_list.get(i)
|
||||
if d is None: continue
|
||||
nm: str = _PyiMarkerName(d)
|
||||
if nm is not None and string.strcmp(nm, "staticmethod") == 0: return 1
|
||||
return 0
|
||||
|
||||
|
||||
# _PyiOverloadSuffix - vpi 里的重载符号后缀。
|
||||
# 同一作用域(模块体 / 类体)内同名 def >=2 时返回 "_v{参数签名hash}",否则 None。
|
||||
# hash 由 GenApi.overload_sig_suffix 计算,与定义端 / 读取端是同一个函数且与 import 上下文无关,
|
||||
# 所以三端算出的名字必然一致;把后缀直接写进 vpi 的 def 名,读取端按名注入即天然对齐。
|
||||
def _PyiOverloadSuffix(mb: memhub.MemBuddy*, body: list[ast.AST*]*, fn: ast.FunctionDef*, skip_first: int) -> str:
|
||||
"""重载组返回 "_v{hash}",否则 None"""
|
||||
if mb is None or body is None or fn is None or fn.name is None: return None
|
||||
# extern/state/export/main 走裸名(C ABI / 入口点),定义端会直接报错,这里也不加后缀
|
||||
if string.strcmp(fn.name, "main") == 0: return None
|
||||
if _PyiMarkerKind(fn.returns, "ExternT") != 0: return None
|
||||
if _PyiMarkerKind(fn.returns, "State") != 0: return None
|
||||
if _PyiMarkerKind(fn.returns, "ExportT") != 0: return None
|
||||
n: sizet = len(body)
|
||||
cnt: int = 0
|
||||
for i in range(int(n)):
|
||||
ch: ast.AST* = body.get(i)
|
||||
if ch is None: continue
|
||||
if ch.kind() != ast.ASTKind.FunctionDef: continue
|
||||
f2: ast.FunctionDef* = ast.FunctionDef*(ch)
|
||||
if f2 is None or f2.name is None: continue
|
||||
if string.strcmp(f2.name, fn.name) == 0:
|
||||
cnt += 1
|
||||
if cnt >= 2: break
|
||||
if cnt < 2: return None
|
||||
return GenApi.overload_sig_suffix(mb, fn, None, None, skip_first)
|
||||
|
||||
|
||||
def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*, indent_level: int, class_name: str, ovl_suffix: str = None) -> sizet:
|
||||
"""Emit a function signature into buf and return the new pos"""
|
||||
# Storage-class / export markers become source keywords; the wrapper is not written.
|
||||
ret_node: ast.AST* = fn.returns
|
||||
fd_is_define: int = _PyiMarkerKind(ret_node, "DefineT")
|
||||
fd_is_export: int = _PyiMarkerKind(ret_node, "ExportT")
|
||||
fd_is_extern: int = _PyiMarkerKind(ret_node, "State")
|
||||
if fd_is_define != 0: ret_node = _PyiMarkerPayload(ret_node, "DefineT")
|
||||
elif fd_is_export != 0: ret_node = _PyiMarkerPayload(ret_node, "ExportT")
|
||||
elif fd_is_extern != 0: ret_node = _PyiMarkerPayload(ret_node, "State")
|
||||
fd_is_static: int = _PyiHasDecorator(fn.decorator_list, "StaticT")
|
||||
fd_is_virtual: int = _PyiHasDecorator(fn.decorator_list, "VTableT")
|
||||
# Decorators
|
||||
if fn.decorator_list is not None:
|
||||
fn_decorator_list: list[ast.AST*]* = fn.decorator_list
|
||||
@@ -466,14 +642,26 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
for ddi in range(int(dn)):
|
||||
dec: ast.AST* = fn_decorator_list.get(ddi)
|
||||
if dec is not None:
|
||||
dmn: str = _PyiMarkerName(dec)
|
||||
if dmn is not None and (string.strcmp(dmn, "StaticT") == 0 or string.strcmp(dmn, "VTableT") == 0 \
|
||||
or string.strcmp(dmn, "NoVTable") == 0):
|
||||
continue
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "@")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, dec)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "def ")
|
||||
if fd_is_define != 0: pos = _PyiEmit(buf, buf_size, pos, "define ")
|
||||
elif fd_is_export != 0: pos = _PyiEmit(buf, buf_size, pos, "export def ")
|
||||
elif fd_is_extern != 0: pos = _PyiEmit(buf, buf_size, pos, "extern def ")
|
||||
else:
|
||||
if fd_is_static != 0: pos = _PyiEmit(buf, buf_size, pos, "static ")
|
||||
elif fd_is_virtual != 0: pos = _PyiEmit(buf, buf_size, pos, "virtual ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, "def ")
|
||||
if fn.name is not None: pos = _PyiEmit(buf, buf_size, pos, fn.name)
|
||||
# 重载组:名字后带 "_v{签名hash}",跨模块注入端按此名生成符号,天然与定义端一致
|
||||
if ovl_suffix is not None: pos = _PyiEmit(buf, buf_size, pos, ovl_suffix)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "(")
|
||||
|
||||
# Parameter list
|
||||
@@ -527,9 +715,9 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
|
||||
# Return type
|
||||
if fn.returns is not None:
|
||||
if ret_node is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " -> ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, fn.returns)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, ret_node)
|
||||
else:
|
||||
# Without a `->` annotation, @c.CReturn is removed: fail fast to regenerate vpi
|
||||
_PyiRejectCReturnDecorator(fn, fn.decorator_list)
|
||||
@@ -605,6 +793,34 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
# _PyiClass - emit a class definition (fields and method stubs).
|
||||
def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, indent_level: int) -> sizet:
|
||||
"""Emit a class definition into buf and return the new pos"""
|
||||
# enum / struct / union / renum sugar: never write EnumBase / StructBase / ... bases.
|
||||
cls_header: str = "class"
|
||||
cls_base_skip: int = -1
|
||||
if cls.bases is not None:
|
||||
cls_nb: sizet = len(cls.bases)
|
||||
for cbi in range(int(cls_nb)):
|
||||
cb_nm: str = _PyiMarkerName(cls.bases.get(cbi))
|
||||
if cb_nm is None: continue
|
||||
# `struct X(Base):` appends StructBase after the real parents, so it may coexist.
|
||||
if string.strcmp(cb_nm, "StructBase") == 0:
|
||||
cls_header = "struct"
|
||||
cls_base_skip = cbi
|
||||
elif cls_nb == 1 and string.strcmp(cb_nm, "EnumBase") == 0:
|
||||
cls_header = "enum"
|
||||
cls_base_skip = cbi
|
||||
elif cls_nb == 1 and string.strcmp(cb_nm, "UnionBase") == 0:
|
||||
cls_header = "union"
|
||||
cls_base_skip = cbi
|
||||
elif cls_nb == 1 and string.strcmp(cb_nm, "REnum") == 0:
|
||||
cls_header = "renum"
|
||||
cls_base_skip = cbi
|
||||
cls_is_virtual: int = _PyiHasDecorator(cls.decorator_list, "VTableT")
|
||||
cls_is_novirtual: int = _PyiHasDecorator(cls.decorator_list, "NoVTable")
|
||||
# Decorator form (@EnumBase class X:) is the same sugar as the base-class form.
|
||||
if string.strcmp(cls_header, "class") == 0:
|
||||
if _PyiHasDecorator(cls.decorator_list, "EnumBase") != 0: cls_header = "enum"
|
||||
elif _PyiHasDecorator(cls.decorator_list, "StructBase") != 0: cls_header = "struct"
|
||||
elif _PyiHasDecorator(cls.decorator_list, "UnionBase") != 0: cls_header = "union"
|
||||
# Decorators
|
||||
if cls.decorator_list is not None:
|
||||
cls_decorator_list: list[ast.AST*]* = cls.decorator_list
|
||||
@@ -612,13 +828,23 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
for dci in range(int(dcn)):
|
||||
ddec: ast.AST* = cls_decorator_list.get(dci)
|
||||
if ddec is not None:
|
||||
ddn: str = _PyiMarkerName(ddec)
|
||||
if ddn is not None and (string.strcmp(ddn, "VTableT") == 0 or string.strcmp(ddn, "NoVTable") == 0 \
|
||||
or string.strcmp(ddn, "EnumBase") == 0 or string.strcmp(ddn, "StructBase") == 0 \
|
||||
or string.strcmp(ddn, "UnionBase") == 0):
|
||||
continue
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "@")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, ddec)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "class ")
|
||||
if string.strcmp(cls_header, "class") == 0:
|
||||
if cls_is_virtual != 0: pos = _PyiEmit(buf, buf_size, pos, "virtual ")
|
||||
elif cls_is_novirtual != 0: pos = _PyiEmit(buf, buf_size, pos, "novirtual ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, "class ")
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, cls_header)
|
||||
if string.strcmp(cls_header, "class") != 0: pos = _PyiEmit(buf, buf_size, pos, " ")
|
||||
if cls.name is not None: pos = _PyiEmit(buf, buf_size, pos, cls.name)
|
||||
|
||||
# PEP 695 type parameters (class Foo[T]:)
|
||||
@@ -635,17 +861,21 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
if tpname is not None: pos = _PyiEmit(buf, buf_size, pos, tpname)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "]")
|
||||
|
||||
# Base classes
|
||||
if cls.bases is not None:
|
||||
# Base classes: plain class keeps all; struct keeps its real parents and drops StructBase.
|
||||
if (string.strcmp(cls_header, "class") == 0 or string.strcmp(cls_header, "struct") == 0) and cls.bases is not None:
|
||||
cls_bases: list[ast.AST*]* = cls.bases
|
||||
bn: sizet = len(cls_bases)
|
||||
if bn > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "(")
|
||||
for bi in range(int(bn)):
|
||||
if bi > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
base: ast.AST* = cls_bases.get(bi)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, base)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
base_first: int = 1
|
||||
for bi in range(int(bn)):
|
||||
if bi == cls_base_skip: continue
|
||||
base: ast.AST* = cls_bases.get(bi)
|
||||
if base is None: continue
|
||||
if base_first != 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "(")
|
||||
base_first = 0
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, base)
|
||||
if base_first == 0: pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
|
||||
pos = _PyiEmit(buf, buf_size, pos, ":\n")
|
||||
|
||||
@@ -689,7 +919,9 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
has_member = 1
|
||||
ici += 1
|
||||
pos = _PyiFunction(buf, buf_size, pos, mfn, indent_level + 1, cls.name)
|
||||
skip_s: int = 1
|
||||
if _PyiIsStaticMethod(mfn) != 0: skip_s = 0
|
||||
pos = _PyiFunction(buf, buf_size, pos, mfn, indent_level + 1, cls.name, _PyiOverloadSuffix(mb, cls_children, mfn, skip_s))
|
||||
has_member = 1
|
||||
|
||||
# Annotated attributes
|
||||
@@ -698,12 +930,43 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||||
tn: ast.Name* = ast.Name*(aa.target)
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level + 1)
|
||||
if _PyiMarkerKind(aa.annotation, "StaticT") != 0: pos = _PyiEmit(buf, buf_size, pos, "static ")
|
||||
if tn.id is not None: pos = _PyiEmit(buf, buf_size, pos, tn.id)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation)
|
||||
if _PyiMarkerKind(aa.annotation, "StaticT") == 2:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, _PyiMarkerPayload(aa.annotation, "StaticT"))
|
||||
else:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation)
|
||||
# 枚举成员(`NAME: T = value`)必须带值:跨模块 EnumTable 只认 .vpi,
|
||||
# 值丢了依赖方的成员常量就跟定义端不一致。普通字段的初值不属于 stub 契约,不写。
|
||||
if string.strcmp(cls_header, "enum") == 0 and aa.value is not None:
|
||||
pos = _PyiEmitEnumValue(buf, buf_size, pos, aa.value)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
has_member = 1
|
||||
|
||||
# 枚举成员 `NAME = value`(含 `A = B = 1` 多目标):源码 AST 里是 Assign
|
||||
# (parser 只把裸名/逗号组改写成 `NAME: State`,带值形式保持 Assign)。
|
||||
# 旧代码只处理 AnnAssign,于是整块带值成员在 .vpi 里丢失 —— `enum FRESULT: pass`
|
||||
# 让依赖方拿不到基类型(只能把枚举名当类名做前向声明)和成员值。
|
||||
# 只对 `enum X:` 生效:普通类体的赋值不属于 stub 契约。
|
||||
elif sk == ast.ASTKind.Assign and string.strcmp(cls_header, "enum") == 0:
|
||||
asg: ast.Assign* = ast.Assign*(stmt)
|
||||
if asg.targets is not None and asg.value is not None:
|
||||
asg_tn: sizet = len(asg.targets)
|
||||
for asg_i in range(int(asg_tn)):
|
||||
asg_t: ast.AST* = asg.targets.get(asg_i)
|
||||
if asg_t is None or asg_t.kind() != ast.ASTKind.Name: continue
|
||||
asg_n: ast.Name* = ast.Name*(asg_t)
|
||||
if asg_n.id is None: continue
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, asg_n.id)
|
||||
# Assign 形式的成员在定义端固定按 i32 处理(ClassGenEnum 的 member_ty_a)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": State")
|
||||
pos = _PyiEmitEnumValue(buf, buf_size, pos, asg.value)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
has_member = 1
|
||||
|
||||
# Nested classes
|
||||
elif sk == ast.ASTKind.ClassDef:
|
||||
ncls: ast.ClassDef* = ast.ClassDef*(stmt)
|
||||
@@ -747,7 +1010,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if tree.children is not None:
|
||||
pre_children: list[ast.AST*]* = tree.children
|
||||
pcn: sizet = len(pre_children)
|
||||
# Pass 1: count t.CDefine declarations (C arrays need a fixed size first)
|
||||
# Pass 1: count t.DefineT declarations (C arrays need a fixed size first)
|
||||
_g_pyi_cdef_count = 0
|
||||
for pci0 in range(int(pcn)):
|
||||
stmt_pc0: ast.AST* = pre_children.get(pci0)
|
||||
@@ -755,9 +1018,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
aa_pc0: ast.AnnAssign* = ast.AnnAssign*(stmt_pc0)
|
||||
if aa_pc0.target is not None and aa_pc0.target.kind() == ast.ASTKind.Name and aa_pc0.annotation is not None:
|
||||
tgt_pc0: ast.Name* = ast.Name*(aa_pc0.target)
|
||||
if tgt_pc0.id is not None and aa_pc0.annotation.kind() == ast.ASTKind.Attribute:
|
||||
ann_pc0: ast.Attribute* = ast.Attribute*(aa_pc0.annotation)
|
||||
if ann_pc0.attr is not None and string.strcmp(ann_pc0.attr, "CDefine") == 0: _g_pyi_cdef_count += 1
|
||||
if tgt_pc0.id is not None and _PyiMarkerKind(aa_pc0.annotation, "DefineT") != 0: _g_pyi_cdef_count += 1
|
||||
if _g_pyi_cdef_count > 0:
|
||||
_g_pyi_cdef_names = (str*)(mb.alloc(sizet(_g_pyi_cdef_count) * 8))
|
||||
_g_pyi_cdef_vals = (int64*)(mb.alloc(sizet(_g_pyi_cdef_count) * 8))
|
||||
@@ -770,14 +1031,12 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
aa_pc: ast.AnnAssign* = ast.AnnAssign*(stmt_pc)
|
||||
if aa_pc.target is not None and aa_pc.target.kind() == ast.ASTKind.Name and aa_pc.annotation is not None:
|
||||
tgt_pc: ast.Name* = ast.Name*(aa_pc.target)
|
||||
if tgt_pc.id is not None and aa_pc.annotation.kind() == ast.ASTKind.Attribute:
|
||||
ann_pc: ast.Attribute* = ast.Attribute*(aa_pc.annotation)
|
||||
if ann_pc.attr is not None and string.strcmp(ann_pc.attr, "CDefine") == 0:
|
||||
if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None:
|
||||
slot_pc: str* = (str*)(uint64(_g_pyi_cdef_names) + cdi * 8)
|
||||
slot_pc[0] = tgt_pc.id
|
||||
_g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value)
|
||||
cdi += 1
|
||||
if tgt_pc.id is not None and _PyiMarkerKind(aa_pc.annotation, "DefineT") != 0:
|
||||
if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None:
|
||||
slot_pc: str* = (str*)(uint64(_g_pyi_cdef_names) + cdi * 8)
|
||||
slot_pc[0] = tgt_pc.id
|
||||
_g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value)
|
||||
cdi += 1
|
||||
|
||||
# File header
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\"\"\"Auto-generated Python stub file\"\"\"\n\n")
|
||||
@@ -861,7 +1120,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
# Generic function templates (def f[T]) emit no vpi: the signature has an
|
||||
# unbound T. Specializations are monomorphized per call site, not here.
|
||||
if fn.type_params is not None: continue
|
||||
pos = _PyiFunction(buf, buf_size, pos, fn, 0, "")
|
||||
pos = _PyiFunction(buf, buf_size, pos, fn, 0, "", _PyiOverloadSuffix(mb, tree_children, fn, 0))
|
||||
|
||||
# Class definition
|
||||
elif k == ast.ASTKind.ClassDef:
|
||||
@@ -874,19 +1133,56 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(aa.target)
|
||||
if nm.id is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation)
|
||||
# If there is a value (e.g. a CDefine constant) emit = value.
|
||||
# CTypedef is special: its value is a type expression, rendered by _PyiTypeStr.
|
||||
# Retired t.TypedefT / t.DefineT markers are written as their source
|
||||
# syntax (`typedef NAME = T` / `define NAME[: T] = value`).
|
||||
is_ctypedef_ann: int = 0
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Name:
|
||||
ctd_nm: ast.Name* = ast.Name*(aa.annotation)
|
||||
if ctd_nm.id is not None and string.strcmp(ctd_nm.id, "CTypedef") == 0: is_ctypedef_ann = 1
|
||||
if is_ctypedef_ann != 0 and aa.value is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " = ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.value)
|
||||
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Constant:
|
||||
ctd_nm0: ast.Name* = ast.Name*(aa.annotation)
|
||||
if ctd_nm0.id is not None and string.strcmp(ctd_nm0.id, "TypedefT") == 0: is_ctypedef_ann = 1
|
||||
cd_kind: int = 0
|
||||
cd_inner: ast.AST* = None
|
||||
if aa.annotation is not None:
|
||||
if aa.annotation.kind() == ast.ASTKind.Name:
|
||||
cd_nm0: ast.Name* = ast.Name*(aa.annotation)
|
||||
if cd_nm0.id is not None and string.strcmp(cd_nm0.id, "DefineT") == 0: cd_kind = 1
|
||||
elif aa.annotation.kind() == ast.ASTKind.Subscript:
|
||||
cd_sub0: ast.Subscript* = ast.Subscript*(aa.annotation)
|
||||
if cd_sub0.value is not None and cd_sub0.value.kind() == ast.ASTKind.Name:
|
||||
cd_vn0: ast.Name* = ast.Name*(cd_sub0.value)
|
||||
if cd_vn0.id is not None and string.strcmp(cd_vn0.id, "DefineT") == 0:
|
||||
cd_kind = 2
|
||||
cd_inner = cd_sub0.slice
|
||||
if is_ctypedef_ann != 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "typedef ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
if aa.value is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " = ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.value)
|
||||
elif cd_kind != 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "define ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
if cd_kind == 2 and cd_inner is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, cd_inner)
|
||||
else:
|
||||
# Storage-class markers become source keywords.
|
||||
st_kw: str = None
|
||||
st_payload: ast.AST* = aa.annotation
|
||||
if _PyiMarkerKind(aa.annotation, "ExportT") != 0:
|
||||
st_kw = "export "
|
||||
st_payload = _PyiMarkerPayload(aa.annotation, "ExportT")
|
||||
elif _PyiMarkerKind(aa.annotation, "State") != 0:
|
||||
st_kw = "extern "
|
||||
st_payload = _PyiMarkerPayload(aa.annotation, "State")
|
||||
elif _PyiMarkerKind(aa.annotation, "StaticT") != 0:
|
||||
st_kw = "static "
|
||||
st_payload = _PyiMarkerPayload(aa.annotation, "StaticT")
|
||||
if st_kw is not None: pos = _PyiEmit(buf, buf_size, pos, st_kw)
|
||||
pos = _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
if st_payload is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, st_payload)
|
||||
if is_ctypedef_ann == 0 and aa.value is not None and aa.value.kind() == ast.ASTKind.Constant:
|
||||
cv: ast.Constant* = ast.Constant*(aa.value)
|
||||
num_buf: bytes = mb.alloc(32)
|
||||
if cv.const_kind == ast.CONST_INT and num_buf is not None:
|
||||
@@ -977,7 +1273,11 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if num_buf_b is not None:
|
||||
viperlib.snprintf(num_buf_b, 32, " = %lld", binop_val_av)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf_b)
|
||||
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Name:
|
||||
# Value suffixes (constants / references) are for `define` and plain
|
||||
# annotated globals only. A typedef's RHS is already a type: without the
|
||||
# is_ctypedef_ann guard a Name RHS got a second suffix
|
||||
# (`typedef pid_t = int32 = int32`, which no longer parses as an alias).
|
||||
elif is_ctypedef_ann == 0 and aa.value is not None and aa.value.kind() == ast.ASTKind.Name:
|
||||
# Name reference constant (e.g. ALIAS = OTHER_CONST):
|
||||
# emit the referenced name for the consumer to resolve locally.
|
||||
name_val: ast.Name* = ast.Name*(aa.value)
|
||||
|
||||
@@ -105,7 +105,10 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
def _overload_sig_text(self, pool: memhub.MemBuddy*, fd: ast.FunctionDef*, trans: HT.Translator*, skip_first: int = 0) -> str:
|
||||
"""返回规范形参签名文本(stdlib 缓冲,调用方 free);失败时已报错并返回 None"""
|
||||
if pool is None or fd is None or trans is None: return None
|
||||
return self._overload_sig_text_ctx(pool, fd, trans._imported_modules, trans._from_imports, trans, skip_first)
|
||||
# hash 与上下文无关:固定传 None(import 表 / translator),只靠全局结构体表解析。
|
||||
# 这样定义端、vpi 写入端(translator 尚未创建)、vpi 读取端天然算出同一个 hash,
|
||||
# 不需要把 import 上下文跨阶段搬运;解析不到的类型会 fail-fast,不会静默算出别的 hash。
|
||||
return self._overload_sig_text_ctx(pool, fd, None, None, None, skip_first)
|
||||
|
||||
# _overload_sig_text_ctx - 同上,但显式传入 import 上下文,供没有 translator 的场景复用:
|
||||
# vpi 生成(写 vpi 时 translator 还没创建)与 vpi 读取(注入 declare 时持有可能为 None 的 trans)。
|
||||
|
||||
Reference in new issue
Block a user