Skip to content

Commit 8ffea4c

Browse files
jun0claude
andcommitted
Merge main (PR #3 classfile-parse-error-propagation) into unsupported-charset-exception
Resolve STATE.md/REPORT.md add/add conflicts by adopting the superset: all task entries kept, classfile task marked merged at main 549b9eb. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 parents dd9fcdf + 549b9eb commit 8ffea4c

13 files changed

Lines changed: 287 additions & 64 deletions

File tree

REPORT.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# REPORT
22

3-
## [2026-07-22] 미지원 charset 패닉 → java.io.UnsupportedEncodingException
3+
## [2026-07-22] 미지원 charset 패닉 → UnsupportedEncodingException (rustjava-unsupported-charset-exception)
44
- 무엇을: `String.getBytes(charset)`/`new String(byte[], charset)`/`InputStreamReader.read()`
55
`unimplemented!()` 패닉을 `java.io.UnsupportedEncodingException`(신설, IOException 하위) throw 로
66
전환하고, String↔Reader 의 지원 charset 목록을 공용 `charset::Charset` 으로 일원화했다.
@@ -13,3 +13,30 @@
1313
- 후속 추천: ① `Charset` 공용화를 계기로 UTF-16/Shift_JIS 등 실제 인코딩 추가는 별건 티켓으로.
1414
② InputStreamReader 가 read 마다 스트림 디코더를 새로 만들어 버퍼 경계의 multibyte 부분
1515
시퀀스가 유실될 수 있는 기존 문제(EUC-KR)가 남아 있다 — 별건 조사 권장.
16+
17+
## [2026-07-22] 클래스파일 파싱 실패 → ClassFormatError 전파 (rustjava-classfile-parse-error-propagation)
18+
- 무엇을: `ClassInfo::parse``Option``Result<_, ParseError>` 로 바꿔 실패 원인(절단/매직
19+
불일치/미지원 상수풀 태그 N/기타 손상)을 담고, `from_classfile``unwrap()`/`assert_eq!`
20+
제거해 `define_class` 에서 기존 예외 관례(`jvm.exception`)대로 `java.lang.ClassFormatError`
21+
올림. `java/lang/ClassFormatError` 런타임 클래스(부모 LinkageError) 신설.
22+
- 왜: 손상되거나 미지원 항목(javac 9+ 가 기본으로 심는 invokedynamic 계열 태그 15~18)을 가진
23+
class 파일을 여는 순간 Rust 패닉으로 프로세스(임베딩 호스트 포함)가 즉사했음. "클래스 못 찾음"
24+
은 예외인데 "못 읽음"만 패닉인 비대칭.
25+
- 사용자 영향: 잘못된 class 파일이 진단 가능한 자바 예외(원인 메시지 포함)로 보고되고 프로세스는
26+
살아남음. 미지원 태그는 명확히 거절(구현 아님). `tests/test_class_format.rs` 4케이스(절단/태그
27+
18/매직/못찾음 대조군)가 회귀 잠금.
28+
- 후속 추천: ① invokedynamic/MethodHandle 실제 지원(별건 대형), ② 상수풀 인덱스 참조
29+
(`.get().unwrap()` 계열) 손상 대응(별건), ③ UnsupportedClassVersionError 도입 검토(major
30+
version 기반).
31+
32+
## [2026-07-22] 시간 API 패닉 제거 + 회귀 잠금 (rustjava-runtime-time-todo-impl)
33+
- 무엇을: `src/runtime.rs``RuntimeImpl` 에서 `now()`/`sleep()`/`r#yield()``todo!()` 를 실제
34+
구현(UNIX epoch ms·`tokio::time::sleep`·`tokio::task::yield_now`)으로 교체하고, `test_utils`
35+
`TestRuntime::r#yield``todo!()` 도 동형으로 구현. 루트 `Cargo.toml` tokio 에 `time` 피처 추가.
36+
- 왜: 배포 바이너리가 `System.currentTimeMillis()`·`Thread.sleep()`·`new Date()` 등 시간 API 를
37+
부르는 순간 Rust 패닉으로 즉사했으나, 테스트 코퍼스가 해당 API 를 0건 사용해 CI 가 초록이었음.
38+
- 사용자 영향: 시간 API 를 쓰는 모든 자바 프로그램이 이제 정상 동작. `test_data/TimeApi`
39+
픽스처(currentTimeMillis/yield/sleep/Date, 결정론적 단언)가 `RuntimeImpl` 경로 통합 테스트로
40+
상시 회귀 감시.
41+
- 후속 추천: ① `jvm_rust/src/interpreter.rs:629` 의 잔여 `todo!()` 제거(별건), ② Timer/Object.wait
42+
경로도 픽스처 확장, ③ 픽스처 .java 소스 보관 체계(현재 .class+.txt 만 커밋하는 관례).

STATE.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
브랜치 `runtime-time-impl`, PR #2 게이트② 대기.
1010
- [rustjava-classfile-parse-error-propagation] 클래스파일 파싱 실패를 패닉 대신
1111
`java.lang.ClassFormatError` 로 전파(절단/매직 불일치/미지원 상수풀 태그 구분).
12-
브랜치 `classfile-parse-error-propagation`, PR #3 게이트② 대기.
12+
★게이트③ 완료: PR #3 스쿼시 머지 → main `549b9eb`(2026-07-23), 브랜치 정리 완료.
1313
- [rustjava-tracing-attributes-pin-removal] `#[tracing::instrument]` 1건을 수동 span 으로 대체,
1414
`tracing-attributes` 상한 핀 제거(tracing 0.1.41→0.1.44 언프리즈), wasm32 clippy CI 커버리지
1515
교정. 브랜치 `tracing-attributes-pin-removal`, PR 게이트② 대기.
@@ -18,12 +18,15 @@
1818
charset 을 공용 `charset::Charset` 으로 일치(ISO-8859-1/US-ASCII 가 Reader 에서도 동작).
1919
부수: `System.setProperty` 반환 시그니처 JDK 규격화(Object→String, jvm 부트스트랩 포함),
2020
`Throwable.getMessage()` 신설, 픽스처 `test_data/UnsupportedCharset`. 브랜치
21-
`unsupported-charset-exception`, PR 게이트② 대기.
21+
`unsupported-charset-exception`, PR #5 게이트② 대기(main 549b9eb 머지 반영, STATE/REPORT
22+
add/add 충돌 superset 해소 완료).
2223

2324
## 다음
24-
- PR approve 후 머지, 브랜치 정리(`gh pr merge --delete-branch``git branch -D``git fetch --prune`)
25-
- ★네 PR 모두 STATE.md/REPORT.md 를 추가하므로 나중에 머지되는 쪽마다 add/add 충돌 예상 —
26-
선행 PR 머지 후 후행 브랜치에 `git merge main` 하고 최신(superset) 내용 채택으로 해소.
25+
- 잔여 PR 게이트② approve 후 머지: #2(runtime-time-impl), tracing-attributes-pin-removal, #5.
26+
브랜치 정리(`gh pr merge --delete-branch``git branch -D``git fetch --prune`)
27+
- ★잔여 PR 도 STATE.md/REPORT.md add/add 충돌 예상 — 선행 머지 후 후행 브랜치에
28+
`git merge main` 하고 최신(superset) 내용 채택으로 해소(#5 는 해소 완료).
29+
- ★PR 발권 시 `--repo Jun025/RustJava` 명시(2026-07-22 upstream 오발행 사고 재발 방지).
2730
- (범위 밖 잔여) `jvm_rust/src/interpreter.rs:629` `todo!()` (invokedynamic) — 별건 티켓 필요
2831
- (신규 발견) javac 21 산출 익명 내부 클래스(.class)가 "Malformed class file" 로 파싱 실패 —
2932
원인 미조사(태그 15~18 아님). 별건 티켓 필요.

classfile/src/class.rs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use nom::{
88

99
use java_constants::ClassAccessFlags;
1010

11-
use crate::{attribute::AttributeInfo, constant_pool::ConstantPoolItem, field::FieldInfo, interface::parse_interface, method::MethodInfo};
11+
use crate::{
12+
attribute::AttributeInfo, constant_pool::ConstantPoolItem, error::ParseError, field::FieldInfo, interface::parse_interface, method::MethodInfo,
13+
};
1214

1315
fn parse_this_class<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], Arc<String>> {
1416
let (data, this_class) = be_u16(data)?;
@@ -44,48 +46,56 @@ pub struct ClassInfo {
4446
pub attributes: Vec<AttributeInfo>,
4547
}
4648

47-
impl ClassInfo {
48-
fn parse_info(data: &[u8]) -> IResult<&[u8], Self> {
49-
let (data, magic) = be_u32(data)?;
50-
if magic != 0xCAFEBABE {
51-
return Err(nom::Err::Error(nom::error::Error::new(data, nom::error::ErrorKind::Verify)));
52-
}
49+
type ClassBody = (
50+
u16,
51+
Arc<String>,
52+
Option<Arc<String>>,
53+
Vec<Arc<String>>,
54+
Vec<FieldInfo>,
55+
Vec<MethodInfo>,
56+
Vec<AttributeInfo>,
57+
);
5358

54-
let (data, minor_version) = be_u16(data)?;
55-
let (data, major_version) = be_u16(data)?;
56-
let (data, constant_pool) = ConstantPoolItem::parse_all(data)?;
59+
impl ClassInfo {
60+
fn parse_body<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], ClassBody> {
5761
let (data, access_flags) = be_u16(data)?;
58-
let (data, this_class) = parse_this_class(data, &constant_pool)?;
59-
let (data, super_class) = parse_super_class(data, &constant_pool)?;
60-
let (data, interfaces) = length_count(be_u16, |x| parse_interface(x, &constant_pool)).parse(data)?;
61-
let (data, fields) = length_count(be_u16, |x| FieldInfo::parse(x, &constant_pool)).parse(data)?;
62-
let (data, methods) = length_count(be_u16, |x| MethodInfo::parse(x, &constant_pool)).parse(data)?;
63-
let (data, attributes) = length_count(be_u16, |x| AttributeInfo::parse(x, &constant_pool)).parse(data)?;
62+
let (data, this_class) = parse_this_class(data, constant_pool)?;
63+
let (data, super_class) = parse_super_class(data, constant_pool)?;
64+
let (data, interfaces) = length_count(be_u16, |x| parse_interface(x, constant_pool)).parse(data)?;
65+
let (data, fields) = length_count(be_u16, |x| FieldInfo::parse(x, constant_pool)).parse(data)?;
66+
let (data, methods) = length_count(be_u16, |x| MethodInfo::parse(x, constant_pool)).parse(data)?;
67+
let (data, attributes) = length_count(be_u16, |x| AttributeInfo::parse(x, constant_pool)).parse(data)?;
6468

65-
Ok((
66-
data,
67-
Self {
68-
magic,
69-
minor_version,
70-
major_version,
71-
constant_pool,
72-
access_flags: ClassAccessFlags::from_bits_truncate(access_flags),
73-
this_class,
74-
super_class,
75-
interfaces,
76-
fields,
77-
methods,
78-
attributes,
79-
},
80-
))
69+
Ok((data, (access_flags, this_class, super_class, interfaces, fields, methods, attributes)))
8170
}
8271

83-
pub fn parse(file: &[u8]) -> Option<Self> {
84-
let (remaining, result) = Self::parse_info(file).ok()?;
72+
pub fn parse(file: &[u8]) -> Result<Self, ParseError> {
73+
let (data, magic) = be_u32::<_, nom::error::Error<&[u8]>>(file).map_err(ParseError::from_nom)?;
74+
if magic != 0xCAFEBABE {
75+
return Err(ParseError::BadMagic(magic));
76+
}
77+
78+
let (data, minor_version) = be_u16::<_, nom::error::Error<&[u8]>>(data).map_err(ParseError::from_nom)?;
79+
let (data, major_version) = be_u16::<_, nom::error::Error<&[u8]>>(data).map_err(ParseError::from_nom)?;
80+
let (data, constant_pool) = ConstantPoolItem::parse_all(data)?;
81+
let (remaining, (access_flags, this_class, super_class, interfaces, fields, methods, attributes)) =
82+
Self::parse_body(data, &constant_pool).map_err(ParseError::from_nom)?;
8583
if !remaining.is_empty() {
86-
return None;
84+
return Err(ParseError::TrailingData);
8785
}
8886

89-
Some(result)
87+
Ok(Self {
88+
magic,
89+
minor_version,
90+
major_version,
91+
constant_pool,
92+
access_flags: ClassAccessFlags::from_bits_truncate(access_flags),
93+
this_class,
94+
super_class,
95+
interfaces,
96+
fields,
97+
methods,
98+
attributes,
99+
})
90100
}
91101
}

classfile/src/constant_pool.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ use nom::{
77
number::complete::{be_f32, be_f64, be_i32, be_i64, be_u16, u8},
88
};
99

10+
use crate::error::ParseError;
11+
1012
fn parse_utf8(data: &[u8]) -> IResult<&[u8], Arc<String>> {
1113
let (data, length) = be_u16(data)?;
1214
let (data, utf8) = take(length as usize).parse(data)?;
1315

14-
Ok((data, Arc::new(String::from_utf8(utf8.to_vec()).unwrap())))
16+
let utf8 = String::from_utf8(utf8.to_vec()).map_err(|_| nom::Err::Error(Error::new(data, ErrorKind::Verify)))?;
17+
18+
Ok((data, Arc::new(utf8)))
1519
}
1620

1721
#[derive(Debug)]
@@ -108,14 +112,19 @@ impl ConstantPoolItem {
108112
}
109113
}
110114

111-
pub fn parse_all(data: &[u8]) -> IResult<&[u8], BTreeMap<u16, Self>> {
112-
let (remaining, count) = be_u16(data)?;
115+
pub fn parse_all(data: &[u8]) -> Result<(&[u8], BTreeMap<u16, Self>), ParseError> {
116+
let (remaining, count) = be_u16::<_, Error<&[u8]>>(data).map_err(ParseError::from_nom)?;
113117

114118
let mut data = remaining;
115119
let mut result = BTreeMap::new();
116120
let mut i = 1;
117121
loop {
118-
let (remaining, item) = Self::parse_with_tag(data)?;
122+
let (remaining, tag) = u8::<_, Error<&[u8]>>(data).map_err(ParseError::from_nom)?;
123+
let (remaining, item) = Self::parse_tagged(remaining, tag).map_err(|err| match err {
124+
// parse_tagged signals an unrecognized tag with ErrorKind::Switch
125+
nom::Err::Error(e) if e.code == ErrorKind::Switch => ParseError::UnsupportedConstantPoolTag { index: i, tag },
126+
other => ParseError::from_nom(other),
127+
})?;
119128
let is_double_entry = match &item {
120129
Self::Long(_) | Self::Double(_) => {
121130
// long or double constant takes two constant pool entries....

classfile/src/error.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use core::fmt::{self, Display, Formatter};
2+
3+
/// Reason a class file could not be parsed. Rendered into the
4+
/// `java.lang.ClassFormatError` message, so each variant carries enough
5+
/// context to diagnose the offending file.
6+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7+
pub enum ParseError {
8+
Truncated,
9+
BadMagic(u32),
10+
UnsupportedConstantPoolTag { index: u16, tag: u8 },
11+
Malformed,
12+
TrailingData,
13+
}
14+
15+
impl ParseError {
16+
pub(crate) fn from_nom(err: nom::Err<nom::error::Error<&[u8]>>) -> Self {
17+
match err {
18+
nom::Err::Incomplete(_) => Self::Truncated,
19+
nom::Err::Error(e) | nom::Err::Failure(e) => {
20+
if e.code == nom::error::ErrorKind::Eof {
21+
Self::Truncated
22+
} else {
23+
Self::Malformed
24+
}
25+
}
26+
}
27+
}
28+
}
29+
30+
impl Display for ParseError {
31+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
32+
match self {
33+
Self::Truncated => write!(f, "Truncated class file"),
34+
Self::BadMagic(magic) => write!(f, "Incompatible magic value 0x{magic:08X} in class file"),
35+
Self::UnsupportedConstantPoolTag { index, tag } => {
36+
write!(f, "Unknown or unsupported constant pool tag {tag} at index {index} in class file")
37+
}
38+
Self::Malformed => write!(f, "Malformed class file"),
39+
Self::TrailingData => write!(f, "Extra bytes at end of class file"),
40+
}
41+
}
42+
}

classfile/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extern crate alloc;
44
mod attribute;
55
mod class;
66
mod constant_pool;
7+
mod error;
78
mod field;
89
mod interface;
910
mod method;
@@ -13,6 +14,7 @@ pub use {
1314
attribute::{AttributeInfo, AttributeInfoCode},
1415
class::ClassInfo,
1516
constant_pool::{ConstantPoolReference, FieldMethodref},
17+
error::ParseError,
1618
field::FieldInfo,
1719
method::MethodInfo,
1820
opcode::Opcode,

java_runtime/src/classes/java/lang.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod array_index_out_of_bounds_exception;
44
mod array_store_exception;
55
mod class;
66
mod class_cast_exception;
7+
mod class_format_error;
78
mod class_loader;
89
mod clone_not_supported_exception;
910
mod cloneable;
@@ -41,14 +42,14 @@ mod unsupported_operation_exception;
4142
pub use self::{
4243
abstract_method_error::AbstractMethodError, arithmetic_exception::ArithmeticException,
4344
array_index_out_of_bounds_exception::ArrayIndexOutOfBoundsException, array_store_exception::ArrayStoreException, class::Class,
44-
class_cast_exception::ClassCastException, class_loader::ClassLoader, clone_not_supported_exception::CloneNotSupportedException,
45-
cloneable::Cloneable, comparable::Comparable, error::Error, exception::Exception, exception_in_initializer_error::ExceptionInInitializerError,
46-
illegal_argument_exception::IllegalArgumentException, incompatible_class_change_error::IncompatibleClassChangeError,
47-
index_out_of_bounds_exception::IndexOutOfBoundsException, instantiation_error::InstantiationError, integer::Integer,
48-
interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math, negative_array_size_exception::NegativeArraySizeException,
49-
no_class_def_found_error::NoClassDefFoundError, no_such_field_error::NoSuchFieldError, no_such_method_error::NoSuchMethodError,
50-
null_pointer_exception::NullPointerException, number_format_exception::NumberFormatException, object::Object, runnable::Runnable,
51-
runtime::Runtime, runtime_exception::RuntimeException, security_exception::SecurityException, string::String, string_buffer::StringBuffer,
52-
string_index_out_of_bounds_exception::StringIndexOutOfBoundsException, system::System, thread::Thread, throwable::Throwable,
53-
unsupported_operation_exception::UnsupportedOperationException,
45+
class_cast_exception::ClassCastException, class_format_error::ClassFormatError, class_loader::ClassLoader,
46+
clone_not_supported_exception::CloneNotSupportedException, cloneable::Cloneable, comparable::Comparable, error::Error, exception::Exception,
47+
exception_in_initializer_error::ExceptionInInitializerError, illegal_argument_exception::IllegalArgumentException,
48+
incompatible_class_change_error::IncompatibleClassChangeError, index_out_of_bounds_exception::IndexOutOfBoundsException,
49+
instantiation_error::InstantiationError, integer::Integer, interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math,
50+
negative_array_size_exception::NegativeArraySizeException, no_class_def_found_error::NoClassDefFoundError, no_such_field_error::NoSuchFieldError,
51+
no_such_method_error::NoSuchMethodError, null_pointer_exception::NullPointerException, number_format_exception::NumberFormatException,
52+
object::Object, runnable::Runnable, runtime::Runtime, runtime_exception::RuntimeException, security_exception::SecurityException, string::String,
53+
string_buffer::StringBuffer, string_index_out_of_bounds_exception::StringIndexOutOfBoundsException, system::System, thread::Thread,
54+
throwable::Throwable, unsupported_operation_exception::UnsupportedOperationException,
5455
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use alloc::vec;
2+
3+
use java_class_proto::JavaMethodProto;
4+
use jvm::{ClassInstanceRef, Jvm, Result};
5+
6+
use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String};
7+
8+
// class java.lang.ClassFormatError
9+
pub struct ClassFormatError;
10+
11+
impl ClassFormatError {
12+
pub fn as_proto() -> RuntimeClassProto {
13+
RuntimeClassProto {
14+
name: "java/lang/ClassFormatError",
15+
parent_class: Some("java/lang/LinkageError"),
16+
interfaces: vec![],
17+
methods: vec![
18+
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
19+
JavaMethodProto::new("<init>", "(Ljava/lang/String;)V", Self::init_with_message, Default::default()),
20+
],
21+
fields: vec![],
22+
access_flags: Default::default(),
23+
}
24+
}
25+
26+
async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
27+
tracing::debug!("java.lang.ClassFormatError::<init>({this:?})");
28+
29+
let _: () = jvm.invoke_special(&this, "java/lang/LinkageError", "<init>", "()V", ()).await?;
30+
31+
Ok(())
32+
}
33+
34+
async fn init_with_message(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, message: ClassInstanceRef<String>) -> Result<()> {
35+
tracing::debug!("java.lang.ClassFormatError::<init>({this:?}, {message:?})");
36+
37+
let _: () = jvm
38+
.invoke_special(&this, "java/lang/LinkageError", "<init>", "(Ljava/lang/String;)V", (message,))
39+
.await?;
40+
41+
Ok(())
42+
}
43+
}

java_runtime/src/loader.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub fn get_runtime_class_proto(name: &str) -> Option<RuntimeClassProto> {
3939
crate::classes::java::lang::ArrayStoreException::as_proto(),
4040
crate::classes::java::lang::Class::as_proto(),
4141
crate::classes::java::lang::ClassCastException::as_proto(),
42+
crate::classes::java::lang::ClassFormatError::as_proto(),
4243
crate::classes::java::lang::ClassLoader::as_proto(),
4344
crate::classes::java::lang::Cloneable::as_proto(),
4445
crate::classes::java::lang::CloneNotSupportedException::as_proto(),

jvm_rust/src/class_definition.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use core::{
1212

1313
use parking_lot::RwLock;
1414

15-
use classfile::{AttributeInfo, ClassInfo, ConstantPoolReference};
15+
use classfile::{AttributeInfo, ClassInfo, ConstantPoolReference, ParseError};
1616
use java_class_proto::JavaClassProto;
1717
use java_constants::{ClassAccessFlags, FieldAccessFlags, MethodAccessFlags};
1818
use jvm::{ClassDefinition, ClassInstance, Field, JavaType, JavaValue, Jvm, Method, Result};
@@ -96,9 +96,8 @@ impl ClassDefinitionImpl {
9696
)
9797
}
9898

99-
pub fn from_classfile(data: &[u8]) -> Result<Self> {
100-
let class = ClassInfo::parse(data).unwrap(); // TODO ClassFormatError
101-
assert_eq!(class.magic, 0xCAFEBABE);
99+
pub fn from_classfile(data: &[u8]) -> core::result::Result<Self, ParseError> {
100+
let class = ClassInfo::parse(data)?;
102101

103102
let mut constant_values = Vec::new();
104103
let fields = class

0 commit comments

Comments
 (0)