Skip to content

Commit 79c9368

Browse files
author
jun0
committed
[rustjava-adopt-javac-fixture-provenance-verified-p0] merge origin/main — 원장 2파일 합집합(theirs 순서 무재배치 · 시각 순 삽입)
2 parents ac8914f + ae32007 commit 79c9368

67 files changed

Lines changed: 2486 additions & 102 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎REPORT.md‎

Lines changed: 109 additions & 0 deletions
Large diffs are not rendered by default.

‎STATE.md‎

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

99
## 완료
10+
- [rustjava-adopt-test-data-version-freeze-uniform-target-p0] ★★**루트 fixture 를 한 target 으로 모을 것인가 — «모으지 않는다».** 채택 제안 `2026-09-17-test-data-version-freeze#p0`(worklog json 기록). ★**코드 0행 · 재컴파일 0 · `.class` 0 변경** — 산출물 = `docs/test-data-target-policy.md`.
11+
★분포 **114**건 · **52×40 · 65×62 · 66×8 · 68×1 · 70×3**(fixture 자신에서 읽음).
12+
★★**양방향**: ⒜**버전이 답** — `StringBuilder` 3건을 21 로 재컴파일 → ★indy 생기고 StringBuilder 사라짐(★`StringConcat` 이 루트의 **유일한** indy) · `ThreadInterruption` `access$`×10 → **0 + NestMembers**(JEP 181) ⒝**아무 버전이나** — 20건 중 ★**16건 명령 시퀀스 완전 동일**.
13+
★**그 커버리지는 다른 데 없다** — 생성기 산출 64건에서 `StringBuilder` 0 · `access$` 0.
14+
★**이유**: 제안의 이득(「숫자 하나로 예측」)이 뒤집힌다 — 52 는 「전-indy·전-nestmate」라는 뜻을 **실제로 갖고**, 펴면 그 구분이 사라지며 유일한 커버리지가 지워진다.
15+
★**잃는 것**: 비균일 잔존(신규 target 규칙 **미수립**) · 16건은 그대로 · ★**본 것은 40 중 20** · `NativeMethod` 차이는 **미규명**.
16+
★`--all` **583/0/1**(불변 · base `8c7b473f` — 앞 회차의 579 는 #61 착지 전 base 다) · 되돌릴 조건 4개를 결정 문서에 명시.
1017
- [rustjava-adopt-javac-fixture-provenance-verified-p0] ★★**루트 픽스처 다섯이 재빌드되지 않는 이유 = `-g`.** 채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0`(worklog json 기록). ★**제품 Rust 0줄 · 커밋 `.class` 바이트 0 변경.**
1118
★**제안이 댄 두 설명이 둘 다 틀렸다** — 컴파일러 아니다(**26.0.1·26.0.2.1 에서 같은 다섯이 같게 행동**) · 소스 발산 아니다(`-g` 면 **지금 소스가 커밋 바이트를 낸다**).
1219
⇒ ★**제안 `tradeoff` 의 「재컴파일이 동작 변경이 될 수 있다」가 «성립하지 않는다»** — 재컴파일이 불요다.
1320
★고친 한 자리: 스크립트가 `--release` 를 픽스처에서 읽듯 **`-g` 도 픽스처에서**(상수풀 `LocalVariableTable`). ★배선 전 판별력 실측 **5/5 · 오탐 0**(보유 5 · 미보유 107).
1421
★양방향: 정상 **109/109/0 differed** ↔ 개악(파생 한 줄 no-op) **104/5 differed**(원래 다섯과 동일).
1522
★**잃는 것**: 스크립트는 **여전히 rc=1**(재빌드 불가 3건 = 제안이 미해결로 적은 별 축 · 넓히지 않았다) · 판정이 한 속성 유무에 걸린다.
1623
★`--all` **579/0/1**(불변) · `check-dod-ci-parity` OK.
24+
- [rustjava-adopt-classfile-error-cause-decision-p0] ★★**거부 사유를 세 층에 꿴다 — 「Invalid class file」 하나가 **14개** 문장이 된다(클래스 8 · 필드 3 · 메서드 3).** 채택 제안 `2026-09-17-classfile-error-cause-decision#p0`. ★**제품 동작 변경 있음**(사용자가 보는 `ClassFormatError` 메시지).
25+
★제안이 **all-or-nothing** 이라 못박은 넷을 다 했다: `InvalidFormat(&'static str)` · `InvalidClassFile(&'static str)`(★`From` 이 **버리던** 자리) · 경계 2자리 · ★**`validate_class` 8항 `||` → 규칙마다 `if`**.
26+
★★**사유를 꿰자 «평평한 오류가 가리던 것 둘»이 나왔다**: ⑴테스트가 **어느 층이 거부하는지를 틀리게 믿었다**(검증 아닌 **파서**) ⇒ ★단언을 실측에 맞췄다 ⑵술어 **이름이 낡아 있었다**(「in_the_pool」인데 **적재 가능성까지** 본다) ⇒ 사유는 규칙대로, ★**이름은 안 바꿨다**(리팩터 금지).
27+
★**양방향 — 세 층 전부 개악**: M1 경계 · M2 `From` 이 사유 버림 · M3 두 사유를 한 문자열로 접음(★잡는 것은 `contains` `:450`(dedup 제거로 452→450) — 초판이 귀속한 dedup 단언은 **상수 대 상수라 공허**했고 **걷어냈다**) · 복원 17/0.
28+
★★**대가**: ★**마지막 홉이 두 번 쓰여 있고 `test-utils` 사본은 «무검증»**(개악해도 579/0 · **합치지 않고 보고**) · 사유가 문자열이라 같은 문구 중복을 막는 것이 없다 · ★**PR #66 과 같은 함수**(충돌은 기계적).
29+
★`--all` **578 → 579/0/1** · `check-dod-ci-parity` → **「OK 두 축 모두 대칭차 0 — 명령 6개 · toolchain 2개로 «둘 다 일치»」**(rc=0).
30+
- [rustjava-adopt-link-stringconcatfactory-p2-fix2] ★★**#57 의 버전 표에 25행 등재 — 「착지 순서」가 만든 부채를 갚는다(PR #61).**
31+
★**막힌 것은 CI 도 충돌도 아니었다**: 핀 `85cf0fba` 에서 rc=0 CI_GREEN · `git merge origin/main` **코드 충돌 0** 인데
32+
★**합친 결과**가 #57 이 세운 「미등재 픽스처는 핀을 실패시킨다」를 어겼다(미등재 **25건** 재현).
33+
★생성기(`record-class-file-versions.py`)를 **돌려서** 채웠다 — 손편집 0. ★**삭제행 0**(`25 0`) = 기존 픽스처 재생성 0 이 이 회차의 안전선.
34+
★추가 25행이 이 PR 의 25개 `.class` 와 **집합 동일**(혼입 0) · ★**양방향**(한 행 제거 → red · 되돌림 → green).
35+
★원장 충돌 2건은 **합집합**(줄 단위 양방향 보존 · 한쪽 통째 채택 0) · #62 기여 전건 잔존.
36+
★`test_fixture_pins` **3/0** · `--all` **581/0/1** · DoD 7명령 rc=0.
37+
★★**[승계 -p2-fix3] 코드 2파일 합집합 — ours 의 «삭제»가 의도가 아니었다.**
38+
★선행 머지 둘(`e53b2142`·`514d5b08`)이 부모2의 makeconcat 가족(`fieldref`·`make_concat_call_site`·`LINKED`)을 **결과에서 떨어뜨렸다** ⇒ **되살렸다**.
39+
★반면 `.rs` 의 16줄은 **진짜 ours 의도**(metafactory 를 링크하니 「링크 안 된다」 단언이 거짓) ⇒ **되살리지 않았다**. 회계: `.py` ↔main **156/0** · `.rs` ↔HEAD **86/0** · ↔main **182/29**.
40+
★**복원분이 산 코드임을 실행으로 증명** — MakeConcat 3장을 지우고 재생성 → **바이트 동일 복구**(복원 전 생성기로는 **불가**).
41+
★양방향 개악 ours 4 red / theirs 1 red · `--all` **583/0/1** · DoD 7명령 rc=0. ★그 회차는 「착지 금지 — 게이트② 재검이 먼저」로 끝났고, ★**그 재검이 approve 로 닫혔다**(아래).
42+
★★**게이트③ 착지 — PR #61 · `--merge`**(등재 repo `contracts/upstream-sync-repos.conf:22` · 티켓 `merge_strategy: merge` 선언분 ⇒ ★**계보 보존**). ★한 PR 이 `-p2`·`-fix`·`-fix2`·`-fix3` **네 회차**를 함께 싣는다.
43+
게이트② **approve**(리니지 최신 회신 `…-p2-fix3.review.md`) · 핀 **`5f7ce1a8`** ↔ 착수 시 PR head **동일**(불이동) · ★**`MERGEABLE/CLEAN` · base 뒤처짐 «0»** ⇒ 충돌 해소·base 당김 **둘 다 불요**(`-fix3` 이 이미 당겼다).
44+
★핀에서 `ci-presence` **rc=0 CI_GREEN**(3건 전건) · 자식 PR **0건** · 배포 **0**(배포 워크플로 없음) · 주기 자동 커밋 **0건** · 라이브 실행 주체 **없음**.
45+
★**선행 `-merge` 두 건은 흡수할 것이 없었다** — `…-fix-merge`(`needs-fix-ticket`)·`…-fix2-merge`(`code-conflict-out-of-scope`) 둘 다 **머지 0·커밋 0·푸시 0** 으로 멈췄다.
1746
- [rustjava-adopt-class-format-mutation-audit-p0-fix] ★★**판정식을 `given` 에서 파생시킨다 — 게이트② 반려 승계(PR #63).**
1847
★**급소 한 줄**: `repaired` 를 정본 인자로 **다시 짓고** 있어 둘째 결함을 버렸다 ⇒ 「single-defect 인가」를 묻는데 **입력이 이미 single-defect** 였다(순환 · 18중 **14건**).
1948
★처방은 발명이 아니라 **옮겨오기** — 이미 옳던 `indy` 근접실패 형태를 `add()` 한 곳으로 모아 **4족 전건**이 지나게 했고,
@@ -63,6 +92,15 @@
6392
게이트② **approve** · 핀 **`c0413f81`** ↔ 착수 시 PR head **동일**(불이동) · ★**`MERGEABLE/CLEAN` · base 뒤처짐 «0»** ⇒ 충돌 해소·base 당김 **둘 다 불요**.
6493
★핀에서 `ci-presence` **rc=0 CI_GREEN** · 자식 PR **0건** · 배포 **0**(배포 워크플로 없음) · 주기 자동 커밋 **0건** · 라이브 실행 주체 **없음**.
6594
★★**착지시킨 것은 «기각 기록»이다** — 제품 코드 **0줄**이 정상이고, 착지 diff 는 원장 4파일뿐이다.
95+
- [rustjava-adopt-ldc-tags-real-world-generator-survey-p1] ★★**kotlinc·scalac 타깃 형상 조사 — 0. 그러나 «다른 0».** 채택 제안 `…-survey#p1`(worklog json `adoptedProposals` 기록) · ★**제품 Rust 0줄**.
96+
★조사 회차가 **「못 쟀다」로 비워 둔 칸**을 채웠다 — 종전 축은 stdlib(호환성용 컴파일)이라 「이 코퍼스에서 0」과 「이 기능들에서 0」이 달랐다.
97+
★**kotlinc 2.4.20 → 0 · scalac 3.9.0 → 0**. ★★**풀 수치가 그 0 을 읽을 값으로 만든다**(Kotlin `MH 7·MT 6` · Scala `11·6` = **indy 가 실제로 돌았다**) ⇒ 「안 썼다」가 아니라 **「썼는데 `ldc` 자리에 안 온다」**.
98+
★**태그 17 은 풀에도 0** ⇒ ★**이 형상들에서는** 두 컴파일러 다 **condy 를 안 낸다**(한정은 나머지 수와 같다 — 컴파일러당 프로그램 1개).
99+
★**양방향**: 양성 대조군 `test-data/ldc` → `{MH 1, MT 2, Dynamic 7}` ⇒ 「스캐너가 못 본다」 배제 · 오차막대 **0.00%**.
100+
★**제안의 값 전제가 부분적으로 거짓**이었다 — openjdk 26 은 **2026-07-22 부터** 설치돼 있었다(`INSTALL_RECEIPT` **`time`** = `1784711495` · ★`source_modified_time` 아님) — 조사(09-16)보다 **56일** 전.
101+
★★**대가**: 머신에 `kotlin`·`scala` **설치됨**(재측정 위해 남겼다) · ★**추가 신고 — `openjdk` 기본 링크 26.0.1 → 26.0.2.1 승격**(의존성 · `/opt/homebrew/opt/openjdk`).
102+
★`brew uninstall kotlin scala` 로는 **안 돌아온다**(Homebrew 7 은 `brew switch` 없음) ⇒ 처방은 **26.0.1 keg 경로 핀**(`JAVA_HOME=/opt/homebrew/Cellar/openjdk/26.0.1` · 실행 확인). ★keg-only 라 `PATH` 의 `java`(=`/usr/bin/java`)는 **불변** — 영향은 opt 경로를 명시적으로 쓰는 소비자뿐.
103+
★컴파일러당 프로그램 1개 · **CI 불가**.
66104
- [rustjava-adopt-link-stringconcatfactory-p1-fix2] ★★**base 당김 — 그런데 막고 있던 코드 충돌은 «이미 없었다»(PR #60).**
67105
★**전제 반증**: 「`make_indy_fixtures.py` 4구역 충돌」은 `-p1-fix` 가 **14:10 `0f06b93f`** 로 합집합 해소했고 게이트②가 **15:43 그 head 를 approve** 했다.
68106
발권 근거(12:12 blocked 회신)가 그 사이 낡은 것이다. ★**재발 불가**도 확인 — 뒤진 9커밋 중 그 파일을 만진 것 **0건**.
@@ -81,6 +119,26 @@
81119
★**커밋된 클래스 파싱이 전/후 «144/12 동일»**(새로 거부 0). ★OpenJDK 26 은 같은 파일을 `ClassFormatError: argument_index 4 has bad constant type` 로 거부한다.
82120
★**안 하면**: 링커에서 `UnsupportedOperationException` — 「파손」을 「미지원」이라 말하게 된다.
83121
★개악 2종 red(존재만 되돌리기 · ★집합에 Utf8 한 칸 추가) · `--all` **575/0/1** · 새 픽스처 **0**(바이트 패치).
122+
- [rustjava-adopt-link-stringconcatfactory-p2-fix] ★★**포획 «순서»를 값으로 잠그고 «호스트 abort»를 없앤다 — 게이트② 반려 승계(PR #61).**
123+
★**검수자 F1·F2 둘 다 옳았다.** F1: 픽스처 전건이 포획 1개 이하라 **순서 축이 무관측**이었고 RM4(읽기 순서 역전)가 **576 green** 이었다
124+
⇒ 포획 2개 람다 둘 추가(`(String,int)`=`a:7` 글자로 · `(int,int)`=`120` ★값으로만) ⇒ **RM4 red**(`7:a`·`2001`).
125+
F2: 서술자가 `I` 인 콜사이트가 **호스트 프로세스를 죽였다**. ★**고친 자리 = `validation.rs` 의 «사용 지점»**(JVMS 4.4.10:
126+
InvokeDynamic=메서드 · Dynamic=필드) — 일반 `NameAndType` 팔의 `||` 는 **옳으므로 두었다**(Fieldref·Methodref 공유 항목).
127+
★근거는 실측이다: **OpenJDK 26 도 같은 파일을 `ClassFormatError`** 로 거부한다 ⇒ 「미지원」이 아니라 「파손」이 옳은 진단.
128+
★**조이기 비용 선측정**: 클래스 175 · indy/condy 44건 중 새로 위법 **1건**(이 회차 픽스처)뿐.
129+
★`lower()` 의 `try_parse` 는 둘째 층이고 ★**독립 관측 불가임을 명시**했다(남긴 근거 = 비용 비대칭).
130+
★개악 2종 전건 red · `--all` **578/0/1** · 픽스처 재생성 멱등(형제 #59 생성기와 합친 뒤에도 바이트 불변).
131+
- [rustjava-adopt-link-stringconcatfactory-p2] ★★**`LambdaMetafactory.metafactory` 링크 — 람다·메서드 참조가 «돈다».**
132+
채택 제안 `2026-09-16-link-stringconcatfactory#p2`(worklog json `adoptedProposals` 기록). ★**제품 동작 변경 있음**
133+
(람다 포함 클래스: 적재 거부 → 실행). ★`jvm/` 무접촉 · `java.lang.invoke` **0줄**.
134+
★★**제안의 「java.lang.invoke 가 불가피 · L」은 틀렸다** — 콜사이트가 의미하는 것은 핸들 사슬이 아니라 **객체**이고,
135+
그걸 만들 두 축이 **이미 있었다**(`MethodBody::Rust(JvmCallback)` · `Jvm::register_class`). ⇒ 팩토리가 스핀할 클래스를 직접 만든다.
136+
★**경계 = 어댑터**(박싱·언박싱·확대): 통과가 아니면 **링크하지 않는다** — 판정이 **lowering 시점**이라 「로드되거나 안 되거나」이고
137+
호출 «도중» 실패 경로가 없다. `LambdaBoxing.class` 가 그 경계를 잠근다(OpenJDK 는 3을 찍는다).
138+
★★**관측 가능성 3종이 «처음엔 안 죽었다»** — ⑴void 버림(인터프리터 «밖» = `Thread.run()` 의 `()` 변환에서만 보인다)
139+
⑵REF_invokeSpecial(javac 11+ 는 안 낸다 ⇒ **--release 8** 픽스처 · 핀을 «픽스처별»로 바꿨다) ⑶정적 인자 개수·종류(손조립 2종).
140+
★**개악 14종 전건 red** · `cargo test --all` **573 → 576 / 0 failed / 1 ignored** · `LambdaKinds` 10줄이 OpenJDK 26.0.1 과 일치 ·
141+
DoD 7명령 rc=0 · 픽스처 재생성 멱등.
84142
- [rustjava-adopt-link-stringconcatfactory-p1] ★★**레시피가 콜사이트와 어긋날 때 — 제안의 「싸고 옳다」가 두 겹으로 거짓이었다.**
85143
채택 제안 `2026-09-16-link-stringconcatfactory#p1`(worklog json `adoptedProposals` 기록). ★**제품 동작 변경 있음.**
86144
★**제안의 처방은 기각**(`classfile/validation.rs`/`ClassFormatError`) — ★**OpenJDK 26.0.1 에 픽스처 3종을 직접 돌린 실측**이 근거다:

‎classfile/src/attribute.rs‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ pub struct MethodHandleRef {
6969
}
7070

7171
impl MethodHandleRef {
72-
fn resolve(constant_pool: &BTreeMap<u16, ConstantPoolItem>, index: u16) -> Option<Self> {
72+
/// Public because a bootstrap method's *static arguments* are method handles too, and the only
73+
/// thing that can read them is a consumer outside this crate: `BootstrapMethod::arguments` is
74+
/// raw indices by design (see below), so whoever links a call site resolves them itself.
75+
/// `LambdaMetafactory.metafactory` is the case that made this necessary — its second static
76+
/// argument is the implementation method, and it is a `CONSTANT_MethodHandle`.
77+
pub fn resolve(constant_pool: &BTreeMap<u16, ConstantPoolItem>, index: u16) -> Option<Self> {
7378
let ConstantPoolItem::MethodHandle {
7479
reference_kind,
7580
reference_index,
@@ -101,6 +106,19 @@ impl MethodHandleRef {
101106
}
102107
}
103108

109+
/// The descriptor a CONSTANT_MethodType names (JVMS 4.4.9).
110+
///
111+
/// Here for the same reader as `MethodHandleRef::resolve`: two of `LambdaMetafactory`'s three
112+
/// static arguments are method types. A method type *is* just its descriptor as far as anything
113+
/// outside `java.lang.invoke` is concerned, so this hands back the string rather than a type.
114+
pub fn method_type_descriptor(constant_pool: &BTreeMap<u16, ConstantPoolItem>, index: u16) -> Option<Arc<String>> {
115+
let ConstantPoolItem::MethodType { descriptor_index } = constant_pool.get(&index)? else {
116+
return None;
117+
};
118+
119+
constant_pool.get(descriptor_index)?.utf8()
120+
}
121+
104122
/// One entry of the `BootstrapMethods` attribute (JVMS 4.7.23).
105123
pub struct BootstrapMethod {
106124
pub method: MethodHandleRef,

‎classfile/src/class.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ impl ClassInfo {
100100
}
101101

102102
pub fn parse(file: &[u8]) -> Result<Self, ClassFileError> {
103-
let (remaining, result) = Self::parse_info(file).map_err(|_| ClassFileError::InvalidFormat)?;
103+
let (remaining, result) = Self::parse_info(file).map_err(|_| ClassFileError::InvalidFormat("truncated or unparsable class file"))?;
104104
if !remaining.is_empty() {
105-
return Err(ClassFileError::InvalidFormat);
105+
return Err(ClassFileError::InvalidFormat("extra bytes after the end of the class file"));
106106
}
107107
if result.major_version < 45 {
108-
return Err(ClassFileError::InvalidFormat);
108+
return Err(ClassFileError::InvalidFormat("class file version predates 45.0"));
109109
}
110110
if result.major_version > 70 {
111111
return Err(ClassFileError::UnsupportedVersion(result.major_version));

‎classfile/src/error.rs‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
/// Why a class file was refused.
2+
///
3+
/// `InvalidFormat` carries the cause so the rejection can say what is wrong instead of repeating
4+
/// one sentence for every reason — the shape `ClassDefinitionError::UnsupportedFeature` already
5+
/// used. A `&'static str` rather than a variant per rule: the set is open (every new rule adds
6+
/// one) and nothing branches on it, so a string is what a caller actually needs.
7+
///
8+
/// The words are the message a user sees, so they read as a JVM does — "multiple BootstrapMethods
9+
/// attributes", not "AtMostOneBootstrapMethods".
110
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
211
pub enum ClassFileError {
3-
InvalidFormat,
12+
InvalidFormat(&'static str),
413
UnsupportedVersion(u16),
514
}

‎classfile/src/lib.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ mod opcode;
1212
mod validation;
1313

1414
pub use {
15-
attribute::{AttributeInfo, AttributeInfoCode, BootstrapMethod, MethodHandleKind, MethodHandleRef},
15+
attribute::{AttributeInfo, AttributeInfoCode, BootstrapMethod, MethodHandleKind, MethodHandleRef, method_type_descriptor},
1616
class::ClassInfo,
1717
constant_pool::{ConstantPoolReference, FieldMethodref},
1818
error::ClassFileError,
1919
field::FieldInfo,
2020
method::MethodInfo,
21-
opcode::{Opcode, StringConcatCallSite},
21+
opcode::{LambdaCallSite, Opcode, StringConcatCallSite},
2222
};

0 commit comments

Comments
 (0)