Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ done | /usr/bin/grep -c .
- Keep `rustjava-runtime/tests/classes` limited to Java standard library class and API behavior.
- Test JVM and interpreter semantics, including class initialization, bytecode execution, and monitor behavior, with compiled Java fixtures under `test-data/src` and expected output under `test-data`, executed by `tests/test_class.rs`.
- Do not place JVM core behavior tests in the `rustjava-runtime` standard library test tree.
- **Adding or recompiling a fixture means editing `test-data/class-file-versions.txt` in the same commit.** Every committed `.class` has its version frozen there, and a fixture that is not listed fails the pin — deliberately, since a pin that only covers what it already knows about stops covering anything added later. Run `python3 test-data/src/record-class-file-versions.py` and commit the row next to the fixture; a moved version means look at why before re-recording.

## Compatibility Sources
- Implement Java compatibility from public specifications, Javadocs, and observable behavior tests. Do not consult or reproduce OpenJDK or other Java runtime implementation source code; keep the implementation independent to avoid licensing and provenance concerns.
41 changes: 41 additions & 0 deletions REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,47 @@
**4종 전건 KILLED** 로 만들었다(`MakeConcatWrongDescriptor` · `MakeConcatWithArgument`).
- ★**픽스처가 «출력»한다**(`ab`) — ★레시피를 틀린 길이로 합성해도 **링크되고 실행된다**. 값을 버리면 그 오류가 안 보인다(M2 가 그 증거).
- 검증: `cargo test --all` **573 → 574 / 0 failed** · 픽스처 재생성 **멱등**(기존 4개 바이트 동일) · DoD 7명령 rc=0.
## [2026-09-17] 표 키의 경로 구분자를 «두 곳에서» 정규화한다 — 윈도우 CI red 를 고친다 (rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p0-fix)
- 무엇을: 표 키를 만드는 **두 곳**(Rust 테스트 · Python 기록기)을 **슬래시**로 맞췄다. ★**제품 코드 무접촉 · 설계 무변경.**
- 왜: 게이트② **request-changes**(PR #57 · 핀 `3061edb7`) — ★**윈도우 CI 가 red 였다**(`ci-presence` → `CI_RED` rc=1).
- 사용자 영향: 없다(시험 위생). ★바뀐 것은 **표 핀이 «모든 OS 에서» 같은 키를 쓴다**는 것이다.
- ★★**결함의 모양**: `to_string_lossy()` 가 윈도우에서 `indy\StringConcat.class` 를 만드는데 표는 `indy/…` 를 담는다 ⇒
★**하위 36개가 «미기록»과 «유령 기록» 양쪽에 «동시에» 걸린다.** ★**루트 114개는 통과**하므로 ★**mac/linux 로는 절대 안 보인다**
(모수 실측: **150 = 114 + 36**).
- ★★**「두 곳」이 급소다** — ★**같은 결함이 기록기에도 있었고**, Rust 만 고치면 ★**윈도우에서 기록한 표가 이번엔 mac/linux 를 red** 로 만든다.
★**그 축은 CI 가 기록기를 안 돌려서 «영영 조용하다»** ⇒ 둘을 함께 고쳤다(`as_posix()`).
- ★**형제 상호작용(②)은 «코드»가 아니라 «순서»였고, 그 사이 `#55` 가 먼저 착지해 해소된 형태로 나타났다**:
base 를 당기니 그 3개가 **정확히 red 로 잡혔고**, 고친 기록기로 재기록하니 **150 → 153**(diff 정확히 3줄).
⇒ ★**기계가 설계대로 잡고 처방이 한 명령이었다는 실증.**
- ★**정정 하나 더(③)**: `STATE.md` 에 착지해 있던 「#55 는 원장 3파일만 만진다」가 **거짓**이었고,
★**그 오기 때문에 ②가 보이지 않았다** ⇒ 그 자리에서 반증을 붙였다.
- ★**이 설계의 «본래 대가»를 이제 «적어 뒀다»**: 「기록에 없는 픽스처 = red」는 의도된 엄격함이고,
그 비용(픽스처를 더하는 회차가 표를 함께 만진다)이 **어디에도 없었다** ⇒ **`AGENTS.md` §Testing Boundaries 에 한 줄**.
- 검증: ⒞ 정규화를 되돌리면 ★**mac 에서도** 단위 시험이 진다(그래서 `components()` 대신 **문자열 치환**으로 구현했다 —
전자는 Unix 에서 역슬래시 입력을 그냥 통과시켜 ★개악이 안 잡힌다) · Python 축은 `PureWindowsPath` 로 **윈도우 없이** 실증 ·
`cargo test --all` **0 failed** · DoD 7명령 rc=0.

## [2026-09-17] `test-data` 전체의 클래스 파일 버전을 «동결»했다 — 통일이 아니라 ★**[위 `-fix` 가 윈도우 경로 축을 정정]** (rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p0)
- 무엇을: 커밋된 픽스처 **150개**의 `<major>.<minor>` 를 표에 기록하고, ★**혼자 움직이면 red** 가 되게 했다.
- 왜: 채택 제안 `2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p0`.
- 사용자 영향: 없다(시험 위생). ★바뀐 것은 **「다른 JDK 로 재생성했는데 아무도 모르는」 경로가 닫힌 것**이다.
- ★★**제안의 «비용 산정»을 바꿨다 — 기각이 아니라 «설계 교체»**: 제안은
「**decide the intended target** per fixture … recompiled … **The decision is the work**」라며 **통일**을 전제했는데,
정작 제안이 산 이득은 「a regenerated fixture **cannot quietly start testing a different Java version's** shapes」 =
★**«드리프트 탐지»**다. ⇒ 목표를 **동결**로 바꾸면 ★**그 「work」가 통째로 사라진다** — 재컴파일 **0** · 바이트 변경 **0** ·
「어느 타깃이 옳은가」 **결정 불요**.
- ★**대가를 숨기지 않는다**: 이 축은 ★**섞임을 «고치지» 않고 «굳힌다»**. 통일하려면 재컴파일해야 하고,
★**재컴파일은 바이트를 바꿔 그 픽스처가 «무엇을 시험하는지»를 바꿀 수 있다**(제안 자신의 경고) ⇒ 별 축으로 넘겼다(후속 L).
- ★**ⓑ 이미 핀하는 축이 둘 있었고, 그 둘이 못 덮는 곳이 대상이었다**: `test_fixture_pins.rs` 는 **indy 만** ·
생성기는 **자기 산출물 16개**의 버전을 소스에 박는다 ⇒ ★**남는 132개가 「javac 로 손수 재컴파일 가능」한 것**이고,
루트의 **66×8 · 68×1 · 70×3** 이 그 사고의 지문이다.
- ★**세 방향을 전부 검사한다**: ⑴기록과 다름 ⑵★**기록에 없는 새 픽스처**(없으면 핀이 «옵트인»이 되어 내일 추가분이 조용히 미보호) ⑶유령 기록.
개악 **3종 전건 red**(M1 버전 바이트 `65→70` · M2 미기록 픽스처 · M3 유령 행) · 복원 **green**.
★실패 문면이 **파일·두 버전·해소 명령**을 함께 말한다(`Hello.class: recorded 65.0, found 70.0`).
- 검증: 재측 인구조사(총 150 · 루트 5버전) · 기록기 **멱등**(재실행 시 표 바이트 동일) ·
`cargo test --all` **572 → 573 / 0 failed / 1 ignored** · DoD 7명령 rc=0.
- 후속 추천: 루트 픽스처를 **하나의 타깃으로 재컴파일할지 «판정»**(L) — ★동결은 「움직였나」를 답하지 「우리가 원하는 값인가」를 답하지 않는다.
상세 = `docs/worklog/2026-09-17-test-data-version-freeze.md`.
## [2026-09-17] 「어느 javac 이 만들었나」를 «기록»이 아니라 «검증»으로 바꿨다 (rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1)
- 무엇을: 기록된 도구로 **다시 빌드해 바이트를 비교**하는 검사를 만들었다. ★**제품 코드 무접촉**(Rust 변경은 doc 주석 1곳).
- 왜: 채택 제안 `2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p1`.
Expand Down
65 changes: 65 additions & 0 deletions STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,66 @@
열린 PR **#57**(ci-pending)이 「`.class` 미등재는 핀이 **실패**시킨다」(`test-data/class-file-versions.txt`)를 세우므로,
★**이 PR 이 먼저 착지하면 #57 의 표에 이 셋이 «없어»** 그 회차가 red 가 된다(해소 = `record-class-file-versions.py` 재생성).
★**텍스트 충돌 0 이라 `mergeable` 로는 보이지 않는다** — 같은 고지가 #60 회신에도 있다(그쪽은 다른 3개).
- [rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p0-fix] ★★**표 키의 경로 구분자를 «두 곳»에서 정규화 — 윈도우 CI red 를 고쳤다.**
게이트② **request-changes** 승계(PR #57 · 핀 `3061edb7` · ★`ci-presence` → **`CI_RED` rc=1**). ★**제품 코드 무접촉 · 설계 무변경**
(검수자가 「설계는 옳고 세 방향 전부 실제로 문다」로 확인했다 — ★**그 축은 다시 열지 않았다**).
★★**결함의 모양**: `to_string_lossy()` 가 윈도우에서 `indy\StringConcat.class` 를 만드는데 표는 `indy/…` 를 담는다 ⇒
★**하위 36개가 «미기록»과 «유령 기록»에 «동시에» 걸린다.** ★**루트 114개는 통과**하므로 ★**mac/linux 로는 «절대» 안 보인다**
— 모수 실측 **150 = 루트 114 + 하위 36**(검수자 수와 일치).
★★**「두 곳」이 급소다**: ★**같은 결함이 기록기(`record-class-file-versions.py`)에도 있었다** — Rust 만 고치면
★**윈도우에서 기록한 표가 이번엔 mac/linux 를 red** 로 만들고, ★**CI 가 기록기를 안 돌려 그 축은 «영영 조용하다».**
⇒ 둘을 함께 고쳤다(Rust 구분자 정규화 · Python `as_posix()`). ★표 자체는 **이미 슬래시**였다(역슬래시 행 **0** 실측).
★★**②는 «코드»가 아니라 «순서»였고 — 그 사이 `#55` 가 먼저 착지해 «해소된 형태»로 나타났다**:
base 를 당기니 픽스처 3개가 ★**정확히 red 로 잡혔고**(`NotFactoryDescriptor`·`NotInvokeStaticFactory`·`NotMakeConcatWithConstants`),
★**고친 기록기로 재기록하니 150 → 153 · diff 가 정확히 3줄**(그 외 무변).
⇒ ★**기계가 설계대로 «잡고», 처방이 «한 명령»이었다는 실증이다.**
★**남은 형제 실측**: `#56` 은 test-data 접촉 **0** · `#58` 은 `test-data/src/verify-javac-fixtures.sh` **스크립트뿐** ⇒
★**둘 다 표와 상호작용하지 않는다** — 착지 순서 제약 **없음**.
★**③ 오기 정정**: `STATE.md` 에 착지해 있던 「**#55 는 원장 3파일만 만진다**」가 **거짓**이었고
★**그 한 줄 때문에 ②가 보이지 않았다** ⇒ 그 자리에 반증을 붙였다. ★**교훈: 「어느 파일을 만지나」는 «PR 파일 목록»으로 확인하라.**
★★**이 설계의 «본래 대가»를 이제 적어 뒀다** — 「기록에 없는 픽스처 = red」는 **의도된 엄격함**이고
그 비용(픽스처를 더하는 회차가 표를 함께 만진다)이 **어디에도 없었다**(실측: `AGENTS.md` 관련 문장 0)
⇒ ★**`AGENTS.md` §Testing Boundaries 에 한 줄**로 남겼다.
★**개악 대조(⒞)**: 정규화 한 줄을 되돌리면 ★**mac 에서도** `table_keys_use_forward_slashes_on_every_platform` 가 **진다** · 복원 green.
★★**그래서 «문자열 치환»으로 구현했다** — `components()` join 이면 Unix 에서 역슬래시 입력이 **그대로 통과**해
★**개악이 mac 에서 안 잡힌다**(대가: Unix 파일명의 진짜 역슬래시는 바뀐다 — 픽스처엔 없고 표는 우리 것이다).
★Python 축은 **윈도우 없이** 실증했다(`PureWindowsPath`: `str()` 역슬래시 ↔ `as_posix()` 슬래시).
★★**게이트③ 착지 — PR #57 · `--merge`**(등재 repo · `merge_strategy: merge` 선언분). 게이트② **approve**(★`-fix2` 승계분) ·
★**핀 `888d8821` 불이동**(착수 실측 2026-09-17T06:21:43Z · `MERGEABLE/CLEAN` · base 뒤처짐 «0»).
★★**게이트③이 «두 번» 돌았다 — 첫 회차는 «옳게» 막혔다**: base 를 당기면 이 PR 자신의 핀 테스트가 red 였다
(형제 #59 가 들여온 `MakeConcat*` 3개가 표에 없었다) ⇒ 머지 티켓은 코드를 고치지 않으므로 `-fix2` 로 넘겼고,
그 회차가 base 당김 + 표 **3행**(`record-class-file-versions.py` 1회 · 추가만 · 삭제 0)을 넣어 풀었다.
⇒ ★**이 PR 이 세운 규율이 이 PR 에 처음 적용된 사례**이고, 검사기를 완화하지 않고 표를 고쳐 통과했다.
★핀에서 `ci-presence` **rc=0 CI_GREEN** · 이 형상에서 `cargo test --test test_fixture_pins` **3/3 green**(이 PR 이 만든 그 축).
★**동봉은 이 기록 한 줄뿐**이다 — 원장(worklog 쌍·`STATE`·`REPORT`)은 구현 회차가 이미 실었다.
★배포 **0**(이 저장소에 배포 워크플로 없음) · 자식 PR **0건** · 주기 자동 커밋 **0건** · 낡음 판별 도구 **0건**.
★★**착지 순서 고지** — 이 PR 이 세운 규율(「`.class` 를 더하면 `test-data/class-file-versions.txt` 에 같은 커밋으로 행을 넣는다 ·
미등재 픽스처는 핀이 **실패**시킨다」)은 ★**열린 PR #60(신규 `.class` 3개)·#61(17개)에 «소급 적용»된다** —
그쪽이 표에 행을 넣지 않고 착지하면 **main 이 red** 가 된다. 텍스트 충돌이 없어 `mergeable` 로는 보이지 않는 종류다.
- [rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p0] ★★**`test-data` 전체(150 클래스)의 클래스 파일 버전을 «동결»했다 — «통일»이 아니라.**
채택 제안 `2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p0`(worklog json `adoptedProposals` 기록).
★★**제안의 «비용 산정»을 바꿨다 — 기각이 아니라 «설계 교체»다.** 제안은 「**decide the intended target** per fixture or
per directory … recompiled … ★**The decision is the work**」라며 **통일**을 전제했는데, 정작 제안이 적은 이득은
「a regenerated fixture **cannot quietly start testing a different Java version's** bytecode shapes」 = ★**«드리프트 탐지»**다.
⇒ ★**목표를 «동결»로 바꾸면 그 「work」가 통째로 사라진다**: 지금 값을 기록하고 혼자 움직이면 red —
★**재컴파일 0 · 바이트 변경 0 · 「어느 타깃이 옳은가」 결정 자체가 불요.**
★**대가는 정직하게**: 이 축은 ★**섞임을 «고치지» 않고 «굳힌다»**(루트에 52·65·66·68·70 이 그대로 남는다).
그것이 옳은 이유는 ★**통일 = 재컴파일 = 바이트 변경 = 「그 픽스처가 무엇을 시험하는지」의 변경**이기 때문이고(제안 자신의 경고),
그 판정은 **별 축(후속 L)** 으로 넘겼다.
★★**ⓑ 이미 핀하는 축이 «둘» 있었고, 그 둘이 못 덮는 곳이 이 회차의 대상이었다**:
⑴`tests/test_fixture_pins.rs` 는 **`test-data/indy` 만** ⑵**생성기 코드**가 자기 산출물 **16개**의 버전을 소스에 박는다
(`make_ldc_fixtures.py` 의 `major=52` + 항목별 오버라이드) ⇒ ★**남는 132개 = 「`.java` 소스가 있어 손수 재컴파일 가능한 것」**이고,
★**루트의 66×8 · 68×1 · 70×3 이 그 사고가 «이미 일어난» 지문**이다.
★**만든 것**: `test-data/class-file-versions.txt`(150행 + 머리 주석에 「이것은 타깃이 아니라 동결이다」) ·
`test-data/src/record-class-file-versions.py`(머리 주석 **보존** · 없으면 **거부** · docstring 이 「실패를 잠재우려고 돌리지 마라」를 못박는다) ·
테스트 `committed_fixtures_keep_their_recorded_class_file_version`.
★★**세 방향을 «전부» 검사한다 — 이것이 급소다**: ⑴기록과 다름 ⑵★**기록에 없는 새 픽스처**
(★없으면 핀이 «옵트인»이 되어 **내일 추가되는 픽스처는 조용히 미보호** — 이 저장소가 반복해 잡은 그 형태의 변종) ⑶**유령 기록**.
★**개악 3종 전건 red**: M1 `Hello.class` 버전 바이트 `65→70`(= 다른 JDK 재생성과 **같은 형상**) ·
M2 미기록 `.class` 투입 · M3 표에만 있는 행 ⇒ 복원 **green**(`test_fixture_pins` **2 passed**).
★실패 문면이 **파일·두 버전·해소 명령**을 함께 말한다(`Hello.class: recorded 65.0, found 70.0`).
★**기록기 멱등**(재실행 시 표 **바이트 동일**) · `cargo test --all` **572 → 573 / 0 failed / 1 ignored** · DoD **7줄 전건 rc=0**.
★**알고 남긴 값**: 생성기 산출물 16개는 **이중 잠금**(생성기 + 표)이다 — ★**예외 목록을 두는 규칙보다 «전건 단일 규칙»이 덜 썩는다.**
- [rustjava-adopt-indy-fixture-jdk-pin-and-slot-accounting-p1] ★★**「어느 javac 이 만들었나」를 «기록»에서 «검증»으로 바꿨다.**
채택 제안 `2026-09-16-indy-fixture-jdk-pin-and-slot-accounting#p1`(worklog json `adoptedProposals` 기록). ★**제품 코드 무접촉.**
★★**제안의 결론 «둘»이 실측으로 반증됐다 — 그래서 제안이 «불가능»하다고 적은 쪽을 만들었다**:
Expand Down Expand Up @@ -232,6 +292,11 @@
★**여파**: 이 착지가 형제 **#54**(BSM 정적 인자)·**#55**(변이 감사)의 base 를 낡게 만든다.
★**#54 는 «코드 파일이 자동 병합»되도록 그 회차가 삽입 위치를 미리 갈라 뒀고**(그 done 회신의 `merge-tree` 실측),
**#55 는 원장 3파일만 만진다** ⇒ 두 형제 모두 충돌은 **원장 계열에 국한**된다(게이트③ 계약 2-c⒜ 범위).
★★**[정정 2026-09-17 · `…-jdk-pin-…-p0-fix`] 바로 윗줄의 「#55 는 원장 3파일만 만진다」는 «거짓»이었다.**
실측: `#55` 는 `test-data/indy/` 에 **`.class` 3개를 추가**하고 생성기·테스트도 만진다 ⇒ ★**원장만이 아니다.**
★**그 오기가 «무해하지 않았다»** — 같은 시기 `#57`(버전 동결 표)이 「픽스처가 늘면 표도 함께」를 요구하는데,
★**「#55 는 원장만」이라고 읽으면 그 상호작용이 «보이지 않는다».** 실제로 그 둘은 **텍스트 충돌 0인데 나중에 착지하는 쪽이 main 을 red** 로 만들었다.
⇒ ★**교훈: 「어느 파일을 만지나」는 «PR 파일 목록»으로 확인하라 — 요약에서 추론하지 마라.**
- [rustjava-ldc-tags-15-16-17-real-world-generator-survey] ★★**「못 쟀다」를 «쟀다»로 바꿨다 — ASM 은 태그 15/16/17 을 «낸다».**
채택 제안 `2026-09-16-ldc-tags-15-16-17#p2`(worklog json `adoptedProposals` 기록). ★**조사 회차 · 크레이트 무접촉**(파서·테스트 0).
★★**ASM 9.7.1 = 낸다(실증)** — `visitLdcInsn(Handle)`·`(Type.getMethodType)`·`(ConstantDynamic)` 15줄로 만든 클래스에서
Expand Down
Loading