Skip to content

v0.1.3

Latest

Choose a tag to compare

@github-actions github-actions released this 25 Oct 06:45
v0.1.3
6cc5f0f

更新内容

正确性(本版重点)

  • 条件表达式不再重复求值已存储的调用。 x = f(); if (x == null) 的字节码是 dup; astore; ifnull;旧实现把调用表达式留在操作数栈上,条件里又打印一次 f()——while (reader.readLine() != null) 与循环体各读一次,MailcapFile 每轮消耗 3 行、AppleScriptEngine 2 行。现在分支读取刚写入的局部变量,且仅限紧随的条件分支(允许中间隔常量比较,如 dup; istore; iconst_m1; if_icmpeq),不存在「局部变量已被重新赋值」的风险。
  • 布尔类型推断收紧。 裸 v == 0 不再单独作为 boolean 的证据:javac 对 int 与 boolean 使用同一条测试指令,int p = ports[i]; if (p == 0) 以前会被声明成 boolean p(boolean p = ports[i] 根本无法编译)。现在要求 0/1 值域证据,并让明确的 int 生产点(调用结果、数组元素、int 字段/强转、算术)否决 boolean;v = v & flag、拷贝等歧义形态既不证明也不否决。
  • 量化:把 rt.jar 中受影响的类重新交给 javac(--release 8,rt.jar 作 classpath),327 个文件的编译错误减少、净减 2,695 条;仅 1 个原本已损坏的文件增加 2 条;没有新增语法错误文件(jclassd 全量语法门禁仍是基线的 1 个——ASM 类名含 (),任何 Java 源码都无法拼写)。

Java 27 / 28 支持

  • 解析 class 文件版本 71/72(Java 27/28),语料补入 Temurin 27+35 与 28-beta+18;完整 JDK 27/28 镜像反编译 0 失败,features 套件在两版各 10/10。

其他缺陷修复

  • record 规范构造器不再输出非法的 super();。
  • 被调用、却被 --synthetic 隐藏的 helper 不再删除(JetBrains 的 $$$reportNull$$$0)。
  • throw 的合并值改用池感知的 LUB(此前退化成 Object)。
  • 数组 checkcast 按描述符解析类型(此前 76 个单元把 [Ljava/lang/Class; 当成类名)。

性能

  • CLI 改用 mimalloc 全局分配器;内部迁移到 jdc-core 0.2.17(Arc<str> IR、Fx 哈希)。
  • 嵌套类反向引用索引改为单飞构建:此前每个渲染 worker 各建一次、全部争用类池互斥锁(JDK 28 镜像系统态时间 19.3s→11.1s)。
  • 18 核 Apple M5 Pro:rt.jar 并行 2.3s / 686MB(单线程 9.4s / 427MB);JDK 28 完整镜像(15,039 个单元)4.7s / 978MB(单线程 23.3s / 525MB)。
  • 对比 Vineflower / CFR / Procyon / fernflower:rt.jar 上约 11× / 23× / 48× / 93×,JDK 28 镜像上约 31× / 106× / 154×(Vineflower 无法完成该负载)。
  • 输出确定性保持不变:并行与 JCDC_THREADS=1 逐字节相同。

验证:features 59/59 × 两条结构化路径;语料 spot 与上一版逐方法字节一致;rt.jar 12,609 个单元全量、无 panic;jclassd 全量语法门禁 1 个(既有基线)。

What's changed (English)

Correctness (the headline of this release)

  • Conditions no longer re-evaluate a call the store already performed. x = f(); if (x == null) compiles to dup; astore; ifnull; the old builder left the CALL on the operand stack, so the branch printed f() a second time — while (reader.readLine() != null) above a body reading the same stream again (MailcapFile consumed three lines per iteration, AppleScriptEngine two). The store now rewrites the surviving duplicate to a read of the local it just wrote, restricted to the immediately-following conditional branch (constants in between are fine, e.g. dup; istore; iconst_m1; if_icmpeq), so a reassignment can never make the local stale.
  • Boolean inference tightened. A bare v == 0 is not boolean evidence on its own: javac has no distinct test opcode, so int p = ports[i]; if (p == 0) used to be declared boolean p — which does not even compile (boolean p = ports[i]). A boolean inference now also requires 0/1 value-range evidence, and an unambiguous int producer (call result, array element, int field/cast, arithmetic) vetoes it; ambiguous shapes (v = v & flag, copies) neither prove nor veto.
  • Measured by recompiling the affected rt.jar classes with javac (--release 8, rt.jar as classpath): 327 files with fewer errors, −2,695 total; one already-broken file gains 2; no new syntax-error file (the jclassd corpus gate stays at its baseline of 1 — ASM's class name containing (), unspeakable in Java source).

Java 27 / 28 support

  • Class-file versions 71/72 are parsed; Temurin 27+35 and 28-beta+18 are in the corpus. Full JDK 27/28 images decompile with 0 failures; the features suite is 10/10 on each.

Other fixes

  • Record canonical constructors no longer emit an illegal super();.
  • Called helpers hidden by --synthetic are no longer dropped (JetBrains $$$reportNull$$$0).
  • Merged throw values take a pool-aware LUB instead of degrading to Object.
  • Array checkcasts resolve their type from the descriptor (76 units previously treated [Ljava/lang/Class; as a class name).

Performance

  • mimalloc as the global allocator; internal migration to jdc-core 0.2.17 (Arc<str> IR, Fx hashing).
  • The nested-class reverse-reference index is built once per pool instead of once per render worker (JDK 28 image: kernel-side time 19.3 s → 11.1 s).
  • Apple M5 Pro, 18 cores: rt.jar 2.3 s / 686 MB parallel (9.4 s / 427 MB single-threaded); full JDK 28 image (15,039 units) 4.7 s / 978 MB (23.3 s / 525 MB single-threaded).
  • Against Vineflower / CFR / Procyon / fernflower: ~11× / 23× / 48× / 93× on rt.jar, and ~31× / 106× / 154× on the JDK 28 image (Vineflower cannot finish it).
  • Deterministic output is unchanged: parallel and JCDC_THREADS=1 runs are byte-identical.

Verification: features 59/59 on both structurer paths; corpus spot byte-identical per method to the previous release; rt.jar 12,609 units, panic-free; jclassd syntax gate at its baseline of 1.

Full Changelog: v0.1.2...v0.1.3