mirror of
https://github.com/hpd840321/starRiverProperty.git
synced 2026-06-09 16:30:29 +08:00
27c3949045
- .gitignore:显式放行全部 maven-*、scripts、dev-support、frontend、反1、artifacts、历史导出目录
- 新增跟踪:device-manager/device-sdk/legacy-public、davinci-manager、cwos-*、cwos-resource 等源码与附属资源
- davinci FileStorageManagerImpl:Feign Response 关闭、绝对 URL 拉流 SSRF 校验(协议/主机/解析地址)
- davinci OuterCallFeignClient:补充契约说明
- cwos-common-aks AksConstant:final 类 + 私有构造防误实例化
- device-manager DeviceConstant:沿用 DEFAULT_APPLICATIONID 拼写修正
Made-with: Cursor
Former-commit-id: 0a34c76a82
101 lines
3.4 KiB
Python
101 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Compare a JAR's .class entries to decompiled .java tree (Maven src/main/java)."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
|
|
def jar_class_entries(jar_path: Path) -> set[str]:
|
|
"""FQN-like names for each .class (uses /), inner as Outer$Inner."""
|
|
out: set[str] = set()
|
|
with zipfile.ZipFile(jar_path, "r") as zf:
|
|
for name in zf.namelist():
|
|
if not name.endswith(".class") or "META-INF" in name:
|
|
continue
|
|
rel = name[:-6] # drop .class
|
|
out.add(rel.replace("/", "."))
|
|
return out
|
|
|
|
|
|
def java_fqns(src_root: Path) -> set[str]:
|
|
"""Best-effort FQN from path .../src/main/java/a/b/C.java -> a.b.C."""
|
|
src_root = src_root.resolve()
|
|
java_root = src_root / "src/main/java"
|
|
if not java_root.is_dir():
|
|
if src_root.name == "java" and (src_root.parent / "main").exists():
|
|
java_root = src_root
|
|
else:
|
|
return set()
|
|
fqns: set[str] = set()
|
|
for f in java_root.rglob("*.java"):
|
|
rel = f.relative_to(java_root).with_suffix("")
|
|
fqns.add(".".join(rel.parts))
|
|
return fqns
|
|
|
|
|
|
def outer_java_for_class(fqn: str, java_fqns: set[str]) -> str | None:
|
|
"""Map class FQN to expected .java outer name (strip $ inner)."""
|
|
if "$" in fqn:
|
|
outer = fqn.split("$", 1)[0]
|
|
else:
|
|
outer = fqn
|
|
if outer in java_fqns:
|
|
return outer
|
|
return None
|
|
|
|
|
|
def analyze(jar: Path, src_module_roots: list[Path]) -> dict:
|
|
classes = jar_class_entries(jar)
|
|
all_java: set[str] = set()
|
|
for root in src_module_roots:
|
|
if root.is_dir():
|
|
all_java |= java_fqns(root)
|
|
missing_outer: list[str] = []
|
|
for c in sorted(classes):
|
|
if c.startswith("module-info"):
|
|
continue
|
|
if outer_java_for_class(c, all_java) is None:
|
|
missing_outer.append(c)
|
|
# "extra" java: outer types not present as any class in jar (rough)
|
|
jar_outers = {x.split("$", 1)[0] for x in classes if not x.startswith("module-info")}
|
|
extra_java = sorted(x for x in all_java if x not in jar_outers and "$" not in x)
|
|
return {
|
|
"jar": str(jar),
|
|
"class_count": len(classes),
|
|
"java_outer_count": len(all_java),
|
|
"missing_classes": missing_outer,
|
|
"possibly_extra_sources": extra_java[:200], # cap
|
|
"possibly_extra_sources_total": len(extra_java),
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("jar", type=Path)
|
|
ap.add_argument("src_roots", nargs="+", type=Path, help="Module dirs containing src/main/java")
|
|
args = ap.parse_args()
|
|
r = analyze(args.jar, args.src_roots)
|
|
print(f"JAR: {r['jar']}")
|
|
print(f"Classes in JAR: {r['class_count']}; outer .java types under src/main/java: {r['java_outer_count']}")
|
|
miss = r["missing_classes"]
|
|
print(f"Missing (no matching outer .java): {len(miss)}")
|
|
for line in miss[:80]:
|
|
print(f" - {line}")
|
|
if len(miss) > 80:
|
|
print(f" ... and {len(miss) - 80} more")
|
|
ex = r["possibly_extra_sources"]
|
|
tot = r["possibly_extra_sources_total"]
|
|
print(f"Possibly extra .java (not as outer in JAR): {tot} (showing up to {len(ex)})")
|
|
for line in ex[:40]:
|
|
print(f" + {line}")
|
|
if tot > 40:
|
|
print(f" ...")
|
|
return 1 if miss else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|