578 lines
32 KiB
Plaintext
578 lines
32 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import mlirlite
|
||
import win32.win32file as win32file
|
||
import win32.win32base as win32base
|
||
import win32.fileio as fileio
|
||
import viperlib
|
||
import sys
|
||
import ast
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.IncludesScanner as IncludesScanner
|
||
import lib.core.Gen.Translator as HandlesTranslator
|
||
import lib.Phase.BuildPipeline as BuildPipeline
|
||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||
import lib.Projectrans.Utils as Utils
|
||
import lib.Projectrans.Config as Config
|
||
|
||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||
import lib.Stub.StubMerger as StubMerger
|
||
|
||
|
||
# ============================================================
|
||
# StubMergerStore - global SHA1 map store (sha1 -> module name / rel path) read/write
|
||
|
||
# Import-context accounting, single source of truth: AST import statements map to
|
||
# imported_modules / from_imports; both translate-time and vpi-injection use these helpers.
|
||
|
||
class ImportCtx:
|
||
"""Import context container: two space-separated lists (ImportedModules / FromImports)"""
|
||
ImportedModules: str
|
||
FromImports: str
|
||
|
||
|
||
# ============================================================
|
||
# AppendImportedModule - append module name to space-separated list (pure bookkeeping)
|
||
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
||
"""Append a module name to a space-separated list (pool-allocated); returns the new list"""
|
||
if name is None: return cur
|
||
if cur is None:
|
||
nlen: sizet = string.strlen(name)
|
||
buf: str = pool.alloc(nlen + 1)
|
||
if buf is not None: string.strcpy(buf, name)
|
||
return buf
|
||
old_len: sizet = string.strlen(cur)
|
||
name_len: sizet = string.strlen(name)
|
||
buf2: str = pool.alloc(old_len + 1 + name_len + 1)
|
||
if buf2 is not None:
|
||
string.strcpy(buf2, cur)
|
||
buf2[old_len] = ' '
|
||
string.strcpy(buf2 + old_len + 1, name)
|
||
return buf2
|
||
|
||
|
||
# AppendFromImport - append a from-import mapping ("local:mod[:orig]") to a space-separated list.
|
||
# Exact-size allocation (no fixed 256 buffer) so overlong entries are never silently truncated.
|
||
def AppendFromImport(pool: memhub.MemBuddy*, cur: str, local: str, mod: str, orig: str) -> str:
|
||
"""Append a from-import mapping (pool-allocated); returns the new list"""
|
||
if local is None or mod is None: return cur
|
||
llen: sizet = string.strlen(local)
|
||
mlen: sizet = string.strlen(mod)
|
||
use_orig: int = 0
|
||
olen: sizet = 0
|
||
if orig is not None and string.strcmp(orig, local) != 0:
|
||
use_orig = 1
|
||
olen = string.strlen(orig)
|
||
# local ':' mod [':' orig] '\0'
|
||
need: sizet = llen + 1 + mlen + 1
|
||
if use_orig != 0: need += 1 + olen
|
||
entry: str = pool.alloc(need)
|
||
if entry is None: return cur
|
||
string.strcpy(entry, local)
|
||
entry[llen] = ':'
|
||
string.strcpy(entry + llen + 1, mod)
|
||
if use_orig != 0:
|
||
entry[llen + 1 + mlen] = ':'
|
||
string.strcpy(entry + llen + 1 + mlen + 1, orig)
|
||
if cur is None: return entry
|
||
old_len: sizet = string.strlen(cur)
|
||
entry_len: sizet = string.strlen(entry)
|
||
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
|
||
if buf is not None:
|
||
string.strcpy(buf, cur)
|
||
buf[old_len] = ' '
|
||
string.strcpy(buf + old_len + 1, entry)
|
||
return buf
|
||
|
||
|
||
# ============================================================
|
||
# AllocImportCtx - build the accounting container from two existing context strings
|
||
def AllocImportCtx(pool: memhub.MemBuddy*, imported_modules: str, from_imports: str) -> ImportCtx*:
|
||
"""Build an import context container (pool-allocated); either list may be None"""
|
||
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
|
||
if ctx is None: return None
|
||
ctx.ImportedModules = imported_modules
|
||
ctx.FromImports = from_imports
|
||
return ctx
|
||
|
||
|
||
# NotePlainImport - record `import X` / `import X as Y` in place on ctx.
|
||
# Aliased: append alias Y + from-import "Y:X" (sole source for reverse module lookup).
|
||
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, name: str, asname: str) -> int:
|
||
"""Record a single import in place on ctx; returns 0"""
|
||
if ctx is None or name is None: return 1
|
||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
|
||
if asname is not None:
|
||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname)
|
||
ctx.FromImports = AppendFromImport(pool, ctx.FromImports, asname, name, None)
|
||
else:
|
||
dot_ptr: str = string.strstr(name, ".")
|
||
if dot_ptr is not None:
|
||
pkg_len: sizet = sizet(uint64(dot_ptr) - uint64(name))
|
||
if pkg_len > 0 and pkg_len < 256:
|
||
pkg_buf: str = pool.alloc(pkg_len + 1)
|
||
if pkg_buf is not None:
|
||
string.strncpy(pkg_buf, name, pkg_len)
|
||
pkg_buf[pkg_len] = '\0'
|
||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, pkg_buf)
|
||
return 0
|
||
|
||
|
||
# PopulateSha1MapStore - build sha1 -> module_name map from ScanResult.Entries into the global store.
|
||
# Memory via stdlib.malloc (global, not freed with mbuddy). Returns entry count, -1 on failure.
|
||
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
|
||
"""Populate the global SHA1 store from a scan result; returns the entry count or -1"""
|
||
if scan_result is None: return -1
|
||
# free the old store
|
||
if StubMerger._sha1_store_arr is not None: stdlib.free(StubMerger._sha1_store_arr)
|
||
if StubMerger._sha1_store_mod is not None: stdlib.free(StubMerger._sha1_store_mod)
|
||
if StubMerger._sha1_store_rel is not None: stdlib.free(StubMerger._sha1_store_rel)
|
||
if StubMerger._sha1_store_incdir is not None: stdlib.free(StubMerger._sha1_store_incdir)
|
||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 CDefine 按最小位宽物化为 i16,
|
||
# 两侧都 i16 的乘积(1024*256 / 1024*512)会静默回绕成 0;显式 sizet() 把乘数加宽到 64 位,结果与旧「提升到 int」一致。
|
||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return -1
|
||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||
StubMerger._sha1_store_count = 0
|
||
p_bad: int = 0
|
||
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||
for i in range(int(scan_result.Count)):
|
||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: VLogger.error("includes SHA1 store exceeded the limit; aborting", "StubMerger")
|
||
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
|
||
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea)
|
||
if ent is None or ent.Sha1 is None or ent.RelPath is None:
|
||
p_bad += 1
|
||
continue
|
||
# build full path includes/{RelPath} (needed by _PathToModuleName)
|
||
rp_len: sizet = string.strlen(ent.RelPath)
|
||
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
|
||
if full_path_buf is None:
|
||
p_bad += 1
|
||
continue
|
||
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath)
|
||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||
stdlib.free(full_path_buf)
|
||
if mod_name is not None:
|
||
idx: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||
string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1)
|
||
idx2: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||
mn_len: sizet = string.strlen(mod_name)
|
||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63)
|
||
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
|
||
stdlib.free(mod_name)
|
||
# save rel_path (used by Phase2 3.5 to compile missing includes)
|
||
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
|
||
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||
# save owning includes root dir (Phase2 pre-register / on-demand path building)
|
||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||
if ent.IncDir is not None:
|
||
icd_len: sizet = string.strlen(ent.IncDir)
|
||
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir, StubMerger.MAX_INC_DIR_LEN - 1)
|
||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||
StubMerger._sha1_store_count += 1
|
||
if p_bad > 0: VLogger.error(f"includes SHA1 store has {p_bad} failed entries", "StubMerger")
|
||
return StubMerger._sha1_store_count
|
||
# AppendToSha1MapStore - append an App module entry to the global SHA1 store (Phase2 use);
|
||
# auto-initializes if empty, skips on duplicate SHA1. Returns 0 ok, 1 failed.
|
||
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||
"""Append an entry to the global SHA1 store (Phase2)"""
|
||
if sha1 is None or rel_path is None: return 1
|
||
# init store if empty (Phase2 may run before Phase1)
|
||
if StubMerger._sha1_store_arr is None:
|
||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||
StubMerger._sha1_store_count = 0
|
||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||
# module name: rel_path "lib/core/StubMerger.vp" -> "lib.core.StubMerger"
|
||
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
|
||
if full_path_buf is None: return 1
|
||
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
|
||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||
stdlib.free(full_path_buf)
|
||
if mod_name is None: return 1
|
||
# Dedup only when both SHA1 and module name match: the same SHA1 may be registered first
|
||
# under a full-path name; the App short name must stay or exact lookup breaks.
|
||
for i in range(int(StubMerger._sha1_store_count)):
|
||
sidx_d: sizet = sizet(i) * 17
|
||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||
midx_d: sizet = sizet(i) * 64
|
||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
|
||
stdlib.free(mod_name)
|
||
return 0
|
||
# write SHA1
|
||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||
# write module name
|
||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||
mn_len: sizet = string.strlen(mod_name)
|
||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||
stdlib.free(mod_name)
|
||
# write rel path
|
||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||
rp_len: sizet = string.strlen(rel_path)
|
||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||
# App sources belong to no includes dir: leave IncDir empty (Phase2 falls back to includes_dir)
|
||
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
|
||
StubMerger._sha1_store_count += 1
|
||
return 0
|
||
# AddIncludeToSha1MapStore - register one includes entry into the global SHA1 store (on-demand).
|
||
# Unreferenced includes stay out; App sources still use AppendToSha1MapStore (empty inc_dir).
|
||
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
|
||
"""Register one includes entry in the global SHA1 store (on-demand scan)"""
|
||
if sha1 is None or rel_path is None or mod_name is None: return 1
|
||
# init store if empty
|
||
if StubMerger._sha1_store_arr is None:
|
||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||
StubMerger._sha1_store_count = 0
|
||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||
# dedup when both SHA1 and module name match (aligned with AppendToSha1MapStore)
|
||
for i in range(int(StubMerger._sha1_store_count)):
|
||
sidx_d: sizet = sizet(i) * 17
|
||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||
midx_d: sizet = sizet(i) * 64
|
||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0: return 0
|
||
# write SHA1
|
||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||
# write module name
|
||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||
mn_len: sizet = string.strlen(mod_name)
|
||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||
# write rel path
|
||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||
rp_len: sizet = string.strlen(rel_path)
|
||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||
# write owning includes root (non-empty = includes entry; App entries empty)
|
||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||
if inc_dir is not None:
|
||
icd_len: sizet = string.strlen(inc_dir)
|
||
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
|
||
else:
|
||
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1)
|
||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||
else: StubMerger._sha1_store_incdir[idx_inc] = '\0'
|
||
StubMerger._sha1_store_count += 1
|
||
return 0
|
||
# ============================================================
|
||
# GetSha1StoreCount - return the global store entry count
|
||
def GetSha1StoreCount() -> int:
|
||
"""Return the global SHA1 store entry count"""
|
||
return StubMerger._sha1_store_count
|
||
# LookupSha1ByModName - find SHA1 by module name; falls back to "{mod_name}.__init__".
|
||
# Returns a pointer into the store (do not free), None if not found.
|
||
def LookupSha1ByModName(mod_name: str) -> str:
|
||
"""Look up a SHA1 by module name (exact then package fallback); delegates to _FindSha1ByModName"""
|
||
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
|
||
|
||
|
||
# Generic-class owner table (generic class name -> defining module SHA1), REPL-session only;
|
||
# needed because REPL skips generic templates, so generic subscript resolution would otherwise miss.
|
||
define GENERIC_OWNER_MAX = 32
|
||
_generic_owner_names: bytes = None
|
||
_generic_owner_sha1s: bytes = None
|
||
_generic_owner_count: int = 0
|
||
|
||
|
||
def ResetGenericOwners() -> None:
|
||
"""Clear the generic-class owner table (called once at REPL session start)"""
|
||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||
if _generic_owner_names is not None:
|
||
stdlib.free(_generic_owner_names)
|
||
_generic_owner_names = None
|
||
if _generic_owner_sha1s is not None:
|
||
stdlib.free(_generic_owner_sha1s)
|
||
_generic_owner_sha1s = None
|
||
_generic_owner_count = 0
|
||
|
||
|
||
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
||
"""Register generic-class-name to defining-module SHA1; 1 = registered, 0 = failed"""
|
||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||
if cls_name is None or mod_sha1 is None: return 0
|
||
if cls_name[0] == '\0' or mod_sha1[0] == '\0': return 0
|
||
if _generic_owner_names is None:
|
||
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
|
||
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
|
||
if _generic_owner_names is None or _generic_owner_sha1s is None: return 0
|
||
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
|
||
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
|
||
_generic_owner_count = 0
|
||
i_go: int
|
||
for i_go in range(int(_generic_owner_count)):
|
||
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
|
||
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0: return 1
|
||
return 0
|
||
if _generic_owner_count >= GENERIC_OWNER_MAX: return 0
|
||
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
|
||
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
|
||
_generic_owner_count += 1
|
||
return 1
|
||
|
||
|
||
def LookupGenericOwnerSha1(cls_name: str) -> str:
|
||
"""Look up the defining-module SHA1 by generic class name (internal pointer, no free)"""
|
||
if cls_name is None or _generic_owner_names is None: return None
|
||
i_lg: int
|
||
for i_lg in range(int(_generic_owner_count)):
|
||
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0: return _generic_owner_sha1s + sizet(i_lg) * 17
|
||
return None
|
||
# ============================================================
|
||
# LookupSha1ByModNameSuffix - find SHA1 by suffix; tries exact, suffix, reverse-suffix. None if absent.
|
||
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||
"""Look up a module SHA1 by suffix match"""
|
||
if mod_suffix is None: return None
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||
if StubMerger._sha1_store_count <= 0: return None
|
||
suffix_len_sf: sizet = string.strlen(mod_suffix)
|
||
if suffix_len_sf == 0: return None
|
||
i_sf: int
|
||
for i_sf in range(int(StubMerger._sha1_store_count)):
|
||
idx_sf: sizet = sizet(i_sf) * 64
|
||
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
|
||
mod_len_sf: sizet = string.strlen(mod_name_sf)
|
||
if mod_len_sf == 0: continue
|
||
# exact match
|
||
if mod_len_sf == suffix_len_sf:
|
||
if string.strcmp(mod_name_sf, mod_suffix) == 0:
|
||
sidx_sf: sizet = sizet(i_sf) * 17
|
||
return StubMerger._sha1_store_arr + sidx_sf
|
||
# suffix match: mod_name ends with "." + mod_suffix
|
||
if mod_len_sf > suffix_len_sf:
|
||
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
|
||
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
|
||
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
|
||
sidx2_sf: sizet = sizet(i_sf) * 17
|
||
return StubMerger._sha1_store_arr + sidx2_sf
|
||
# reverse suffix match: mod_suffix ends with "." + mod_name
|
||
if suffix_len_sf > mod_len_sf:
|
||
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
|
||
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
|
||
if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0:
|
||
sidx_rev_sf: sizet = sizet(i_sf) * 17
|
||
return StubMerger._sha1_store_arr + sidx_rev_sf
|
||
return None
|
||
# ============================================================
|
||
# LookupSha1ByAlias - resolve SHA1 from from_imports alias "local:module[:orig]" entries.
|
||
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||
"""Resolve a module SHA1 from a from-import alias mapping (import X as Y)"""
|
||
if from_imports is None or alias is None: return None
|
||
alias_len: sizet = string.strlen(alias)
|
||
if alias_len == 0: return None
|
||
total_len: sizet = string.strlen(from_imports)
|
||
ci: sizet = 0
|
||
while ci < total_len:
|
||
# skip leading spaces
|
||
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||
if ci >= total_len: break
|
||
# entry starts at ci; find ':' (end of local_name)
|
||
colon: sizet = ci
|
||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||
local_len: sizet = colon - ci
|
||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||
# compare local_name == alias
|
||
Matched: int = 1
|
||
for li in range(int(alias_len)):
|
||
if from_imports[ci + li] != alias[li]:
|
||
Matched = 0
|
||
break
|
||
if Matched == 1:
|
||
# extract module (from colon+1 to next ':' or space)
|
||
m_start: sizet = colon + 1
|
||
m_end: sizet = m_start
|
||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||
if m_end > m_start:
|
||
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
||
if mbuf is not None:
|
||
string.strncpy(mbuf, from_imports + m_start, m_end - m_start)
|
||
mbuf[m_end - m_start] = '\0'
|
||
res: str = LookupSha1ByModName(mbuf)
|
||
stdlib.free(mbuf)
|
||
return res
|
||
return None
|
||
# advance to next entry
|
||
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||
return None
|
||
# ============================================================
|
||
# LookupModNameByAlias - return real module name X for alias Y (from_imports "Y:X"; caller frees).
|
||
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||
"""Resolve the real module name for an alias from from_imports (import X as Y -> Y:X)"""
|
||
if from_imports is None or alias is None: return None
|
||
alias_len: sizet = string.strlen(alias)
|
||
if alias_len == 0: return None
|
||
total_len: sizet = string.strlen(from_imports)
|
||
ci: sizet = 0
|
||
while ci < total_len:
|
||
# skip leading spaces
|
||
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||
if ci >= total_len: break
|
||
# entry starts at ci; find ':' (end of local_name)
|
||
colon: sizet = ci
|
||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||
local_len: sizet = colon - ci
|
||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||
# compare local_name == alias
|
||
Matched: int = 1
|
||
for li in range(int(alias_len)):
|
||
if from_imports[ci + li] != alias[li]:
|
||
Matched = 0
|
||
break
|
||
if Matched == 1:
|
||
# extract module (from colon+1 to next ':' or space)
|
||
m_start: sizet = colon + 1
|
||
m_end: sizet = m_start
|
||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||
if m_end > m_start:
|
||
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
||
if mbuf is not None:
|
||
string.strncpy(mbuf, from_imports + m_start, m_end - m_start)
|
||
mbuf[m_end - m_start] = '\0'
|
||
return mbuf
|
||
return None
|
||
# advance to next entry
|
||
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||
return None
|
||
# ============================================================
|
||
# PopulateIncludesDeps - store includes deps in memory (deps_str space-separated). 0 ok, non-0 fail.
|
||
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||
"""Store includes-file dependencies in memory"""
|
||
if sha1 is None: return 1
|
||
# lazy init
|
||
if StubMerger._includes_deps_sha1 is None:
|
||
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(MAX_DEPS_STR_LEN) 会回绕成 0,显式加宽到 64 位
|
||
StubMerger._includes_deps_str = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 1
|
||
string.memset(StubMerger._includes_deps_sha1, 0, StubMerger.MAX_INCLUDES * 17)
|
||
string.memset(StubMerger._includes_deps_str, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||
StubMerger._includes_deps_count = 0
|
||
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES: return 1
|
||
# dedup: skip if SHA1 already present
|
||
for i in range(int(StubMerger._includes_deps_count)):
|
||
sidx: sizet = sizet(i) * 17
|
||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: return 0
|
||
# write SHA1
|
||
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
|
||
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
|
||
# write deps string
|
||
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
|
||
if deps_str is not None:
|
||
dl: sizet = string.strlen(deps_str)
|
||
if dl < StubMerger.MAX_DEPS_STR_LEN: string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
|
||
else: string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
|
||
else: StubMerger._includes_deps_str[idx2] = '\0'
|
||
StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1
|
||
return 0
|
||
# ============================================================
|
||
# LookupIncludesDeps - look up includes deps by SHA1 in memory; returns space-separated deps or None.
|
||
def LookupIncludesDeps(sha1: str) -> str:
|
||
"""Look up includes-file dependencies in memory"""
|
||
if sha1 is None or StubMerger._includes_deps_sha1 is None: return None
|
||
if StubMerger._includes_deps_count <= 0: return None
|
||
for i in range(int(StubMerger._includes_deps_count)):
|
||
sidx: sizet = sizet(i) * 17
|
||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
||
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
|
||
return StubMerger._includes_deps_str + idx2
|
||
return None
|
||
def GetSha1StoreArrPtr() -> bytes:
|
||
"""Return the SHA1 array pointer (17 bytes per entry)"""
|
||
return StubMerger._sha1_store_arr
|
||
def GetSha1StoreModArrPtr() -> bytes:
|
||
"""Return the module-name array pointer (64 bytes per entry)"""
|
||
return StubMerger._sha1_store_mod
|
||
def GetSha1StoreRelArrPtr() -> bytes:
|
||
"""Return the rel-path array pointer (StubMerger.MAX_REL_PATH_LEN bytes per entry)"""
|
||
return StubMerger._sha1_store_rel
|
||
def GetSha1StoreIncdirArrPtr() -> bytes:
|
||
"""Return the owning includes-root array pointer (empty string = not in any includes dir)"""
|
||
return StubMerger._sha1_store_incdir
|
||
# ============================================================
|
||
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
|
||
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
||
"""Get the global store array pointers and entry count; returns -1 on failure"""
|
||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
|
||
sp: rawptr[t.CPtr] = out_sha1_arr
|
||
if sp is not None: sp[0] = StubMerger._sha1_store_arr
|
||
mp: rawptr[t.CPtr] = out_mod_arr
|
||
if mp is not None: mp[0] = StubMerger._sha1_store_mod
|
||
rp: rawptr[t.CPtr] = out_rel_arr
|
||
if rp is not None: rp[0] = StubMerger._sha1_store_rel
|
||
return StubMerger._sha1_store_count
|
||
|
||
# ============================================================
|
||
# LookupSha1RelPath - return the read-only rel_path pointer for a SHA1 (no free), None if missing.
|
||
def LookupSha1RelPath(sha1: str) -> str:
|
||
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
|
||
if sha1 is None: return None
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: return None
|
||
for i in range(int(StubMerger._sha1_store_count)):
|
||
sidx: sizet = sizet(i) * 17
|
||
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
|
||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||
return StubMerger._sha1_store_rel + ridx
|
||
return None
|
||
|
||
|
||
# ============================================================
|
||
# LookupModNameBySha1 - reverse lookup module name by SHA1 (read-only ptr, no free); first hit wins.
|
||
def LookupModNameBySha1(sha1: str) -> str:
|
||
"""Return the read-only module-name pointer for a SHA1, or None if missing"""
|
||
if sha1 is None or sha1[0] == '\0': return None
|
||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||
for i in range(int(StubMerger._sha1_store_count)):
|
||
sidx_mb: sizet = sizet(i) * 17
|
||
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
|
||
midx_mb: sizet = sizet(i) * 64
|
||
return StubMerger._sha1_store_mod + midx_mb
|
||
return None
|