From f2c83174c682502e6ea159d84c615049e535fe08 Mon Sep 17 00:00:00 2001 From: jun0 Date: Thu, 17 Sep 2026 01:16:29 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[rustjava-adopt-bound-bootstrap-method-attr?= =?UTF-8?q?-index-p0]=20feat(classfile):=20BootstrapMethods=20=EB=A5=BC=20?= =?UTF-8?q?=EB=91=90=20=EB=B2=88=20=EC=84=A0=EC=96=B8=ED=95=9C=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=A5=BC=20=EA=B1=B0=EB=B6=80=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JVMS 4.7.23 은 최대 한 개만 허용하는데, bootstrap_method_indices_resolve 의 find_map 은 첫 표를 쓰고 나머지를 조용히 무시했다 — 인덱스가 어느 표에 대해 경계 검사되는지가 임의였다. 참조 JVM(OpenJDK 26.0.1)은 같은 파일에 ClassFormatError: Multiple BootstrapMethods attributes 를 내고, 표가 하나뿐인 대조군은 rc=0 으로 로드한다. 술어를 옆에 두고 validate_class 에 이었다 — indices_resolve 는 「인덱스가 실재 항목을 가리키는가」 한 문장이고 「표가 몇 개인가」는 다른 문장이라, 접어 넣으면 이름까지 바꿔야 한다. 픽스처는 같은 유효한 표를 바이트 동일하게 두 번 써서 거부 원인을 「둘이라는 사실」로 고정했다. 개악 양방향(호출 제거 · 상수 통과) 전건 red · cargo test --all 571 passed. --- REPORT.md | 25 +++++ STATE.md | 23 +++++ classfile/src/validation.rs | 22 +++++ ...16-reject-duplicate-bootstrap-methods.json | 41 +++++++++ ...9-16-reject-duplicate-bootstrap-methods.md | 86 ++++++++++++++++++ test-data/ldc/LdcDynamicDuplicateBSM.class | Bin 0 -> 388 bytes test-data/src/ldc/make_ldc_fixtures.py | 24 +++++ tests/test_class_format.rs | 20 ++++ 8 files changed, 241 insertions(+) create mode 100644 docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.json create mode 100644 docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.md create mode 100644 test-data/ldc/LdcDynamicDuplicateBSM.class diff --git a/REPORT.md b/REPORT.md index b3c1b6f7..72771f6f 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,4 +1,29 @@ # REPORT +## [2026-09-16] `BootstrapMethods` 를 두 번 선언한 클래스를 거부한다 (rustjava-adopt-bound-bootstrap-method-attr-index-p0) +- 무엇을: JVMS 4.7.23 은 `BootstrapMethods` 를 **최대 한 개**만 허용한다. 두 개를 실은 파일을 ★**거부**한다 + (종전에는 `find_map` 이 **첫 표**를 쓰고 나머지를 조용히 무시했다). +- 왜: 채택 제안 `2026-09-16-bound-bootstrap-method-attr-index#p0`(운영자 tower 패널 채택). +- 사용자 영향: ★**진단이 바뀐다** — `UnsupportedOperationException`(「이 런타임이 아직 못 한다」) → + ★`ClassFormatError`(「이 파일이 깨졌다」). ★**참조 JVM 과 같은 판정**이 된다. +- ★★**참조 JVM 이 근거다**(observable behavior · OpenJDK 소스 미참조): OpenJDK 26.0.1 은 같은 파일에 + **`ClassFormatError: Multiple BootstrapMethods attributes in class file`** 를 내고, ★**표가 하나뿐인 대조군은 + rc=0 으로 로드**한다 ⇒ 픽스처의 결함이 «둘이라는 사실» 하나임이 참조 구현으로 확증된다. +- ★**고친 자리는 «한 곳»**: `validate_class` 에 술어 `at_most_one_bootstrap_methods_attribute` 를 이었다. + ★`bootstrap_method_indices_resolve` 는 **무접촉** — 그 함수의 doc 이 스스로 「인덱스가 실재 항목을 가리키는가」라는 + **한 문장**임을 선언하고, 「표가 몇 개인가」는 **다른 문장**이다(접어 넣으면 이름까지 바꿔야 한다). + ★**새 관용 0** — 필드 `ConstantValue`·메서드 `Code` 가 이미 쓰는 **개수 세기** 모양 그대로다. +- ★**픽스처는 «결함이 하나»가 되게 지었다**: `LdcDynamicDuplicateBSM.class` = 같은 유효한 표를 ★**바이트 동일**하게 두 번. + 어느 한 표만 있어도 정상 파일이라 ★**거부 원인이 «둘»로 고정**된다(둘째 표를 다르게 하면 다른 규칙이 먼저 물어 + 테스트가 «이름과 다른 이유»로 통과한다). 구조 실측: 속성 `['BootstrapMethods','BootstrapMethods']` · 본문 동일 `True`. +- ★★**제안의 한 문장은 «과했다»**: 「생성기가 만들 수 없는 픽스처가 필요하다」 — ★**8줄 래퍼로 됐다** + (속성 목록이 빌더에 그대로 전달된다). **관측은 맞았고 비용 추정이 틀렸다.** +- 검증: 개악 **양방향** — ⑴호출부에서 술어 제거(=제안 이전 상태) **red** ⑵술어 본문을 **`true`(상수 통과)** 로 **red** + (★⑵가 없으면 「검사가 상수로 뭉개진」 축을 못 잡는다) · 정상 **green** · + `cargo test --all` **571 passed / 0 failed / 1 ignored**(27 스위트 **전건 합산**) · 픽스처 재생성 **멱등**(기존 11 바이트 동일) · DoD 7명령 rc=0. +- 후속 추천: 클래스 수준의 **다른 「최대 1개」 속성**(SourceFile·EnclosingMethod·Signature…)도 같은 규칙을 받아야 하는지 **판정** + — ★이번 건이 검사를 얻은 이유는 「중복이 하류에 임의 선택을 만든다」이고, 파서가 무시하는 속성엔 그 논거가 **전이되지 않는다**. + 상세 = `docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.md`. + ## [2026-09-16] `ldc` 태그 15/16/17 — ★**ASM 은 «낸다»**(Kotlin·Scala·Lombok 산출물은 0) (rustjava-ldc-tags-15-16-17-real-world-generator-survey) - 무엇을: 선행 회차가 남긴 **「못 쟀다」**(ASM·Kotlin·Scala·Lombok)를 **쟀다**. 조사 회차 — ★**크레이트 무접촉**(파서·테스트 0). - 왜: 채택 제안 `2026-09-16-ldc-tags-15-16-17#p2`. javac 은 안 낸다가 이미 증명됐고, **직접 바이트코드를 짜는 도구**가 남아 있었다. diff --git a/STATE.md b/STATE.md index f2b3969c..81f3ada6 100644 --- a/STATE.md +++ b/STATE.md @@ -4,6 +4,29 @@ (없음 — 2026-09-16 실측: 착수 시 진행 티켓 0 · 열린 PR 0. ※「열린 PR 0」은 ★**이 회차 PR 착지 시점 기준**이다 — 회신 시점에는 그 PR 자신이 열려 있다) ## 완료 +- [rustjava-adopt-bound-bootstrap-method-attr-index-p0] ★★**`BootstrapMethods` 를 «두 번» 선언한 클래스를 거부한다 — 임의 선택을 없앴다.** + 채택 제안 `2026-09-16-bound-bootstrap-method-attr-index#p0`(운영자 tower 패널 채택 · worklog json `adoptedProposals` 기록). + ★**JVMS 4.7.23 = 최대 한 개.** 종전에는 `find_map` 이 **첫 표**를 쓰고 나머지를 **조용히 무시**했다 ⇒ + ★**`bootstrap_method_attr_index` 가 «어느 표»에 대해 경계 검사되는지가 임의**였고 그 사실이 아무 데도 드러나지 않았다. + ★**전/후**: `UnsupportedOperationException`(「아직 못 한다」) → ★`ClassFormatError`(「이 파일이 깨졌다」). + ★★**참조 JVM 이 근거다**(observable behavior · OpenJDK 소스 미참조): OpenJDK 26.0.1 → + **`ClassFormatError: Multiple BootstrapMethods attributes in class file`** · ★**표 하나짜리 대조군은 rc=0 로드**. + ★**고친 자리 «한 곳»** — `validate_class` 에 술어 `at_most_one_bootstrap_methods_attribute` 를 이었다. + ★`bootstrap_method_indices_resolve` **무접촉**(그 doc 이 스스로 「인덱스가 실재 항목을 가리키는가」라는 한 문장임을 + 선언한다 — 「표가 몇 개인가」는 다른 문장이고, 접어 넣으면 **이름까지 바꿔야** 한다) · ★**새 관용 0** + (필드 `ConstantValue`·메서드 `Code` 가 이미 쓰는 **개수 세기** 모양 그대로). + ★★**픽스처를 «결함이 하나»가 되게 지었다** — `LdcDynamicDuplicateBSM.class` = **같은 유효한 표를 바이트 동일하게 두 번**. + 어느 한 표만 있어도 정상 파일이라 ★거부 원인이 «둘이라는 사실»로 **고정**된다(둘째를 다르게 하면 다른 규칙이 먼저 물어 + 테스트가 «이름과 다른 이유»로 통과한다 — 이 저장소가 #49 에서 세운 그 규율). + 구조를 **측정**했다: 속성 `['BootstrapMethods','BootstrapMethods']` · 두 본문 **바이트 동일 True**. + ★★**제안의 한 문장은 «과했다»** — 「생성기가 만들 수 없는 픽스처가 필요하다」는 **거짓**이고 ★**8줄 래퍼**로 됐다 + (속성 목록이 빌더에 그대로 전달된다). ⇒ **관측은 맞았고 «비용 추정»이 틀렸다** — 다음 사람이 같은 이유로 미루지 않게 적는다. + ★**개악 대조 양방향**: ⑴호출부에서 술어 제거(= 제안 이전 상태) **red** ⑵술어 본문을 **`true`(상수 통과)** 로 **red** · + 정상 **green**. ★**⑵가 없으면 「검사가 상수로 뭉개진」 축을 못 잡는다**(⑴만으로는 호출 삭제만 잡힌다). + ★`cargo test --all` **571 / 0 failed / 1 ignored**(27 스위트 **전건 합산** — 꼬리만 세지 않았다) · + 픽스처 재생성 **멱등**(기존 11 전건 바이트 동일 · 신규 1) · DoD **7줄 전건 rc=0**. + ★**잃는 것**: 지금까지 «로드되던» 파일 하나가 거부된다 — 다만 그 형상은 어제 이 저장소가 잰 대로 + **javac·kotlinc·scalac·Lombok 산출물 5,479 클래스에 0**이고 ASM 으로도 «일부러» 만들어야 나온다. - [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줄로 만든 클래스에서 diff --git a/classfile/src/validation.rs b/classfile/src/validation.rs index b6bfa5a6..41ada08e 100644 --- a/classfile/src/validation.rs +++ b/classfile/src/validation.rs @@ -16,6 +16,7 @@ pub(crate) fn validate_class(class: &ClassInfo) -> Result<(), ClassFileError> { || !validate_constant_pool(&class.constant_pool) || !constant_pool_tags_fit_the_class_file_version(class) || !bootstrap_method_indices_resolve(class) + || !at_most_one_bootstrap_methods_attribute(class) { return Err(ClassFileError::InvalidFormat); } @@ -134,6 +135,27 @@ fn bootstrap_method_indices_resolve(class: &ClassInfo) -> bool { }) } +/// JVMS 4.7.23: at most one `BootstrapMethods` attribute may appear in a ClassFile's attributes +/// table. A file carrying two is broken, not a file using a feature this runtime lacks. +/// +/// Kept separate from `bootstrap_method_indices_resolve` because it is a different sentence: that +/// one asks whether an index names a real entry, this one asks how many tables exist. Folding it in +/// would also mean renaming that function for a rule it did not previously make. +/// +/// It matters because `bootstrap_method_indices_resolve` resolves the table with `find_map`, which +/// stops at the first one. With two tables that choice is arbitrary — the index would be bounded +/// against whichever came first and the other silently ignored — so the honest answer is to reject +/// the file rather than pick. Counting is the same shape `validate_class` already uses for the +/// per-member "at most one" rules (`ConstantValue` on a field, `Code` on a method). +fn at_most_one_bootstrap_methods_attribute(class: &ClassInfo) -> bool { + class + .attributes + .iter() + .filter(|attribute| matches!(attribute, AttributeInfo::BootstrapMethods(_))) + .count() + <= 1 +} + fn validate_constant_pool(constant_pool: &BTreeMap) -> bool { constant_pool.values().all(|item| match item { ConstantPoolItem::Class { name_index } => constant_pool diff --git a/docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.json b/docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.json new file mode 100644 index 00000000..c8e92453 --- /dev/null +++ b/docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.json @@ -0,0 +1,41 @@ +{ + "schema": "worklog/v1", + "date": "2026-09-16", + "taskId": "rustjava-adopt-bound-bootstrap-method-attr-index-p0", + "summary": "A class declaring BootstrapMethods twice is now rejected as malformed instead of being read with whichever table came first. JVMS 4.7.23 allows at most one; OpenJDK 26 answers ClassFormatError: Multiple BootstrapMethods attributes for the same file.", + "changes": [ + "classfile/src/validation.rs: new at_most_one_bootstrap_methods_attribute predicate, wired into validate_class's rejection chain. bootstrap_method_indices_resolve is untouched — counting tables is a different sentence from bounding an index, and folding it in would have meant renaming that function", + "test-data/src/ldc/make_ldc_fixtures.py: new duplicate_bootstrap_methods(inner) wrapper that re-appends the attribute its inner builder wrote", + "test-data/ldc/LdcDynamicDuplicateBSM.class: new fixture, the same valid table byte-for-byte twice", + "tests/test_class_format.rs: test_a_class_declaring_bootstrap_methods_twice_is_malformed" + ], + "verification": [ + "proposal re-measured at start: the find_map it names is still at classfile/src/validation.rs:117, so the proposal is still live", + "reference JVM, OpenJDK 26.0.1: the fixture gives 'java.lang.ClassFormatError: Multiple BootstrapMethods attributes in class file LdcDynamicDuplicateBSM', while the single-table control LdcDynamic loads with rc=0 and no output", + "our runtime before the fix: UnsupportedOperationException 'ldc of a dynamically-computed constant'; after: ClassFormatError 'Invalid class file'; the single-table control's answer is unchanged, which shows the change is narrow", + "fixture structure measured, not assumed: class attributes are ['BootstrapMethods', 'BootstrapMethods'] and the two bodies are byte-identical", + "fixture regeneration is idempotent: 11 existing .class files byte-identical, 1 added", + "mutation M1, removing the call from validate_class (i.e. the pre-proposal state): the new test goes red", + "mutation M2, replacing the predicate body with `true` (constant pass): red at the same assertion — M1 alone would not catch a check flattened to a constant", + "restore: test_class_format 12 passed / 0 failed", + "full suite: 571 passed / 0 failed / 1 ignored, counted by summing every 'test result' line rather than reading the tail", + "DoD 7 commands all rc=0" + ], + "issues": [ + "The proposal's tradeoff said the fixture was one 'the generator cannot currently build'. That was too strong: the attribute list is handed to each builder, so an 8-line wrapper that re-appends what the inner builder wrote is enough. The observation was right, the cost estimate was not." + ], + "adoptedProposals": [ + "2026-09-16-bound-bootstrap-method-attr-index#p0" + ], + "proposals": [ + { + "title": "Decide whether the other class-level attributes also need cardinality rules", + "plainSummary": "We now reject a class that declares two bootstrap tables. Several other class attributes are also allowed only once each, and nothing checks those.", + "userBenefit": "The same honesty applied consistently: a file carrying two of something the spec allows one of is reported as broken, whichever attribute it is.", + "why": "JVMS 4.7 marks several ClassFile attributes as at-most-one (SourceFile, EnclosingMethod, Signature, SourceDebugExtension, and the module-related ones). This round added the rule for BootstrapMethods because a proposal named it and because the resolver silently picks the first of two. Whether the others deserve the same treatment is a separate question, and the answer may be no for the ones nothing reads.", + "tradeoff": "Rejecting more files that load today, for attributes whose duplication changes nothing we act on — the BootstrapMethods case earned its check because a duplicate makes an arbitrary choice observable downstream, and that argument does not transfer to an attribute the parser ignores. Doing it uniformly would also need a fixture per attribute.", + "effort": "S", + "target": "classfile/src/validation.rs" + } + ] +} diff --git a/docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.md b/docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.md new file mode 100644 index 00000000..5fb5f21c --- /dev/null +++ b/docs/worklog/2026-09-16-reject-duplicate-bootstrap-methods.md @@ -0,0 +1,86 @@ +# 2026-09-16 — `BootstrapMethods` 를 두 번 선언한 클래스를 거부한다 + +티켓 `rustjava-adopt-bound-bootstrap-method-attr-index-p0` — 채택 제안 +`2026-09-16-bound-bootstrap-method-attr-index#p0`(운영자가 tower 패널에서 채택). + +## ⓐ 제안이 «지금도» 참인가 — 재서 확인했다 + +원문 `why`: 「`bootstrap_method_indices_resolve` uses `find_map`, which takes the first and never +looks for a second」. ★**착수 시 재측: 그 `find_map` 은 `classfile/src/validation.rs:117` 에 그대로 있다.** +⇒ 제안은 **유효**하다(그 worklog 를 낸 회차 이후 이 자리는 움직이지 않았다). + +JVMS 4.7.23 은 ClassFile 속성표에 `BootstrapMethods` 를 ★**최대 한 개**만 허용한다. + +## ⓑ 이미 같은 것을 하는 축이 있는가 — 없다. 단 «모양»은 이미 있다 + +`validate_class` 는 이미 **부재/중복 개수**를 세는 규칙을 둘 갖고 있다: +필드의 `ConstantValue` (`constant_values.len() > 1` → 거부) · 메서드의 `Code` (`code_attributes != 1` → 거부). +★**클래스 «자신»의 속성표에는 그런 규칙이 하나도 없었다.** ⇒ 새 관용을 발명하지 않고 **그 모양을 그대로** 썼다. + +## ⓒ 제안이 틀린 부분 — ★**`tradeoff` 의 한 문장은 «과했다»** + +원문: 「It also needs a fixture **the generator cannot currently build**, since the attribute list is +assembled per fixture.」 ★**그것은 사실이 아니다.** 속성 목록은 빌더에게 `attributes` 리스트로 **그대로 전달**되므로, +기존 빌더를 감싸 **그 빌더가 쓴 속성을 한 번 더 append** 하는 래퍼 **8줄**이면 된다 +(`duplicate_bootstrap_methods(inner)`). 생성기 구조를 바꿀 필요가 없었다. +⇒ ★**제안의 관측은 맞았고, 비용 추정이 틀렸다.** 그 차이를 적어 둔다 — 다음 사람이 같은 이유로 미루지 않게. + +## 고친 자리 — 한 곳 + +`classfile/src/validation.rs` 에 술어 하나를 더하고 `validate_class` 의 거부 조건에 이었다: + +```rust +fn at_most_one_bootstrap_methods_attribute(class: &ClassInfo) -> bool { + class.attributes.iter().filter(|a| matches!(a, AttributeInfo::BootstrapMethods(_))).count() <= 1 +} +``` + +★**`bootstrap_method_indices_resolve` 를 «고치지» 않고 «옆에» 뒀다** — 그 함수의 doc 이 스스로 +「인덱스가 실재 항목을 가리키는가」라는 **한 문장**임을 선언하고 있고, 「표가 몇 개인가」는 **다른 문장**이다. +안에 접어 넣으면 그 함수를 **이름까지 바꿔야** 하는데 이 회차의 경계가 그것을 금한다. + +## 픽스처 — ★**결함이 «하나»가 되도록 지었다** + +`test-data/ldc/LdcDynamicDuplicateBSM.class`(생성기 `make_ldc_fixtures.py` · 신규 래퍼 `duplicate_bootstrap_methods`). +★**같은 유효한 표를 «바이트 동일»하게 두 번** 쓴다 — 어느 한 표만 있어도 **정상 파일**이므로 +★**거부의 원인이 «둘이라는 사실» 하나로 고정**된다(둘째 표를 다르게 만들면 다른 규칙이 먼저 물어서 +테스트가 «이름과 다른 이유»로 통과한다). +구조 확인(측정): 클래스 속성 = `['BootstrapMethods', 'BootstrapMethods']` · **두 본문 바이트 동일 = True**. +재생성 **멱등**: 기존 11개 **전건 바이트 동일** · 신규 1개. + +## 전/후 — 실제로 무엇이 바뀌나 (end-to-end) + +| 파일 | 고치기 «전» | 고친 «후» | ★참조 JVM(OpenJDK 26.0.1) | +|---|---|---|---| +| `LdcDynamicDuplicateBSM`(표 2개) | `UnsupportedOperationException: ldc of a dynamically-computed constant` | ★**`ClassFormatError: Invalid class file`** | ★**`ClassFormatError: Multiple BootstrapMethods attributes in class file`** | +| `LdcDynamic`(표 1개 · 대조군) | `UnsupportedOperationException` | ★**불변** | 로드 성공(rc=0 · 무출력) | + +⇒ ★**이 리니지의 문장 그대로다**: 「이 런타임이 아직 못 한다」 → **「이 파일이 깨졌다」**, 그리고 그 판정이 +참조 JVM 과 **일치**한다. ★대조군이 불변이라는 것이 **변경의 좁음**을 보인다. +※`AGENTS.md` 의 허용 축(observable behavior)만 썼다 — OpenJDK **소스 미참조**. + +## 개악 대조 (양방향 · 제품 «호출부») + +| 개악 | 결과 | +|---|---| +| **M1** — 호출부에서 `|| !at_most_one_bootstrap_methods_attribute(class)` **제거**(= 제안 이전 상태) | ★**red** — `expected ClassFormatError, got: … UnsupportedOperationException` | +| **M2** — 술어 본문을 **`true`(상수 통과)** 로 | ★**red** — 같은 자리에서 | +| 정상 | **green**(`test_class_format` 12 passed) | + +★**M2 를 따로 돌린 이유**: M1 만으로는 「호출을 지웠다」만 잡고 ★**「검사가 상수로 뭉개졌다」는 안 잡힌다** +(이 저장소가 반복해 지적한 「상수 대 상수」 형태). 두 축이 다 red 여야 그 단언이 **실제로 무는 것**이다. + +## 잃는 것 (계약 2⒜) — 숨기지 않는다 + +- ★**지금까지 로드되던 파일 하나가 거부된다.** 다만 그 형상은 ★**어떤 컴파일러도 내지 않는다** — + 이 저장소가 바로 어제 잰 바로는 javac·kotlinc·scalac·Lombok 산출물 **5,479 클래스에 0**이고, + ASM 으로도 «일부러» 만들어야 나온다. ⇒ 실사용자를 놀라게 할 확률이 낮다. +- ★**파스 시점 비용**: 클래스 속성표 1회 선형 순회(속성 수는 보통 한 자릿수). 상수 풀은 건드리지 않는다. +- ★**오탐 여지**: 규칙이 «개수»뿐이라 해석 여지가 없다 — JVMS 4.7.23 문면이 그대로 술어다. + +## 안 하면 무엇이 나쁜가 (계약 2⒝) + +★**두 표 중 «어느 것이 진짜인가»를 파서가 임의로 정한다.** `find_map` 이 첫 번째를 쓰므로 +`bootstrap_method_attr_index` 는 **첫 표**에 대해서만 경계 검사되고 둘째 표는 **조용히 무시**된다. +⇒ 같은 파일이 「첫 표 기준으로는 유효」할 수 있고, 그 사실이 **아무 데도 드러나지 않는다**. +그리고 우리는 그 파일을 **「미지원」**이라 불렀다 — ★**참조 JVM 이 «깨졌다»고 답하는 파일에 대해서.** diff --git a/test-data/ldc/LdcDynamicDuplicateBSM.class b/test-data/ldc/LdcDynamicDuplicateBSM.class new file mode 100644 index 0000000000000000000000000000000000000000..2343b98a55ccdbfc047223ecc23a35ab3a5b6c3e GIT binary patch literal 388 zcmZvYu};G<5QhJQ&_D?^4Tv4V&`uc{vNatTQZZD50SU<@R!QsHQSDUlSgZ^@01t(F zRa=^Y#lP>q|IX+8`ThC^aED96RaWG~4=v_pKCCNM=EBLrc;tf!;Z!U|tc0G#53f_1 zJ0FJxZ!SvBg}vu&&&O_2>PganWS*Kr5^nae)JrpyacZ=6Lc4)6&bo!DtdCGwtc;kNtLiR={GW)A0z?VIBnwuL8pZ?0=*NW y|7;zEU0za`*YG#+W)6#M9Kd4;5O+F4i`C~SWF=NFik?2#n0*&~uK77@ Date: Thu, 17 Sep 2026 02:40:21 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[rustjava-adopt-bound-bootstrap-method-attr?= =?UTF-8?q?-index-p0]=20docs(state):=20=EA=B2=8C=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=E2=91=A2=20=EC=B0=A9=EC=A7=80=20=EA=B8=B0=EB=A1=9D=20=EB=8F=99?= =?UTF-8?q?=EB=B4=89=20(PR=20#53=20=C2=B7=20--merge)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- STATE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/STATE.md b/STATE.md index 81f3ada6..51af573f 100644 --- a/STATE.md +++ b/STATE.md @@ -27,6 +27,12 @@ 픽스처 재생성 **멱등**(기존 11 전건 바이트 동일 · 신규 1) · DoD **7줄 전건 rc=0**. ★**잃는 것**: 지금까지 «로드되던» 파일 하나가 거부된다 — 다만 그 형상은 어제 이 저장소가 잰 대로 **javac·kotlinc·scalac·Lombok 산출물 5,479 클래스에 0**이고 ASM 으로도 «일부러» 만들어야 나온다. + ★★**게이트③ 착지 — PR #53 · `--merge`**(등재 repo `contracts/upstream-sync-repos.conf:22` — 스쿼시는 부모 2개를 접어 계보를 지운다). + 게이트② **1회차 approve**(반려 0) · 핀 `f2c83174` **불이동**(착수 실측 17:39:54Z · ★워밍 후 재조회 `MERGEABLE/CLEAN`). + ★**충돌 0 · base 당김 0**(`merge-tree` rc=0) — 이 브랜치가 `origin/main` 위에서 갈렸고 그 뒤 착지한 형제가 없다. + ★**여파**: 이 착지가 형제 **#54**(BSM 정적 인자)·**#55**(변이 감사)의 base 를 낡게 만든다. + ★**#54 는 «코드 파일이 자동 병합»되도록 그 회차가 삽입 위치를 미리 갈라 뒀고**(그 done 회신의 `merge-tree` 실측), + **#55 는 원장 3파일만 만진다** ⇒ 두 형제 모두 충돌은 **원장 계열에 국한**된다(게이트③ 계약 2-c⒜ 범위). - [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줄로 만든 클래스에서