Skip to content

Commit c759bfd

Browse files
author
jun0
committed
Merge remote-tracking branch 'origin/main' into rustjava-2026-09-20-name-the-missing-bootstrap-class-adopt-p0
# Conflicts: # REPORT.md # STATE.md
2 parents 647e928 + f43f7b1 commit c759bfd

16 files changed

Lines changed: 371 additions & 74 deletions

‎REPORT.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
- 무엇을: `JavaError::Unraisable(String)` 변종 추가. `Jvm::new` 의 패닉 2곳(부트스트랩 클래스·오류 경로 closure)이 빠진 클래스 이름을 담은 `Err` 를 돌려준다. `Jvm::exception` 은 같은 스레드에서 이미 만들고 있는 예외를 다시 만들려 하면(또는 깊이 8) `Unraisable` 로 첫 실패를 명명한다.
44
- 왜: 채택 제안 2건(`…name-the-missing-bootstrap-class#p0` · `…string-array-hiding-overflows-stack#p0`)이 같은 장애물 — 단일 변종 `JavaError` — 에 닿았고, 하류 임베더 wie 가 같은 변종을 요청했다(타이틀 2건이 호스트를 죽였다). AGENTS.md 「라이브러리 코드는 패닉하지 않는다」.
55
- 사용자 영향: 불완전한 클래스 집합을 받은 호스트가 프로세스 abort 대신 처리 가능한 오류를 받는다. ★공개 enum 확장이라 외부 소비자의 irrefutable 구조분해는 깨진다. 후속 추천 1건(GC 순회 `Result` 전파) — `docs/worklog/2026-09-24-unraisable-error-variant.{md,json}`.
6+
- 보정(-fix · 검수 반려): `StreamHandler::publish` 의 `if let Err(JavaException)` 2곳(헤드·본문 쓰기)이 `Unraisable` 을 `Ok(())` 로 삼키던 것을 `match` 로 전파 · 회귀 시험 1건.
7+
## [2026-09-23] 클래스 파일 거부가 «어디서» 걸렸는지 말한다 — 검증 규칙 11개, 오류 변종은 1개 (rustjava-2026-09-18-bootstrap-argument-index-and-tag-adopt-p0)
8+
- 무엇을: `validate_class` 규칙을 전수 세어(14개 · 고정문장 13개) 표를 걸으며 멈춘 위치를 이미 쥔 **11개**가 그 위치를 싣게 했다 — `ClassFileError::InvalidFormatAt { cause, location }` 하나와 표 5종(`Location`)으로.
9+
- 왜: #73 이 부트스트랩 인자 규칙 하나를 구조화한 뒤 나머지가 몇이나 되는지 아무도 세지 않았다. 규칙마다 변종을 늘리면 `&'static str` 설계가 피하던 enum 비대가 오므로, 변종은 «규칙 수»가 아니라 «표 종류 수»로만 늘게 멈춤 기준을 먼저 세웠다.
10+
- 사용자 영향: `ClassFormatError` 문면 끝에 `(constant pool entry #18)`·`(method #2)` 처럼 위치가 붙는다. 종전 문장은 그대로 앞에 남는다. 받아들이는/거부하는 파일은 하나도 바뀌지 않는다.
11+
- 후속 추천: `class.rs` 의 파싱 단계 거부 3종(잘림·꼬리 바이트·45.0 미만)은 이번 범위 밖 — 그중 위치를 쥔 것이 있는지만 세어 볼 것(S). 상세 = `docs/worklog/2026-09-23-validation-rules-name-their-position.{md,json}`.
12+
## [2026-09-23] charset 보류 판단을 `Charset` 의 exhaustive match 로 옮겼다 (rustjava-2026-09-23-stale-next-pointer-and-euc-kr-boundary-adopt-p0)
13+
- 무엇을: `InputStreamReader::read()` 의 charset 이름 문자열 비교 2곳을 `Charset::bytes_to_hold_back` 로 옮겼다(wildcard 없는 match · 동작 불변).
14+
- 왜: 채택 제안 `2026-09-23-stale-next-pointer-and-euc-kr-boundary#p0` — 새 charset 을 더하면 그 if 사슬은 조용히 빠졌다. 이제 컴파일이 막는다.
15+
- 사용자 영향: 없음(동작 불변). 다음에 멀티바이트 charset 을 더할 때 읽기 경계에서 글자가 사라지는 결함을 «잊어서» 만들 수 없다. 후속 추천 0건. 상세 = `docs/worklog/2026-09-23-charset-hold-back-exhaustive-match.{md,json}`.
616
## [2026-09-23] `## 다음` 은 «ref 목록»으로 남긴다 — 카드로 옮기면 할 일이 0으로 보인다 (rustjava-2026-09-23-stale-next-pointer-and-euc-kr-boundary-adopt-p1)
717
- 무엇을: `STATE.md` `## 다음` 머리에 규칙 3줄을 박고 절을 한 번 재작성했다 — 항목은 카드 ref·PR 번호만, 선행 사슬 1개, 카드 밖 항목(PR #81) 1개. 종전 ⓪ 블록은 사료로 내렸다.
818
- 왜: 세 번 낡았고 세 번 다 «지목을 쓴 회차 ≠ 닫은 회차»였다(규칙 「닫히는 즉시」는 08-27 부터 있었다). 카드 전면 이전(⒞)은 tower 술어로 이 레인 열린 카드가 **0**(32건 전건 injected)이라 정본이 「할 일 없음」을 말하게 돼 기각.

‎STATE.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88

99
## 완료
1010
- [rustjava-2026-09-20-name-the-missing-bootstrap-class-adopt-p0] ★**공개 API 변경(breaking)**: `JavaError::Unraisable(String)` 신설 — Java 예외로 만들 수 없는 실패. `Jvm::new` 패닉 2곳 → `Err(Unraisable)`(빠진 클래스 이름 포함) · `Jvm::exception` 재귀 바닥(같은 스레드에서 «같은 예외»를 다시 만들거나 깊이 8 → `Unraisable`, 첫 실패 명명) · `[Ljava/lang/String;` 숨김 재현이 stack overflow → loader 질문 2회. ★외부 소비자: `let JavaError::JavaException(..) = ..` irrefutable 구조분해가 컴파일 에러가 된다(이 repo 3곳 수정 · wie 는 crates.io 0.1.1 소비라 판올림 때 발생). 채택 `2026-09-20-name-the-missing-bootstrap-class#p0` · `2026-09-20-string-array-hiding-overflows-stack#p0`. 상세 `docs/worklog/2026-09-24-unraisable-error-variant.md`.
11+
보정(-fix): `stream_handler.rs` `publish` 의 `if let Err(JavaException)` 2곳 → `match` + `Unraisable` 전파(삼킴 제거) · 회귀 `stream_handler_propagates_unraisable_output_failures`.
12+
- [rustjava-2026-09-18-bootstrap-argument-index-and-tag-adopt-p0] ★**검증 규칙이 멈춘 위치를 말한다 — 11규칙, 변종 1개.** 채택 제안 `2026-09-18-bootstrap-argument-index-and-tag#p0`.
13+
★**전제 반증(작게)**: `validate_class` 규칙은 15/14 가 아니라 **14 · 고정문장 13**(@origin/main `c654ae2e`).
14+
★**전수**: 13 중 **11**이 표를 걸으며 멈춘 위치를 쥐고 있었다(pool 3 · field 3 · method 3 · interface 1 · class attribute 1) · `this_class`/`super_class` 2개는 가리킬 곳 없음 → `InvalidFormat` 유지.
15+
★**멈춤 기준**: enum 은 «규칙»이 아니라 «표 종류»로만 자란다 ⇒ `InvalidFormatAt { cause, location: Location }` **1변종** + `Location` 5종. `ClassFileError` 크기 불변(기존 크기 테스트 무수정 통과).
16+
★**문면** = `<종전 문장> (<표> #<n>)` · pool 은 `javap` 의 1-기반 `#N` · 파일 바이트 0. 경계 무이동(술어 본문 불변 · `all/any` → 첫 위반 위치).
17+
★**양방향**: 인덱스 3종 개악(M1 문면에서 위치 삭제 · M2 field/method 0 고정 · M3 pool 첫 키 보고) **전건 red** · pool 인덱스(#11·#18·#34)는 **독립 바이트 워커로 교차 확인**.
18+
- [rustjava-2026-09-23-stale-next-pointer-and-euc-kr-boundary-adopt-p0] charset 보류 판단을 `Charset::bytes_to_hold_back`(wildcard 없는 match)로 옮김 · `read()` 이름 비교 0 · 동작 불변 · 변이 양방향 확인. 채택 `2026-09-23-stale-next-pointer-and-euc-kr-boundary#p0`.
1119
- [rustjava-2026-09-23-stale-next-pointer-and-euc-kr-boundary-adopt-p1] `## 다음` 정본 결정 = ⒝ 얇은 층(ref + 선행 사슬 + 카드 밖 항목만 · 산문 지목 금지). ⒞ 기각 근거 = tower 술어로 열린 카드 0(32건 전건 injected). 채택 `2026-09-23-stale-next-pointer-and-euc-kr-boundary#p1`. 상세 `docs/worklog/2026-09-23-next-section-canon-decision.md`.
1220
- [rustjava-prune-declined-followup-proposals-2026-09-21] ★**추천 후속작업 2건 기각** — 운영자 지시(2026-09-21 우선순위 정리). ★제품 코드 **0줄** · 새 제안 **0** · 검사기/CI 신설 **0**.
1321
★**닫은 둘**(검사기 다듬기 축): `2026-09-19-nonliteral-blind-spot-is-reported-not-gated#p0` · `2026-09-20-lock-script-output-order#p0`.

‎classfile/src/error.rs‎

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ pub enum ClassFileError {
1313
/// A bootstrap method argument that is not a loadable constant, carrying the two things the
1414
/// predicate already knows at the moment it refuses: *which* argument, and what it found there.
1515
///
16-
/// ★ Which variant to use: `InvalidFormat` is for a rule that has nothing to point at, and that
17-
/// is still most of them. Use this one only when a number is already in hand — folding it into
18-
/// prose is the thing this variant exists to stop. `UnsupportedVersion` is the same shape and
16+
/// ★ Which variant to use: `InvalidFormat` is for a rule that has nothing to point at;
17+
/// `InvalidFormatAt` for a rule that stopped at one position in one table; this one for the
18+
/// bootstrap-argument rule, which holds two indices and what it found. Folding a number already
19+
/// in hand into prose is the thing these variants exist to stop. `UnsupportedVersion` is the same shape and
1920
/// predates it, so this is the established way here rather than a second scheme.
2021
///
2122
/// It stays `Copy`: two `u16`s and a `&'static str`, no owned data.
@@ -31,5 +32,43 @@ pub enum ClassFileError {
3132
/// and the message says which.
3233
actual: Option<&'static str>,
3334
},
35+
/// A rule that walks one of the class file's tables and stopped at a known position.
36+
///
37+
/// ★ One variant for all of them, not one per rule. Eleven of `validate_class`'s rules hold a
38+
/// position when they refuse, and a variant each is the enum growth the `&'static str` design
39+
/// was avoiding. What differs between them is only *which table* the number indexes, and there
40+
/// are five tables — so the table is the enum, and the rule stays the sentence it already was.
41+
/// `InvalidBootstrapArgument` stays separate because it carries a second index and what it found.
42+
InvalidFormatAt {
43+
cause: &'static str,
44+
location: Location,
45+
},
3446
UnsupportedVersion(u16),
3547
}
48+
49+
/// Where in the class file an `InvalidFormatAt` rule stopped.
50+
///
51+
/// The constant pool uses its own 1-based index — the `#N` that `javap -v` prints — because that
52+
/// is how every tool names a pool entry. The others are zero-based positions in their table, the
53+
/// same convention `InvalidBootstrapArgument` uses. Only an index: nothing from the file's bytes
54+
/// (a name, a descriptor) is carried, so a hostile file cannot put text into the message.
55+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
56+
pub enum Location {
57+
ConstantPoolEntry(u16),
58+
Interface(u16),
59+
Field(u16),
60+
Method(u16),
61+
ClassAttribute(u16),
62+
}
63+
64+
impl core::fmt::Display for Location {
65+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66+
match self {
67+
Self::ConstantPoolEntry(index) => write!(f, "constant pool entry #{index}"),
68+
Self::Interface(index) => write!(f, "interface #{index}"),
69+
Self::Field(index) => write!(f, "field #{index}"),
70+
Self::Method(index) => write!(f, "method #{index}"),
71+
Self::ClassAttribute(index) => write!(f, "class attribute #{index}"),
72+
}
73+
}
74+
}

‎classfile/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub use {
1515
attribute::{AttributeInfo, AttributeInfoCode, BootstrapMethod, MethodHandleKind, MethodHandleRef, method_type_descriptor},
1616
class::ClassInfo,
1717
constant_pool::{ConstantPoolReference, FieldMethodref},
18-
error::ClassFileError,
18+
error::{ClassFileError, Location},
1919
field::FieldInfo,
2020
method::MethodInfo,
2121
opcode::{LambdaCallSite, Opcode, StringConcatCallSite},

0 commit comments

Comments
 (0)