Files
ViperC/App/lib/Stub/StubMerger/StubMergerStore.vp
T

578 lines
32 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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