Skip to content

Commit 38c02a2

Browse files
author
jun0
committed
[rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1] test(fixtures): 어느 javac 이 만들었나를 기록에서 검증으로 바꾼다
제안의 결론 둘이 실측으로 반증됐다. 「offline 으로 검증할 수 없다」는 거짓이다 — javac 은 같은 소스·플래그·컴파일러에 결정적이라 기록된 도구로 재빌드하면 indy 6개가 바이트 단위로 재현된다. 「형상 단언이 한 픽스처만 덮는다」도 거짓이다 — javac 산출 indy 3/3 이 이미 갖고 있고, 제안이 든 condy 위험은 ConstantKinds 의 Dynamic 개수가 잠근다. 그래서 제안이 불가능하다고 본 쪽을 만들었다. --release 는 픽스처 자신의 major-44 로 읽어 외부 표에 의존하지 않고, 명시한 JAVAC 가 안 되면 조용히 대체하지 않고 rc=2 로 멈추며, 「못 만들었다」와 「만들었는데 다르다」를 가른다. JDK 가 CI 에도 PATH 에도 없어 배선하지 않았다. -sourcepath 는 넣었다가 되돌렸다: test-data/src/Exception.java 가 java.lang.Exception 을 가린다. 루트 일반화는 109 rebuilt / 104 재현 / 5 상이 — 원인은 단정하지 않고 후속으로 넘겼다.
1 parent 308d56b commit 38c02a2

6 files changed

Lines changed: 286 additions & 2 deletions

File tree

‎REPORT.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,27 @@
11
# REPORT
2+
## [2026-09-17] 「어느 javac 이 만들었나」를 «기록»이 아니라 «검증»으로 바꿨다 (rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1)
3+
- 무엇을: 기록된 도구로 **다시 빌드해 바이트를 비교**하는 검사를 만들었다. ★**제품 코드 무접촉**(Rust 변경은 doc 주석 1곳).
4+
- 왜: 채택 제안 `2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p1`.
5+
- 사용자 영향: 없다(시험 위생). ★바뀐 것은 ★**「javac 26.0.1 로 만들었다」가 «주장»에서 «검증된 사실»이 된 것**이다.
6+
- ★★**제안의 결론 «둘»이 실측으로 반증됐다**:
7+
⑴「**Nothing offline can verify** a recorded compiler version … buys **provenance, not enforcement**」 →
8+
★**거짓**. javac 은 같은 소스·플래그·컴파일러에 **결정적**이라 ★**`test-data/indy` 6개가 바이트 단위로 재현된다**.
9+
⑵「강제 가능한 축은 `constant_pool.rs` 의 상수 개수뿐이고 **한 픽스처만** 덮는다」 → ★**거짓**.
10+
★**javac 산출 indy 픽스처 3/3 이 형상 단언을 갖는다** — `ConstantKinds`(태그별 정확한 개수) ·
11+
`StringConcat`(신원 4축 + 인자가 «가리키는 값» + ★**바이트 창** `[15,6,0,35]`) · `Lambda`(`args[0]==args[2]!=args[1]`).
12+
★제안이 든 위험(「현대 javac 이 enum switch 를 condy 로 낸다」)은 ★**`ConstantKinds` 의 Dynamic 개수 3 이 이미 잠근다.**
13+
- ★**만든 것**: `test-data/src/verify-javac-fixtures.sh` — ★`--release` 를 **픽스처 자신의 major − 44** 로 읽어
14+
**외부 표에 의존하지 않고**, ★명시한 `JAVAC` 가 안 되면 **조용히 대체하지 않고 rc=2** 로 멈추며,
15+
★**「못 만들었다」와 「만들었는데 다르다」를 «가른다»**(합치면 발견을 과장한다).
16+
★**CI 에 배선하지 «않았다»** — 워크플로에도 PATH 에도 JDK 가 없어 **어디서나 실패하거나 어디서나 건너뛴다**.
17+
- ★**실패담 둘(밟은 대로 적는다)**: ⑴`command -v javac` 이 macOS **스텁**을 고른다 ⇒ **실행해서** 판별 ⑵★`-sourcepath` 를 넣었다가
18+
**더 나빠졌다** — `test-data/src/Exception.java` 가 `java.lang.Exception` 을 가려 멀쩡하던 재현이 타입 오류로 무너졌다 ⇒ **되돌리고 그 대가를 따로 보고**.
19+
- ★**개악 대조**: 커밋본 **1바이트 반전** → ★**✗ 감지** · 복원 → 6/6 재현 · 명시 `JAVAC` 부재 → ★**rc=2(통과 아님)**.
20+
- ★**일반화 — 값만 적고 고치지 않았다**: 루트에 돌리니 **109 rebuilt · 104 재현 · 5 상이 · 3 재빌드 불가**.
21+
상이 5건(`MonitorSemantics`×3 · `NativeMethod` @8 · `OddEven` @21)의 ★**원인은 단정하지 않는다**(다른 컴파일러 ↔ 빌드 후 소스 수정이 둘 다 맞는다).
22+
★그래도 적는 이유: ★**제안이 걱정한 드리프트가 «실재»한다는 첫 직접 증거**다.
23+
- 검증: `cargo test --all` **572 / 0 failed / 1 ignored**(doc 주석만 바꿔 **불변**) · DoD 7명령 rc=0.
24+
- 후속 추천: 루트 5건이 **왜** 재현되지 않는지 규명(M) — 상세 = `docs/worklog/2026-09-17-javac-fixture-provenance-verified.md`.
225
## [2026-09-16] 부트스트랩 메서드의 «정적 인자» 인덱스를 경계 검사한다 (rustjava-adopt-bound-bootstrap-method-attr-index-p1)
326
- 무엇을: JVMS 4.7.23 의 `bootstrap_arguments` 는 상수 풀 인덱스인데 ★**아무도 그것이 실재하는지 보지 않았다.**
427
이제 풀에 «없는» 인덱스를 가리키면 **거부**한다.

‎STATE.md‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,35 @@
44
(없음 — 2026-09-16 실측: 착수 시 진행 티켓 0 · 열린 PR 0. ※「열린 PR 0」은 ★**이 회차 PR 착지 시점 기준**이다 — 회신 시점에는 그 PR 자신이 열려 있다)
55

66
## 완료
7+
- [rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1] ★★**「어느 javac 이 만들었나」를 «기록»에서 «검증»으로 바꿨다.**
8+
채택 제안 `2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p1`(worklog json `adoptedProposals` 기록). ★**제품 코드 무접촉.**
9+
★★**제안의 결론 «둘»이 실측으로 반증됐다 — 그래서 제안이 «불가능»하다고 적은 쪽을 만들었다**:
10+
⑴「**Nothing offline can verify** a recorded compiler version … buys **provenance, not enforcement**」 → ★**거짓**:
11+
javac 은 같은 소스·플래그·컴파일러에 **결정적**이라, 기록된 도구(`javac 26.0.1 --release 21`)로 재빌드하니
12+
★**`test-data/indy` 의 6개가 «바이트 단위로 동일»**했다. ⇒ ★**기록이 «재현»으로 검증된다.**
13+
⑵「강제 가능한 축은 `constant_pool.rs` 의 상수 개수뿐이고 **한 픽스처만** 덮는다」 → ★**거짓**:
14+
★**javac 산출 indy 픽스처 «3/3»이 형상 단언 보유** — `ConstantKinds`(MethodType 1·Dynamic 3·MethodHandle 7·InvokeDynamic 3) ·
15+
`StringConcat`(부트스트랩 **4축** + 인자가 «가리키는 값» `"a\u{1}"` + ★**바이트 창** `[15,6,0,35]` · 「layout changed」로 실패) ·
16+
`Lambda`(`LambdaMetafactory.metafactory` · 인자 3 · ★`args[0]==args[2]!=args[1]`).
17+
★**제안이 든 위험(「현대 javac 은 enum switch 를 condy 로 낸다」)은 `ConstantKinds` 의 Dynamic **3** 이 이미 잠그고 있다.**
18+
★**만든 것**: `test-data/src/verify-javac-fixtures.sh` — ⑴★`--release` 를 **픽스처 자신의 major − 44** 에서 읽어
19+
★**외부 표(형제 PR #57 의 버전 표)에 의존하지 않는다**(동기화할 것이 없다 · 미착지 의존도 없다)
20+
⑵★명시한 `JAVAC` 가 안 되면 **조용히 다른 컴파일러로 대체하지 않고 rc=2** — 「무엇이 검증했나」가 흐려지면 안 된다
21+
⑶★**「못 만들었다」와 「만들었는데 다르다」를 «가른다»** — 합치면 발견을 과장한다.
22+
★**CI 에 배선하지 «않았다»**: `.github/workflows/rust.yml` 에 JDK 가 없고 PATH 에도 없다 ⇒
23+
JDK 를 요구하는 테스트는 ★**어디서나 실패하거나 어디서나 건너뛴다.** 이건 «재생성했을 때 사람이 돌리는» 검사다(doc 주석에 명시).
24+
★★**실패담 둘을 남긴다 — 이 회차가 실제로 밟았다**: ⑴`command -v javac` 이 macOS **스텁**(실행되는데 「JDK 없음」)을 고른다
25+
⇒ 경로가 아니라 **실행해서** 판별한다 ⑵★**`-sourcepath` 를 넣었다가 «더 나빠졌다»** — `test-data/src` 에 **`Exception.java`**·
26+
`Array.java`·`Method.java` 가 있어 javac 이 `Exception` 을 ★**`java.lang.Exception` 이 아니라 그 픽스처로** 해석했다
27+
(멀쩡히 재현되던 파일들이 `incompatible types` 로 무너졌다) ⇒ **되돌리고 그 대가**(형제 참조 소스는 홀로 재빌드 불가)를 **따로 보고**한다.
28+
★**개악 대조**: 커밋본 **마지막 1바이트 반전** → ★**✗ 감지** · 복원 → **6 reproduced** · 명시 `JAVAC` 부재 → ★**rc=2 「nothing was verified」**(통과 아님).
29+
★★**일반화 — 시켜 보고 «나온 값»만 적었다(고치지 않았다)**: 루트 `sh … test-data` →
30+
**109 rebuilt · 104 재현 · ★5 상이 · 3 재빌드 불가**. 상이 5건 = `MonitorSemantics`(+내부 2) · `NativeMethod`(`--release 8`) · `OddEven`(`--release 21`).
31+
★**원인은 단정하지 않는다** — 「다른 컴파일러」와 「빌드 뒤 소스 수정」이 **둘 다 이 관측과 맞는다**. ★범위 밖이라 후속(M)으로 넘겼다.
32+
★**그래도 적는 이유**: ★**제안이 걱정한 드리프트가 «실재»한다는 첫 «직접» 증거**다(그전까지는 버전 분포에서의 추론이었다).
33+
★**직전 회차가 «커밋하지 않기로» 한 개악 하네스와 다른 종류다** — 그건 **제품 소스를 치환**해 죽으면 트리를 오염시켰고,
34+
이건 **읽고 비교만** 한다(실패해도 트리 무변) ⇒ 그래서 **남겼다.**
35+
★`cargo test --all` **572 / 0 failed / 1 ignored**(doc 주석만 바꿔 **불변**) · DoD **7줄 전건 rc=0**.
736
- [rustjava-adopt-bound-bootstrap-method-attr-index-p1] ★★**부트스트랩 «정적 인자» 인덱스를 경계 검사한다 — 「감지되나 판정되지 않던」 자리를 닫았다.**
837
채택 제안 `2026-09-16-bound-bootstrap-method-attr-index#p1`(worklog json `adoptedProposals` 기록).
938
★**전/후**: `UnsupportedOperationException` → ★`ClassFormatError`. ★참조 JVM(OpenJDK 26.0.1) →
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"schema": "worklog/v1",
3+
"date": "2026-09-17",
4+
"taskId": "rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1",
5+
"summary": "Turned the recorded compiler from an assertion into a check: rebuilding test-data/indy with the recorded javac reproduces all six class files byte for byte. Both of the proposal's blocking claims — that nothing offline can verify a compiler record, and that shape assertions cover only one fixture — are false, measured.",
6+
"changes": [
7+
"test-data/src/verify-javac-fixtures.sh: new. Rebuilds javac-compiled fixtures and compares bytes, reading the --release from each fixture's own major version so nothing has to be kept in sync",
8+
"tests/test_fixture_pins.rs: the pin's doc comment now records that the compiler was verified by rebuilding, with the command and the date, instead of only naming it"
9+
],
10+
"verification": [
11+
"test-data/indy: 6 rebuilt, 6 reproduced byte-for-byte, 0 differed, under javac 26.0.1 --release 21 — the toolchain the comment records",
12+
"the proposal's claim that shape assertions cover one fixture is false: ConstantKinds has exact tag counts in classfile/src/constant_pool.rs, StringConcat has the four identity axes plus what its static argument points at plus a byte-window check in classfile/tests/test.rs, and Lambda has its bootstrap identity plus the args[0] == args[2] != args[1] relation",
13+
"controlled mutation: flipping the last byte of the committed StringConcat.class is detected as differed; restoring returns 6 reproduced",
14+
"an explicitly set JAVAC that does not work exits 2 with 'nothing was verified' rather than silently falling back to another compiler",
15+
"generalization probe over test-data root: 109 rebuilt, 104 reproduced, 5 differed (MonitorSemantics and two inner classes, NativeMethod at --release 8, OddEven at --release 21), 3 could not be rebuilt alone",
16+
"-sourcepath was tried and reverted: test-data/src contains Exception.java, so putting it on the source path makes javac resolve Exception to the fixture instead of java.lang.Exception and turns clean rebuilds into type errors",
17+
"cargo test --all: 572 passed / 0 failed / 1 ignored, unchanged — the only Rust change is a doc comment",
18+
"DoD 7 commands all rc=0"
19+
],
20+
"issues": [
21+
"The check cannot run in CI: there is no JDK in the workflow and none on PATH here. It is a manual check, so it is only as good as the habit of running it after regenerating a fixture.",
22+
"Five root fixtures do not reproduce. Whether that is a different compiler or a source edited after the fixture was built was not determined — both fit the observation, and chasing it is outside this ticket's target of test-data/src/indy.",
23+
"Three root fixtures cannot be rebuilt in isolation because their sources reference sibling classes, and -sourcepath is not usable here for the reason above."
24+
],
25+
"adoptedProposals": [
26+
"2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p1"
27+
],
28+
"proposals": [
29+
{
30+
"title": "Find out why five root fixtures do not rebuild to their committed bytes",
31+
"plainSummary": "Rebuilding the main test-data fixtures reproduces 104 of them exactly. Five come out different, and we do not know why.",
32+
"userBenefit": "Either the fixtures get back in step with their sources, or we learn that a test has been asserting against bytecode nobody can regenerate — both are better than not knowing.",
33+
"why": "Measured by the script this round added: MonitorSemantics and its two inner classes and NativeMethod differ at --release 8, OddEven differs at --release 21. A different compiler and a source edited after the fixture was built both explain it, and the difference matters: the first is harmless provenance drift, the second means the fixture and its source have diverged.",
34+
"tradeoff": "The likely fix for the second case is recompiling, which changes bytes — and several tests compare fixture output against committed .txt files, so a recompile can turn into a behaviour change. Also three more fixtures cannot be rebuilt alone at all, so the survey cannot be completed without deciding how to compile sources that reference siblings, in a directory where Exception.java shadows java.lang.Exception.",
35+
"effort": "M",
36+
"target": "test-data/, test-data/src/"
37+
}
38+
]
39+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# 2026-09-17 — 「어느 javac 이 만들었나」를 «기록»이 아니라 «검증»으로 바꿨다
2+
3+
티켓 `rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1` — 채택 제안
4+
`2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p1`.
5+
6+
★**제안의 결론 두 개가 실측으로 반증됐다.** 그래서 제안이 «불가능»하다고 적은 쪽을 만들었다.
7+
8+
## ⓒ 반증 1 — 「검증할 수 없다」는 거짓이다
9+
10+
제안 tradeoff: 「**Nothing offline can verify a recorded compiler version** — a record is only as good
11+
as the person writing it, so this buys **provenance, not enforcement**.」
12+
13+
★**실측**: `javac` 은 같은 소스·같은 플래그·같은 컴파일러에 대해 **결정적**이다. 기록된 도구
14+
(`javac 26.0.1 --release 21`)로 다시 빌드하니 ★**`test-data/indy` 의 6개 클래스 파일이 «바이트 단위로 동일»**했다.
15+
16+
```
17+
$ sh test-data/src/verify-javac-fixtures.sh
18+
javac: javac 26.0.1
19+
✓ ConstantKinds$Op · ConstantKinds$Suit · ConstantKinds · Lambda$Op · Lambda · StringConcat (--release 21)
20+
6 rebuilt: 6 reproduced, 0 differed; 0 could not be rebuilt
21+
```
22+
23+
⇒ ★**기록이 «검증»된다.** 「사람이 적은 만큼만 믿을 수 있다」가 아니라 **재현으로 확인된다.**
24+
25+
## ⓐ·ⓒ 반증 2 — 「강제 가능한 축은 한 픽스처만 덮는다」도 거짓이다
26+
27+
제안 why: 「The only assertions holding that axis today are the constant counts in
28+
`classfile/src/constant_pool.rs`, and they **cover one fixture**.」
29+
30+
★**실측 — javac 산출 indy 픽스처 «셋 모두» 형상 단언을 갖고 있다**:
31+
32+
| 픽스처 | 형상 단언 | 어디 |
33+
|---|---|---|
34+
| `ConstantKinds` | 태그별 **정확한 개수**(MethodType 1 · Dynamic 3 · MethodHandle 7 · InvokeDynamic 3) | `classfile/src/constant_pool.rs` |
35+
| `StringConcat` | 부트스트랩 **kind·class·name·descriptor** 4축 + 인자 수 + ★**인자가 «가리키는 값»**(`"a\u{1}"` 레시피) · 게다가 ★**바이트 창** `[15, 6, 0, 35]` 를 찾아 「layout changed」로 실패한다 | `classfile/tests/test.rs` |
36+
| `Lambda` | 부트스트랩 `LambdaMetafactory.metafactory` · 인자 **3개** · ★**`args[0] == args[2] != args[1]`**(samMethodType/instantiatedMethodType 동일성) | `classfile/tests/test.rs` |
37+
38+
⇒ ★**제안이 「더 만들어야 한다」고 본 「enforceable half」는 이미 3/3 있다.**
39+
★그리고 제안이 예로 든 위험(「enum switch 가 현대 javac 에서 condy 가 된다」)은 ★**`ConstantKinds` 의 Dynamic 개수 3 이 정확히 그것을 잠그고 있다.**
40+
41+
## 만든 것 — `test-data/src/verify-javac-fixtures.sh`
42+
43+
기록을 **검사**로 바꾸는 최소 도구. ★**재빌드해서 바이트를 비교한다.**
44+
- ★**`--release` 를 «픽스처 자신»에서 읽는다**(major − 44) — 외부 표에 의존하지 않아 **동기화할 것이 없다**.
45+
- ★**명시한 `JAVAC` 가 안 되면 «다른 컴파일러로 조용히 대체하지 않는다»** — rc=2 로 멈춘다.
46+
(그러지 않으면 「무엇이 검증했나」가 흐려진다.)
47+
- ★**CI 에 배선하지 «않았다»** — `.github/workflows/rust.yml` 에 JDK 가 없고 이 맥의 PATH 에도 없다.
48+
JDK 를 요구하는 테스트는 **어디서나 실패하거나 어디서나 건너뛴다**. 이건 «재생성했을 때 사람이 돌리는» 검사다.
49+
- ★**「못 만들었다」와 「만들었는데 다르다」를 «가른다»** — 합치면 발견을 과장하게 된다(아래 실패담 참조).
50+
51+
## ★실패담 둘 — 이 회차가 실제로 밟은 것
52+
53+
⑴**`command -v javac` 이 macOS 스텁을 찾는다**(실행 파일인데 「JDK 없음」을 출력한다) ⇒ 경로가 아니라 **실행해서** 고른다.
54+
⑵★**`-sourcepath` 를 넣었다가 «더 나빠졌다»** — `test-data/src` 에는 **`Exception.java`·`Array.java`·`Method.java`** 가 있어
55+
소스 경로에 올리면 javac 이 `Exception` 을 ★**`java.lang.Exception` 이 아니라 그 픽스처로** 해석한다
56+
(실측: 멀쩡히 재현되던 파일들이 `incompatible types: Exception cannot be converted to Throwable` 로 무너졌다).
57+
⇒ **되돌렸고, 그 대가**(형제 클래스를 참조하는 소스는 홀로 재빌드 불가)를 ★**「could not be rebuilt」로 «따로» 보고**한다.
58+
59+
## 개악 대조(양방향)
60+
61+
| 조작 | 결과 |
62+
|---|---|
63+
| 커밋된 `StringConcat.class` 의 **마지막 1바이트** 반전 | ★**✗ 감지** — `rebuilt bytes differ from the committed fixture` |
64+
| 복원 | **6 reproduced, 0 differed** |
65+
| 명시한 `JAVAC` 가 없는 경우 | ★**rc=2 「nothing was verified」** — ★**«통과»가 아니다** |
66+
67+
## ★일반화 — 시켜 보고 «나온 값»을 적는다(고치지는 않았다)
68+
69+
같은 도구를 루트에 돌렸다(`sh … test-data`):
70+
```
71+
109 rebuilt: 104 reproduced, 5 differed; 3 could not be rebuilt
72+
```
73+
★**다른 5개**: `MonitorSemantics`(+내부 클래스 2) · `NativeMethod` — 셋 다 `--release 8` · `OddEven` — `--release 21`.
74+
★**원인은 여기서 «단정하지 않는다»** — 「다른 컴파일러로 빌드됐다」와 「빌드 뒤 소스가 수정됐다」가 **둘 다 이 관측과 맞는다.**
75+
★**3개는 홀로 재빌드 불가**(형제 클래스 참조) — 위 `-sourcepath` 제약의 결과다.
76+
⇒ ★**이 회차의 범위(제안 대상 = `test-data/src/indy/`)를 넘으므로 고치지 않았고, 후속으로 넘겼다.**
77+
★**그래도 적는 이유**: 이 수가 ★**제안이 걱정한 드리프트가 «실재»한다는 첫 직접 증거**다 — 가정이 아니다.
78+
79+
## 잃는 것 / 안 하면 무엇이 나쁜가
80+
81+
⒜**잃는 것**: ⑴**스크립트 1개**가 는다(≈70줄 · 읽기 전용 · 트리를 고치지 않는다) ⑵★**CI 가 돌리지 않는다** —
82+
JDK 가 없으니 **자동으로 지켜지지 않는다**. 이것이 이 축의 진짜 한계이고, 숨기지 않는다.
83+
⑶픽스처를 의도적으로 재생성하면 **한 번 돌려 확인하는 습관**이 필요하다(문서·doc 주석에 적었다).
84+
★**왜 그래도 남기나**: 직전 회차가 «커밋하지 않기로» 한 개악 하네스와 **다른 종류**다 —
85+
그건 **제품 소스를 치환**해서 죽으면 트리를 오염시켰고, 이건 **읽고 비교만** 한다(실패해도 트리 무변).
86+
⒝**안 하면**: 「javac 26.0.1 로 만들었다」가 ★**영원히 «주장»으로 남는다.** 그리고 루트의 5건이 보여 주듯
87+
★**드리프트는 이미 일어나 있다** — 확인할 수단이 없으면 다음 회차도 그것을 못 본다.

0 commit comments

Comments
 (0)