Skip to content

feat: navigate into the jars old projects keep in their tree - #6

Merged
1lck merged 1 commit into
mainfrom
feat/legacy-project-jars
Nov 13, 2026
Merged

1lck merged 1 commit into
mainfrom
feat/legacy-project-jars

Conversation

@1lck

@1lck 1lck commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

为什么

上一个 PR 只在 Nacos 和 ruoyi-vue-pro 这两个新的 Spring/Maven 项目上测过。这次补测了小众框架和老项目,各取 1500 个随机位置:JFinal、Nutz、Solon、Apache Ant、Struts 1、JForum 2。跳到的目标逐个核对过,全部落在对应名字的声明行上,没有跳错的。但发现两个问题:

  1. JForum 2 一个依赖都读不到。它是典型的老 Ant 项目,没有 pom,62 个 jar 直接提交在 WEB-INF/lib 和 lib/ 里,由 Eclipse 的 .classpath 列出。插件只读 Maven,所以 Logger、HttpSession、FreeMarker 的类都跳不过去。
  2. 一条声明里逗号后面的变量找不到,例如 for (int i = 0, size = key.length(); i < size; i++) 里的 size、int S2, S5; 里的 S5。

改动

读工作区里的 jar(新文件 internal/deps/tree.go)

  • 先按 Eclipse .classpath 里 kind="lib" 条目的顺序读,kind="var" 的 M2_REPO/... 条目解析到本地仓库,sourcepath 当作源码;/Other/lib/x.jar 这种指向别的 Eclipse 项目的路径,按同级目录找。
  • 再读路径往上三层内有 lib 或 libs 目录的 jar,WEB-INF/lib、lib/optional 都算。测试资源里的 jar(比如 ant 的 src/etc/testcases/.../*.jar)、gradle-wrapper.jar,以及 target/build/out/bin 里的 jar 都不算。
  • 工作区根目录和每个 build.xml、.classpath、.project 所在目录下的 lib、libs、WEB-INF/lib、WebContent/WEB-INF/lib、WebRoot/WEB-INF/lib、web/WEB-INF/lib、src/main/webapp/WEB-INF/lib,即使被 .gitignore 忽略也会读,因为 Ant/Ivy 常把 jar 下载到这些位置。
  • 源码取 .classpath 的 sourcepath,否则取同目录的 -sources.jar、-src.jar、-sources.zip、-src.zip。
  • 最多 2000 个 jar。jar 只读,不会被修改;副本和 Maven 依赖一样,写到 ${stateDir}/dependency-sources。

源码包必须按包名放(internal/deps/jars.go)

  • 采用源码包之前,先确认 class jar 里至少有一个类在源码包里的对应位置上。像 lib-1.0/src/com/... 这样整个源码树打成的 zip,会把每个类的全名都推错,结果反而跳不过去,所以这种情况改用 class 存根。Maven 的 -sources.jar 同样走这个检查。
  • 在 Nacos 和 ruoyi 上逐个检查了所有带源码包的 jar:只拒掉两个里面一个 .java 都没有的空源码包(grpc-netty-shaded、ojdbc8),和旧代码的处理方式一样。

依赖开关(workspace、lsp、main)

  • 新增 Options.Dependencies 作为依赖库跳转的开关。以前要配了 Maven 仓库路径才会打开依赖库跳转,所以只有 Maven 项目能跳;现在 MavenRepo 为空只是不读 Maven 依赖,工作区里的 jar 照样读。--no-dependencies 两者都关掉。
  • pom.xml、.classpath、任一个工作区 jar 或它的源码包变了,下一次扫描就会重新读取依赖(depInputs)。
  • 日志会分开写出 Maven 依赖和工作区 jar 各有多少个。

一条声明里的多个变量(internal/codenav/resolve.go)

  • findLocal 记下当前这条声明的类型和所在的括号深度。逗号后面、在同一层括号里的变量名沿用这个类型,遇到 ;、离开这层括号或进入代码块时清掉。
  • 初始化表达式里的调用参数在更深一层括号里,所以 M x = make(s2, b), z; 里的 b 仍然是字段,不会被当成局部变量;方法参数和 for (X x : xs) 也不受影响。

其他

  • cmd/nav-bench 新增 -under 参数,用来指定源码根目录(比如 src/、src/main/org/),适配非标准布局的项目。
  • README 新增“小众框架和老项目”一节的实测,更新“怎么工作”和“限制”;同步更新了 docs/releases/v0.0.1.md 和 codenav/UPSTREAM.md。

实测(Apple M4,各 1500 个随机位置,种子 2)

项目 改动前 改动后
JForum 2(Ant,没有 pom,读到 60 个工作区 jar) 1438(95.9%) 1490(99.3%)
JFinal 1447 1452
Nutz 1460 1467
Apache Ant(另读到 lib/optional 里的 3 个 jar) 1483 1487
Solon 1447 1448
Struts 1 1462 1462
Nacos / ruoyi-vue-pro 1481 / 1476 1481 / 1476
  • 新旧两个版本在 Nacos 和 ruoyi 上交替各跑两次:跳转 p50、查找引用和物理内存都在噪声范围内,Nacos 67–73 对 71 MiB,ruoyi 91 对 89–91 MiB。
  • 六个小项目的物理内存都在 20–36 MiB。

测试

  • go test ./... 和 go test -race ./... 全部通过,gofmt 和 vet 干净。
  • 新增:
    • TestTreeJarsOfAnAntAndEclipseProject:.classpath 的顺序、sourcepath、M2_REPO、缺失的条目、WEB-INF/lib 和旁边的 -sources.jar、lib/optional、被 gitignore 的 lib/ivy;测试 jar、wrapper、build/lib 被排除。
    • TestSourcesNotRootedAtThePackagesAreIgnored:源码树打的 zip 改用存根,按包名放的源码包照常使用。
    • TestDefinitionIntoJarsTheTreeKeeps:没有 pom 的项目跳进 WEB-INF/lib 的 jar;之后往 lib/ 加 jar,下一次扫描就能找到,并且打开的是源码包;WEB-INF 前后哈希一致。
    • TestEveryVariableOfADeclarationIsALocal:旧代码上 6 个用例失败 5 个。

不在本 PR

🤖 Generated with Claude Code

Tested on niche frameworks and old projects (JFinal, Nutz, Solon, Ant,
Struts 1, JForum 2): every target lands on its declaration, but JForum 2,
an Ant project with its jars committed to WEB-INF/lib and no pom, found
none of its dependencies, and the second variable of a declaration of
several (int i = 0, size = s.length()) was never found.

- deps.TreeJars lists the jars a working tree keeps: the lib and M2_REPO
  entries of Eclipse .classpath files in their order (with sourcepath),
  then jars with a lib or libs directory up to three levels above them
  (WEB-INF/lib, lib/optional), probing the usual lib directories next to
  build.xml, .classpath and .project even when .gitignore leaves them
  out. Test resource jars, the Gradle wrapper and build output are not
  dependencies. A -sources.jar or -src.zip next to a jar is its source.
- A source archive is used only when one of the jar's classes is at its
  place in it: a zip of a source tree would name every class wrongly, so
  the class jar's stubs are used instead.
- Dependencies are now switched on by their own option rather than by a
  Maven repository being set, and a change to a .classpath file or to any
  tree jar rereads them.
- findLocal gives the variables after the commas of a declaration the
  first one's type; names in the initializers' arguments are deeper in
  parentheses and stay unaffected.
- nav-bench -under samples another source root (src/, src/main/org/).

JForum 2 resolves 99.3% of 1,500 positions (was 95.9%); JFinal, Nutz and
Ant gain 4-7 positions each; Nacos and ruoyi-vue-pro are unchanged, and an
A/B run shows no change in latency or memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@1lck
1lck merged commit 4bc54ea into main Nov 13, 2026
3 checks passed
@1lck
1lck deleted the feat/legacy-project-jars branch November 13, 2026 04:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant