Skip to content

Commit 8d96f3f

Browse files
author
jun0
committed
[rustjava-adopt-link-stringconcatfactory-p2-fix] fix(jvm-bytecode): 포획 «순서»를 값으로 잠그고 적재 시점 호스트 abort 를 없앤다
게이트② 반려 2건(F1·F2)을 받는다. 둘 다 검수자 실측이 옳았다. F1 — 포획 순서가 전 스위트에 무관측이었다. 픽스처 전건이 포획 1개 이하라 `LambdaBody::call` 의 읽기 순서를 뒤집어도 576 green 이었다(검수자 RM4). 조용히 틀린 답이다. ⇒ LambdaKinds 에 포획 2개 람다 «둘»을 넣었다: (String,int) 는 a:7 로 순서가 «글자»에 보이고, (int,int) 는 120 으로 «값»에만 보인다 — 후자는 어떤 타입 검사로도 못 잡는 축이다. 개악 대조: RM4 → a:7→7:a · 120→2001 로 red. F2 — 적재 시점 «호스트 프로세스 abort»(jvm/src/type.rs:74 "Invalid type"). 게스트 예외가 아니다. ★고친 자리를 «골랐고 왜인지 적는다»: 검수자 제안(lambda.rs 2줄)만 하면 진단이 UnsupportedOperationException 이 되는데, ★OpenJDK 26 은 같은 파일을 ClassFormatError 로 거부한다 (Method "run" ... has illegal signature "I"). 즉 그 파일은 «미지원»이 아니라 «파손»이다. 근인은 JVMS 4.4.10 의 규칙이 «사용 지점»에 있다는 것이다 — NameAndType 자체는 필드/메서드 서술자 «둘 다» 적법해야 하고(Fieldref·Methodref 가 같은 항목을 쓴다), 어느 쪽인지는 그것을 «참조하는 태그»가 정한다. ⇒ validation.rs 의 일반 NameAndType 팔은 그대로 두고, InvokeDynamic=메서드 서술자 · Dynamic=필드 서술자를 «그 팔에서» 요구한다. 비용을 먼저 쟀다: 커밋된 클래스 175개 · indy/condy 참조 44건 중 새로 위법이 되는 것은 이 회차가 만든 픽스처 «1건»뿐이다. lambda.rs 의 try_parse 도 함께 넣었다 — 검증이 이미 막지만, 틀렸을 때의 대가가 게스트 예외가 아니라 «프로세스 죽음»이라 두 겹으로 둔다(verifier.rs 가 ldc 에 대해 쓴 그 문장). 덤으로 origin/main 의 선행 입구(string concat 의 extract_invoke_params 경로)도 같은 규칙에 막힌다 — 검수자가 「별 티켓 권장」이라 한 자리다. 넓힌 것이 아니라 규칙을 옳은 자리에 둔 결과다.
1 parent e53b214 commit 8d96f3f

13 files changed

Lines changed: 117 additions & 7 deletions

‎classfile/src/validation.rs‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,29 @@ fn validate_constant_pool(constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> bo
231231
// The bootstrap method index is bounded by `bootstrap_method_indices_resolve`, not here:
232232
// it needs the class attributes, and this function only gets the pool. What is left for
233233
// this arm is the half that the pool alone can answer.
234-
ConstantPoolItem::Dynamic { name_and_type_index, .. } | ConstantPoolItem::InvokeDynamic { name_and_type_index, .. } => {
235-
constant_pool.get(name_and_type_index).and_then(ConstantPoolItem::name_and_type).is_some()
236-
}
234+
//
235+
// JVMS 4.4.10 makes the *kind* of descriptor part of that half, and it differs by tag: a
236+
// `CONSTANT_InvokeDynamic` names a method, a `CONSTANT_Dynamic` names a field type. The
237+
// `NameAndType` arm above cannot say this — one entry is shared by Fieldref and Methodref,
238+
// so "a field *or* method descriptor" is the strongest rule available *there*. The usage
239+
// site is where the choice is decided, which is why the check lives here and why the arm
240+
// above stays as it is.
241+
//
242+
// Measured before tightening: of 175 committed class files and 44 invokedynamic/dynamic
243+
// references, exactly one is rejected by this — `MetafactoryFieldDescriptorCallSite`, the
244+
// fixture written for it. OpenJDK 26 refuses that same file
245+
// (`ClassFormatError: Method "run" ... has illegal signature "I"`), so this moves us onto
246+
// the real JVM's answer rather than away from it.
247+
ConstantPoolItem::InvokeDynamic { name_and_type_index, .. } => constant_pool
248+
.get(name_and_type_index)
249+
.and_then(ConstantPoolItem::name_and_type)
250+
.and_then(|(_, descriptor_index)| constant_pool.get(&descriptor_index).and_then(ConstantPoolItem::utf8))
251+
.is_some_and(|descriptor| is_method_descriptor(&descriptor)),
252+
ConstantPoolItem::Dynamic { name_and_type_index, .. } => constant_pool
253+
.get(name_and_type_index)
254+
.and_then(ConstantPoolItem::name_and_type)
255+
.and_then(|(_, descriptor_index)| constant_pool.get(&descriptor_index).and_then(ConstantPoolItem::utf8))
256+
.is_some_and(|descriptor| is_field_descriptor(&descriptor)),
237257
_ => true,
238258
})
239259
}

‎jvm-bytecode/src/lambda.rs‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,22 @@ pub(crate) fn lower(class: &mut ClassInfo) {
103103
};
104104
// The call site's return type is the interface being implemented. Anything else
105105
// is not a `metafactory` call site whatever its bootstrap says.
106-
let call_site_type = JavaType::parse(descriptor);
107-
let (captures, JavaType::Class(interface)) = call_site_type.as_method() else {
106+
//
107+
// Parsed with `try_parse`, not `parse`, and that is not a style choice: a call
108+
// site's descriptor is a string out of the class file. `validation.rs` now requires
109+
// it to be a method descriptor at this usage site (JVMS 4.4.10), so a bad one is
110+
// refused before reaching here — but this stays `try_parse` anyway, because the
111+
// cost of being wrong is not a guest exception, it is a host abort:
112+
// `JavaType::parse` panics with "Invalid type". `verifier.rs` writes the same
113+
// sentence about `ldc` — "reaching it would abort the host, not the guest". Two
114+
// checks for one rule is cheap; one missing check is a crashed process.
115+
let Some(JavaType::Method(captures, returns)) = JavaType::try_parse(descriptor) else {
108116
continue;
109117
};
118+
let JavaType::Class(interface) = &*returns else {
119+
continue;
120+
};
121+
let captures = &captures[..];
110122
if !adapts(captures, &linked.sam_descriptor, &linked.implementation) {
111123
continue;
112124
}
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
220 Bytes
Binary file not shown.
212 Bytes
Binary file not shown.

‎test-data/indy/LambdaKinds.class‎

558 Bytes
Binary file not shown.
566 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)