Skip to content

Commit ac8914f

Browse files
author
jun0
committed
[rustjava-adopt-javac-fixture-provenance-verified-p0] test(fixtures): 다섯 픽스처가 재빌드되지 않는 이유는 -g 다
채택 제안 2026-09-17-javac-fixture-provenance-verified#p0 의 처분. 커밋본은 디버그 정보를 달고 컴파일됐고 스크립트는 그것 없이 재빌드하고 있었다 — javac 기본은 `-g:lines,source` 라 LocalVariableTable 이 안 나온다. `-g` 를 주면 다섯 전건 바이트 동일. ★제안이 댄 두 설명은 둘 다 틀렸다: · 다른 컴파일러 아니다 — 같은 다섯이 26.0.1·26.0.2.1 에서 똑같이 다르고 둘 다 -g 면 똑같이 동일하다. · 소스 발산 아니다 — -g 만 주면 지금 소스가 커밋 바이트를 정확히 낸다. ⇒ 제안 tradeoff 의 「재컴파일이 동작 변경이 될 수 있다」는 성립하지 않는다(재컴파일 불요 · 커밋된 .class 바이트 0 변경). 고친 한 자리: 스크립트가 --release 를 픽스처에서 읽듯 -g 도 픽스처에서 읽는다(상수풀의 LocalVariableTable 유무). 배선 전 판별력 실측 — 보유 5 · 미보유 107 · 5/5 · 오탐 0. 양방향: 정상 109 rebuilt / 109 reproduced / 0 differed ↔ 개악(-g 파생 한 줄 no-op) 104 / 5 differed (✗ 목록이 원래 다섯과 동일) · 복원 0. 잃는 것: 스크립트는 여전히 rc=1(재빌드 불가 3건 = 제안이 미해결로 적은 별 축 · 넓히지 않았다). cargo test --all 579/0/1(불변 — Rust 무접촉) · check-dod-ci-parity OK.
1 parent a45ea47 commit ac8914f

5 files changed

Lines changed: 128 additions & 4 deletions

File tree

‎REPORT.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
# REPORT
2+
## [2026-09-18] 루트 픽스처 다섯이 재빌드되지 않는 이유 — ★**`-g` 다. 제안이 댄 두 설명은 «둘 다» 틀렸다** (rustjava-adopt-javac-fixture-provenance-verified-p0)
3+
- 무엇을: 채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0`(worklog json `adoptedProposals` 기록). ★**제품 Rust 0줄 · 커밋된 `.class` 바이트 «0 변경»** — 고친 것은 검증 스크립트 한 자리다.
4+
- ★**답**: 커밋본은 **디버그 정보를 달고**(`-g`) 컴파일됐고 스크립트는 **그것 없이** 재빌드했다. javac 기본은 `-g:lines,source` 라 `LocalVariableTable` 이 안 나온다. 단서는 `javap -v -p` 대조(커밋본 902B 에만 `LocalVariableTable` 과 `this`·`args`·`oe`…).
5+
- ★★**제안의 두 설명을 측정으로 반증했다**: ⒜**다른 컴파일러 아니다** — 같은 다섯이 **26.0.1 과 26.0.2.1 에서 똑같이** 다르고 둘 다 `-g` 면 **똑같이 동일**하다(두 판본 각각 직접 실행 · 26.0.1 keg 잔존) ⒝**소스 발산 아니다** — `-g` 만 주면 **지금 소스가 커밋 바이트를 정확히 낸다**.
6+
⇒ ★★**제안이 가장 걱정한 대가가 사라진다** — `tradeoff` 의 「재컴파일이 동작 변경이 될 수 있다」는 ★**재컴파일 자체가 불요**라 성립하지 않는다.
7+
- ★**고친 한 자리**: 스크립트가 이미 `--release` 를 픽스처에서 읽으므로 **`-g` 도 같은 자리에서** 읽게 했다(상수풀의 `LocalVariableTable` 유무). ★**배선 전에 판별력을 쟀다** — 보유 **5** · 미보유 **107** · ★**5/5 · 오탐 0**.
8+
- ★**양방향**: 정상 **109 rebuilt / 109 reproduced / 0 differed** ↔ ★개악(`-g` 파생 한 줄 no-op) **104 / 5 differed**(✗ 목록이 원래 다섯과 동일) · 복원 0.
9+
- ★**잃는 것**: ⒜스크립트는 **여전히 `rc=1`** — 재빌드 불가 **3건**(형제 참조 소스 · `-sourcepath` 미사용)은 제안이 미해결로 적은 **별 축**이라 넓히지 않았다 ⒝판정이 **한 속성의 유무**에 걸린다(`-g:none` 재생성은 조용히 드리프트로 읽힌다).
10+
- 검증: `cargo test --all` **579 passed / 0 failed / 1 ignored**(불변 — Rust 무접촉) · `check-dod-ci-parity` → **「OK 두 축 모두 대칭차 0 — 명령 6개 · toolchain 2개로 «둘 다 일치»」**.
11+
- ★후속 추천: 재빌드 불가 3건의 컴파일 방법 결정(M) — 상세 = `docs/worklog/2026-09-18-five-fixtures-were-built-with-g.md`.
12+
213
## [2026-09-17] 「전건 single-defect」는 «측정»이 아니었다 — 판정식을 `given` 에서 파생시킨다 (rustjava-adopt-class-format-mutation-audit-p0-fix)
314
- 무엇을: 게이트② **request-changes** 승계(PR #63 · 핀 `377d58b1`). ★**검수자 지적이 옳았다** — 고친 것은 감사 스크립트 **1파일**이고 제품 Rust 는 **0줄**이다.
415
- ★★**급소는 한 줄이다**: `repaired` 를 **정본 인자로 다시 짓고** 있었다 ⇒ 픽스처에 둘째 결함이 무엇이 들어오든 `repaired` 와 `canonical` 이 **똑같이 버려서** 항상 같았다.

‎STATE.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
(둘 다 이것보다 오래됐고 MERGEABLE/CONFLICTING 처분이 이미 걸려 있다). 겹침은 전부 **append 형 합집합**이라 해소는 기계적이다)
88

99
## 완료
10+
- [rustjava-adopt-javac-fixture-provenance-verified-p0] ★★**루트 픽스처 다섯이 재빌드되지 않는 이유 = `-g`.** 채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0`(worklog json 기록). ★**제품 Rust 0줄 · 커밋 `.class` 바이트 0 변경.**
11+
★**제안이 댄 두 설명이 둘 다 틀렸다** — 컴파일러 아니다(**26.0.1·26.0.2.1 에서 같은 다섯이 같게 행동**) · 소스 발산 아니다(`-g` 면 **지금 소스가 커밋 바이트를 낸다**).
12+
⇒ ★**제안 `tradeoff` 의 「재컴파일이 동작 변경이 될 수 있다」가 «성립하지 않는다»** — 재컴파일이 불요다.
13+
★고친 한 자리: 스크립트가 `--release` 를 픽스처에서 읽듯 **`-g` 도 픽스처에서**(상수풀 `LocalVariableTable`). ★배선 전 판별력 실측 **5/5 · 오탐 0**(보유 5 · 미보유 107).
14+
★양방향: 정상 **109/109/0 differed** ↔ 개악(파생 한 줄 no-op) **104/5 differed**(원래 다섯과 동일).
15+
★**잃는 것**: 스크립트는 **여전히 rc=1**(재빌드 불가 3건 = 제안이 미해결로 적은 별 축 · 넓히지 않았다) · 판정이 한 속성 유무에 걸린다.
16+
★`--all` **579/0/1**(불변) · `check-dod-ci-parity` OK.
1017
- [rustjava-adopt-class-format-mutation-audit-p0-fix] ★★**판정식을 `given` 에서 파생시킨다 — 게이트② 반려 승계(PR #63).**
1118
★**급소 한 줄**: `repaired` 를 정본 인자로 **다시 짓고** 있어 둘째 결함을 버렸다 ⇒ 「single-defect 인가」를 묻는데 **입력이 이미 single-defect** 였다(순환 · 18중 **14건**).
1219
★처방은 발명이 아니라 **옮겨오기** — 이미 옳던 `indy` 근접실패 형태를 `add()` 한 곳으로 모아 **4족 전건**이 지나게 했고,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"date": "2026-09-18",
3+
"taskId": "rustjava-adopt-javac-fixture-provenance-verified-p0",
4+
"summary": "The five root fixtures that would not rebuild to their committed bytes were compiled with -g and rebuilt without it. Both explanations the proposal offered — a different compiler, or a source edited after the fixture was built — are refuted by measurement. No fixture was recompiled and no committed byte changed; verify-javac-fixtures.sh now derives -g from the fixture the same way it already derived --release, and all 109 rebuildable fixtures reproduce exactly.",
5+
"changes": [
6+
"test-data/src/verify-javac-fixtures.sh: per-fixture `debug=-g` derived from the presence of LocalVariableTable in the committed class, passed to the rebuild and echoed in the per-fixture line; header comment records the measurement"
7+
],
8+
"verification": [
9+
"before: `sh test-data/src/verify-javac-fixtures.sh test-data` reported 109 rebuilt: 104 reproduced, 5 differed — the same counts the proposal reported, and under javac 26.0.2.1 rather than the 26.0.1 it was measured on",
10+
"after: 109 rebuilt: 109 reproduced, 0 differed",
11+
"MUTATION, kill the one line that derives -g (`debug=-g` becomes a no-op) in the script's own call path: 109 rebuilt: 104 reproduced, 5 differed — and the five named are exactly the original five. Restored: back to 0 differed",
12+
"the discriminator was measured before it was wired: over all of test-data, exactly 5 fixtures carry LocalVariableTable and 107 do not, and those 5 are precisely the ones that differed — 5/5 with zero false positives",
13+
"compiler axis ruled out by hand: rebuilding the five under javac 26.0.1 (the version the proposal measured) gives differ-without-g and byte-identical-with-g, the same as 26.0.2.1",
14+
"cargo test --all: 579 passed / 0 failed / 1 ignored, unchanged — this round touches no Rust",
15+
"no committed .class byte changed: the only file modified under test-data/ is the script"
16+
],
17+
"issues": [
18+
"The script still exits 1, because 3 fixtures (ConstantsReader, IncompatibleStatic, VirtualDispatchSemantics) cannot be rebuilt alone — they reference siblings, and the script deliberately omits -sourcepath so that test-data/src/Exception.java does not shadow java.lang.Exception. That is a separate axis the proposal named as unresolved, and this round did not widen into it.",
19+
"The -g axis is now derived, so a fixture rebuilt by hand without -g and committed would be reported as drift on the next run — which is the intended reading, but it means the script's answer depends on an attribute that a future `-g:none` build would remove silently."
20+
],
21+
"adoptedProposals": ["2026-09-17-javac-fixture-provenance-verified#p0"]
22+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# 2026-09-18 — 다섯 픽스처는 `-g` 로 만들어졌다 (rustjava-adopt-javac-fixture-provenance-verified-p0)
2+
3+
채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0` —
4+
「루트 픽스처 다섯이 왜 커밋된 바이트로 재빌드되지 않는지 알아내라」의 처분이다.
5+
★**제품 Rust 0줄 · 커밋된 `.class` 바이트 0 변경.**
6+
7+
## 답 — `-g` 다
8+
9+
커밋본은 **디버그 정보를 달고** 컴파일됐고, 검증 스크립트는 **그것 없이** 재빌드하고 있었다.
10+
javac 의 기본은 `-g:lines,source`(`LineNumberTable`+`SourceFile`)라 `LocalVariableTable` 은 **안 나온다**.
11+
12+
| 픽스처 | `-g` 없이 | `-g` |
13+
|---|---|---|
14+
| `MonitorSemantics` · `$CounterWorker` · `$FailingWorker` (`--release 8`) | 다름 | ★**바이트 동일** |
15+
| `NativeMethod` (`--release 8`) | 다름 | ★**바이트 동일** |
16+
| `OddEven` (`--release 21`) | 다름 | ★**바이트 동일** |
17+
18+
처음 단서는 `javap -v -p` 대조였다 — 커밋본(902B)에만 `LocalVariableTable` 과 그 이름들
19+
(`this`·`args`·`oe`·`arg`·`i`)이 있고 재빌드본(709B)에는 없다.
20+
21+
## ★제안의 «두 설명»은 둘 다 틀렸다
22+
23+
제안 `why` 는 「**다른 컴파일러**와 **픽스처를 만든 뒤 편집된 소스**가 둘 다 이것을 설명한다」고 적었다.
24+
측정은 **둘 다 반증한다**:
25+
26+
- **컴파일러 아니다** — 같은 다섯이 **26.0.1 과 26.0.2.1 에서 똑같이** 다르고, 둘 다 `-g` 를 주면
27+
**똑같이 바이트 동일**하다(두 판본을 각각 직접 돌려 확인 · 26.0.1 keg 가 남아 있어 가능했다).
28+
※제안은 26.0.1 에서 측정됐고 이 회차는 26.0.2.1 이 기본인 맥에서 쟀는데 **수가 같았다** — 그 자체가 첫 반증이다.
29+
- **소스 발산 아니다** — `-g` 만 주면 **지금 소스가 커밋된 바이트를 정확히 낸다**. 소스와 픽스처는 **어긋나 있지 않다.**
30+
31+
★★**그래서 제안이 가장 걱정한 대가가 «사라진다»**: 제안 `tradeoff` 는 「두 번째 경우의 처방은 재컴파일이고,
32+
여러 시험이 픽스처 출력을 커밋된 `.txt` 와 비교하므로 재컴파일이 **동작 변경**이 될 수 있다」였다.
33+
⇒ ★**재컴파일이 필요 없다.** 이 회차는 `.class` 를 **한 바이트도** 건드리지 않았다.
34+
35+
## 고친 «한 자리»
36+
37+
스크립트는 이미 **`--release` 를 픽스처 자신에서** 읽는다(「nothing external has to be consulted or kept in sync」).
38+
★**`-g` 도 같은 자리에서 같은 방식으로** 읽게 했다 — 커밋된 클래스의 상수풀에 `LocalVariableTable` 이 있으면 `-g`.
39+
40+
★**배선 «전»에 판별력을 쟀다**: `test-data` 전체에서 `LocalVariableTable` 보유는 **정확히 5개**,
41+
미보유 **107개**이고 그 5개가 **바로 그 다섯**이다 — ★**5/5 · 오탐 0**.
42+
43+
## 검증 — 양방향
44+
45+
| | 결과 |
46+
|---|---|
47+
| 고친 뒤(정상) | ★**109 rebuilt: 109 reproduced, 0 differed** |
48+
| ★개악 — `-g` 파생 **한 줄**을 no-op 으로 | ★**104 reproduced, 5 differed** · ✗ 목록이 **원래 다섯과 동일** |
49+
| 복원 | 0 differed |
50+
51+
`cargo test --all` **579 passed / 0 failed / 1 ignored**(불변 — Rust 무접촉).
52+
53+
## ★잃는 것 — 그리고 «안 하면 무엇이 나쁜가»
54+
55+
- ★**스크립트는 여전히 `rc=1` 이다.** 재빌드 불가 **3건**(`ConstantsReader`·`IncompatibleStatic`·
56+
`VirtualDispatchSemantics`)이 남아 있고, 그것은 제안이 **따로 미해결로 적어 둔 축**이다(형제가 필요한 소스 ·
57+
`-sourcepath` 를 일부러 안 쓴다 — `test-data/src/Exception.java` 가 `java.lang.Exception` 을 가리기 때문).
58+
⇒ ★**드리프트 질문은 답했고, 종료 코드는 아직 0 이 아니다.** 넓히지 않았다.
59+
- ★**파생 축이 하나 늘었다** — 누군가 `-g:none` 으로 다시 만들어 커밋하면 그 속성이 **조용히 사라지고**
60+
스크립트는 그것을 「드리프트」로 읽는다. 의도한 읽기이지만, **판정이 한 속성의 유무에 걸린다**는 뜻이기도 하다.
61+
- ★**안 하면**: 스크립트가 **영구히 거짓 드리프트 5건**을 보고한다. 그러면 ⑴「재컴파일해서 맞추자」는
62+
제안의 `tradeoff` 대로 **정말로 동작 변경 위험**을 감수하게 되거나 ⑵다음 사람이 **같은 조사를 처음부터 다시** 한다.
63+
★그리고 이 스크립트는 「픽스처를 재생성했을 때 사람이 돌리는 검사」라 ★**거짓 경보가 그 유일한 용도를 무디게 한다.**
64+
65+
## 후속
66+
67+
- ★**재빌드 불가 3건** — 형제를 참조하는 소스를 어떻게 컴파일할 것인가(`-sourcepath` 없이).
68+
★제안이 「이걸 정하지 않으면 조사가 완결되지 않는다」고 적은 그 축이고, **여기서 넓히지 않았다.**

‎test-data/src/verify-javac-fixtures.sh‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
# `javac 26.0.1 --release 21`, and five sampled root fixtures reproduce under the --release their
1212
# recorded major version implies (65->21, 66->22, 70->26).
1313
#
14+
# "flags" in that sentence is not decoration. Measured 2026-09-18 over all of test-data with javac
15+
# 26.0.2.1: five fixtures rebuilt to different bytes -- MonitorSemantics and its two named inner
16+
# classes, NativeMethod, OddEven -- and all five reproduce exactly once -g is passed. They were
17+
# built with debug info; the rebuild was not. Neither a different compiler nor a source edited
18+
# after the fact was involved, which were the two explanations on offer: the same five differ under
19+
# 26.0.1 and 26.0.2.1 alike, and the sources are in step with the committed bytes. So the -g is
20+
# derived per fixture below, from the fixture, exactly as --release already was.
21+
#
1422
# It is *not* wired into CI, and that is deliberate rather than an oversight: there is no JDK in
1523
# .github/workflows/rust.yml and none on PATH here, so a test that needed one would either fail
1624
# everywhere or skip everywhere. This is the manual check you run when you regenerate a fixture.
@@ -63,23 +71,31 @@ for class in "$DIR"/*.class; do
6371
major=$(od -An -tu1 -j6 -N2 "$class" | tr -s ' \n' ' ' | awk '{ print $1 * 256 + $2 }')
6472
release=$((major - 44))
6573

74+
# ...and its own debug attributes say which -g it was built with, read the same way and for the
75+
# same reason: nothing external to keep in sync. javac's default is -g:lines,source, which emits
76+
# LineNumberTable but not LocalVariableTable, so the presence of that attribute name in the pool
77+
# is what separates a `-g` build from a default one. Measured 2026-09-18 over test-data: exactly
78+
# the five fixtures that did not reproduce carry it, and none of the other 107 do.
79+
debug=
80+
if LC_ALL=C grep -aq LocalVariableTable "$class"; then debug=-g; fi
81+
6682
# Deliberately no -sourcepath: test-data/src holds Exception.java, Array.java, Method.java and
6783
# friends, so putting it on the source path makes javac resolve `Exception` to the fixture
6884
# rather than java.lang.Exception. Measured — it turns clean rebuilds into type errors. The
6985
# cost is that a source needing a sibling cannot be rebuilt alone, which is reported as
7086
# "could not be rebuilt" rather than as drift.
7187
rm -rf "$work/out" && mkdir -p "$work/out"
72-
if ! "$JAVAC" --release "$release" -d "$work/out" "$source" 2>"$work/err"; then
73-
echo " ? $base: could not rebuild at --release $release: $(grep -m1 error "$work/err" || head -1 "$work/err")" >&2
88+
if ! "$JAVAC" ${debug:+"$debug"} --release "$release" -d "$work/out" "$source" 2>"$work/err"; then
89+
echo " ? $base: could not rebuild at --release $release${debug:+ $debug}: $(grep -m1 error "$work/err" || head -1 "$work/err")" >&2
7490
unbuildable=$((unbuildable + 1))
7591
continue
7692
fi
7793

7894
checked=$((checked + 1))
7995
if cmp -s "$work/out/$base.class" "$class"; then
80-
echo " ✓ $base (--release $release)"
96+
echo " ✓ $base (--release $release${debug:+ $debug})"
8197
else
82-
echo " ✗ $base (--release $release): rebuilt bytes differ from the committed fixture" >&2
98+
echo " ✗ $base (--release $release${debug:+ $debug}): rebuilt bytes differ from the committed fixture" >&2
8399
differed=$((differed + 1))
84100
fi
85101
done

0 commit comments

Comments
 (0)