Repository navigation
更新内容
正确性(本版重点)
- 条件表达式不再重复求值已存储的调用。
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 todup; astore; ifnull; the old builder left the CALL on the operand stack, so the branch printedf()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 == 0is not boolean evidence on its own: javac has no distinct test opcode, soint p = ports[i]; if (p == 0)used to be declaredboolean 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
--syntheticare no longer dropped (JetBrains$$$reportNull$$$0). - Merged
throwvalues 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.jar2.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=1runs 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