Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions REPORT.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# REPORT

## [2026-08-17] upstream 동기 S1 — 컷 `1f356ae` 머지 (rustjava-upstream-sync-s1-tracing-cut-1f356ae)
- 무엇을: upstream `1f356ae`(#173~#179 · 5커밋)를 머지했다. 충돌 **2** 해소 —
`lang.rs` 는 **양쪽 병합**(우리 `class_format_error` + upstream 의 Java 1.2 wrapper 9종),
`thread.rs` 는 **upstream 뼈대 + PR #4 수동 span 재적용**(`#[tracing::instrument]` 한 줄만 치환,
`Cargo.toml` 2개 무접촉). 66파일 `+5,235 / −151`.
- 왜: 접근안 §6 이 정한 7회차 중 첫 회차이고 축은 tracing 이다. upstream `thread.rs` 를 그대로 취하면
`attributes` 피처가 꺼진 tracing 에 속성 매크로가 걸려 **컴파일이 깨지고**, 피처를 되살리면 PR #4 가
통째로 되돌아간다. 뼈대만 취하고 span 만 수동으로 되돌려 둘 다 피했다.
★**충돌 목록 밖에서 하나가 더 깨졌다**: 우리 PR #5 가 JDK 규격에 맞게 고친
`System.setProperty` 서술자(`…)Ljava/lang/String;` — 실제 javac 바이트코드가 그렇다)와
upstream 의 구판(`…)Ljava/lang/Object;`)이 어긋나, upstream 이 새로 들여온 wrapper 테스트 3건이
`NoSuchMethodError` 로 죽었다. 우리 서술자를 유지하고 upstream 테스트 호출부 6곳을 고쳤다.
- 사용자 영향: 없음(동작 변경 0). Java 1.2 wrapper 클래스 9종
(`Boolean`/`Byte`/`Character`/`Double`/`Float`/`Long`/`Number`/`Short` · `ClassNotFoundException`)과
`Thread.currentThread()` 동일객체 반환이 들어왔다. `cargo test --all` **169 passed / 0 failed / 1 ignored**
(기준선 149 → +20, 전부 upstream 신규 + 우리 기존분).
- 후속 추천: S2(컷 `af4f6f8` · charset 축 · 새 충돌 +5). ★착수 시 충돌 재측정 필수 ·
★**우리 프로덕션 서술자/시그니처 변경이 upstream 신규 테스트와 어긋나는지**를 S1 과 같은 방식으로 훑어라.

## [2026-08-16] upstream 동기화 접근안 확정 (rustjava-upstream-sync-approach-plan)
- 무엇을: 격차를 오늘 값으로 다시 재고(**10 앞섬 / 33 뒤처짐** · 충돌 **17 → 19파일**), 충돌 19파일을
처분 어휘 4종으로 분류한 표와 단계 분할안을 `docs/upstream-sync-approach.md` 로 확정했다.
Expand Down
25 changes: 21 additions & 4 deletions STATE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# STATE

## 진행중
- (없음)
- [rustjava-upstream-sync-s1-tracing-cut-1f356ae] upstream 컷 `1f356ae` 머지 완료(충돌 2 해소 ·
tracing 축). green 전건 rc=0 · 169 passed. **PR 대기 — 게이트③ 미착지.**

## 완료
- [rustjava-runtime-time-todo-impl] RuntimeImpl 시간 API `todo!()` 3건 제거(now/sleep/yield) +
Expand Down Expand Up @@ -29,9 +30,25 @@

## 다음

### ①(최우선) upstream 동기화 — ★**접근안 확정(2026-08-16)**. 정본 = `docs/upstream-sync-approach.md`

★**설계는 끝났다. 다음은 실행이다** — 아래는 그 문서의 요약이고, 착수 전 **문서를 읽어라**.
### ①(최우선) upstream 동기화 — ★**S1 착지 대기(2026-08-17)**. 정본 = `docs/upstream-sync-approach.md`

★**S1(`1f356ae` · tracing 축)은 머지 완료 · PR 대기 중이다. 다음은 S2(`af4f6f8` · charset 축).**

**S1 실측(2026-08-17 13:1x)**: `merge-tree` 충돌 **2 그대로**(`lang.rs`·`thread.rs`) — 계획서 예측과 일치.
green 전건 rc=0 · `cargo test --all` **169 passed / 0 failed / 1 ignored**.
★**계획서가 이름 붙인 3위험 중 S1 에서 실제로 터진 것은 tracing 하나뿐**이다 —
`tests/test_class_format.rs` 4/4 통과(upstream `classfile/src/error.rs` 재작성은 S3 컷 `822504b` 에 온다) ·
`charset.rs` 호출자 2건 생존(clippy green).
★★**대신 계획서가 «몰랐던» 파손이 하나 나왔다 — `java/lang/System.setProperty` 서술자**:
우리 PR #5 가 `…)Ljava/lang/String;` 로 고쳤고(**실제 javac 바이트코드**
`test_data/UnsupportedCharset.class` 상수풀이 그 서술자다 — JDK 규격상 우리가 옳다),
upstream 은 여전히 `…)Ljava/lang/Object;` 다. 충돌 0으로 우리 쪽이 머지되는데 upstream PR #176 이
새로 들여온 wrapper 테스트 6개 호출부가 `Object` 서술자를 박아 두어 **`NoSuchMethodError` 3건**이 났다.
⇒ 우리 서술자를 유지하고 **upstream 테스트 호출부 6곳을 고쳤다.**
★**교훈: 「충돌 목록 밖 파손」은 우리 «테스트»만이 아니라 우리 «프로덕션 서술자 변경»에서도 나온다.
그리고 그것은 upstream 이 «앞으로» 들여올 테스트에 의해 뒤늦게 터진다 — S2~S7 에서도 같은 형태를 예상하라.**

아래는 접근안 문서의 요약이고, 착수 전 **문서를 읽어라**.

**재실측(2026-08-16)**: `rev-list --left-right --count origin/main...upstream/main` → **`10 33`**
(선행 08-15 의 `9 32` 는 낡았다) · 공통조상 `62cf0c6` · origin tip `85f294a` · upstream tip `ba5797b`.
Expand Down
4 changes: 3 additions & 1 deletion classfile/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub enum AttributeInfo {
MethodParameters(Vec<u8>), // TODO
NestMembers(Vec<u8>), // TODO
NestHost(Vec<u8>), // TODO
Unknown(Arc<String>, Vec<u8>),
}

impl AttributeInfo {
Expand All @@ -170,7 +171,8 @@ impl AttributeInfo {
"MethodParameters" => AttributeInfo::MethodParameters(info.to_vec()),
"NestMembers" => AttributeInfo::NestMembers(info.to_vec()),
"NestHost" => AttributeInfo::NestHost(info.to_vec()),
_ => return Err(nom::Err::Error(nom::error_position!(info, nom::error::ErrorKind::Switch))),
// unrecognized attributes must be silently ignored (JVMS 4.7.1)
_ => AttributeInfo::Unknown(name.clone(), info.to_vec()),
})
},
)
Expand Down
19 changes: 4 additions & 15 deletions java_runtime/src/classes/java/io/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,7 @@ impl File {
let path = jvm.invoke_virtual(&this, "getPath", "()Ljava/lang/String;", ()).await?;
let path = JavaLangString::to_rust_string(jvm, &path).await?;

let stat = context.metadata(&path).await;
if stat.is_err() {
return Ok(false);
}

Ok(stat.unwrap().r#type == FileType::Directory)
Ok(context.metadata(&path).await.is_ok_and(|x| x.r#type == FileType::Directory))
}

async fn is_file(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<bool> {
Expand All @@ -73,12 +68,7 @@ impl File {
let path = jvm.invoke_virtual(&this, "getPath", "()Ljava/lang/String;", ()).await?;
let path = JavaLangString::to_rust_string(jvm, &path).await?;

let stat = context.metadata(&path).await;
if stat.is_err() {
return Ok(false);
}

Ok(stat.unwrap().r#type == FileType::File)
Ok(context.metadata(&path).await.is_ok_and(|x| x.r#type == FileType::File))
}

async fn delete(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<bool> {
Expand All @@ -96,8 +86,7 @@ impl File {
let path = jvm.invoke_virtual(&this, "getPath", "()Ljava/lang/String;", ()).await?;
let path = JavaLangString::to_rust_string(jvm, &path).await?;

let stat = context.metadata(&path).await.unwrap();

Ok(stat.size as _)
// File.length() is 0 when the file does not exist
Ok(context.metadata(&path).await.map_or(0, |x| x.size as _))
}
}
16 changes: 12 additions & 4 deletions java_runtime/src/classes/java/io/file_input_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ impl FileInputStream {
let rust_file = FileDescriptor::file(jvm, context, fd).await?;

// TODO get os buffer size
let stat = rust_file.metadata().await.unwrap();
let tell = rust_file.tell().await.unwrap();
let Ok(stat) = rust_file.metadata().await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};
let Ok(tell) = rust_file.tell().await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};

let available = stat.size - tell;

Expand All @@ -101,7 +105,9 @@ impl FileInputStream {
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

let mut rust_buf = vec![0; length as _];
let read = rust_file.read(&mut rust_buf).await.unwrap();
let Ok(read) = rust_file.read(&mut rust_buf).await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};
if read == 0 {
return Ok(-1);
}
Expand All @@ -118,7 +124,9 @@ impl FileInputStream {
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

let mut buf = [0; 1];
let read = rust_file.read(&mut buf).await.unwrap();
let Ok(read) = rust_file.read(&mut buf).await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};
if read == 0 {
return Ok(-1);
}
Expand Down
18 changes: 13 additions & 5 deletions java_runtime/src/classes/java/io/file_output_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ impl FileOutputStream {
let path = jvm.invoke_virtual(&file, "getPath", "()Ljava/lang/String;", ()).await?;
let path = JavaLangString::to_rust_string(jvm, &path).await?;

let fd = context.open(&path, true).await.unwrap();
let fd = FileDescriptor::from_fd(jvm, fd).await?;
let fd = context.open(&path, true).await;
if fd.is_err() {
return Err(jvm.exception("java/io/FileNotFoundException", "File not found").await);
}

let fd = FileDescriptor::from_fd(jvm, fd.unwrap()).await?;

let _: () = jvm
.invoke_special(&this, "java/io/FileOutputStream", "<init>", "(Ljava/io/FileDescriptor;)V", (fd,))
Expand Down Expand Up @@ -81,9 +85,11 @@ impl FileOutputStream {
let mut file = FileDescriptor::file(jvm, context, fd).await?;

let mut buf = vec![0; length as _];
jvm.array_raw_buffer(&buffer).await?.read(offset as _, &mut buf).unwrap();
jvm.array_raw_buffer(&buffer).await?.read(offset as _, &mut buf)?;

file.write(cast_slice(&buf)).await.unwrap();
if file.write(cast_slice(&buf)).await.is_err() {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
}

Ok(())
}
Expand All @@ -94,7 +100,9 @@ impl FileOutputStream {
let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?;
let mut file = FileDescriptor::file(jvm, context, fd).await?;

file.write(&[byte as u8]).await.unwrap();
if file.write(&[byte as u8]).await.is_err() {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
}

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/io/input_stream_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl InputStreamReader {

let read_buf_size: i32 = jvm.get_field(&this, "readBufSize", "I").await?;
let mut read_buf_data = vec![0; read_buf_size as _];
jvm.array_raw_buffer(&read_buf).await?.read(0, &mut read_buf_data).unwrap();
jvm.array_raw_buffer(&read_buf).await?.read(0, &mut read_buf_data)?;

let charset_ref = jvm.get_field(&this, "charset", "Ljava/lang/String;").await?;
let charset = JavaLangString::to_rust_string(jvm, &charset_ref).await?;
Expand Down
3 changes: 2 additions & 1 deletion java_runtime/src/classes/java/io/print_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ impl PrintStream {
async fn println_char(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, char: JavaChar) -> Result<()> {
tracing::debug!("java.io.PrintStream::println({this:?}, {char:?})");

let char = char::from_u32(char as _).unwrap();
// an unpaired surrogate is not a valid char; the JDK charset encoder replaces it with '?'
let char = char::from_u32(char as _).unwrap_or('?');

let java_string = JavaLangString::from_rust_string(jvm, &char.to_string()).await?;

Expand Down
27 changes: 20 additions & 7 deletions java_runtime/src/classes/java/io/random_access_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ impl RandomAccessFile {
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

let mut rust_buf = vec![0; length as usize];
let read = rust_file.read(&mut rust_buf).await.unwrap();
let Ok(read) = rust_file.read(&mut rust_buf).await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};

jvm.array_raw_buffer_mut(&mut buf).await?.write(offset as _, &rust_buf)?;

Expand Down Expand Up @@ -144,8 +146,10 @@ impl RandomAccessFile {
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

let mut rust_buf = vec![0; length as usize];
jvm.array_raw_buffer(&buf).await?.read(offset as _, &mut rust_buf).unwrap();
rust_file.write(&cast_vec(rust_buf)).await.unwrap();
jvm.array_raw_buffer(&buf).await?.read(offset as _, &mut rust_buf)?;
if rust_file.write(&cast_vec(rust_buf)).await.is_err() {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
}

Ok(())
}
Expand All @@ -156,7 +160,9 @@ impl RandomAccessFile {
let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?;
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

rust_file.seek(pos as _).await.unwrap();
if rust_file.seek(pos as _).await.is_err() {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
}

Ok(())
}
Expand All @@ -167,7 +173,9 @@ impl RandomAccessFile {
let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?;
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

rust_file.set_len(new_length as _).await.unwrap();
if rust_file.set_len(new_length as _).await.is_err() {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
}

Ok(())
}
Expand All @@ -178,7 +186,10 @@ impl RandomAccessFile {
let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?;
let rust_file = FileDescriptor::file(jvm, context, fd).await?;

let len = rust_file.metadata().await.unwrap().size;
let Ok(metadata) = rust_file.metadata().await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};
let len = metadata.size;

Ok(len as i64)
}
Expand All @@ -189,7 +200,9 @@ impl RandomAccessFile {
let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?;
let rust_file = FileDescriptor::file(jvm, context, fd).await?;

let pos = rust_file.tell().await.unwrap();
let Ok(pos) = rust_file.tell().await else {
return Err(jvm.exception("java/io/IOException", "I/O error").await);
};

Ok(pos as i64)
}
Expand Down
31 changes: 21 additions & 10 deletions java_runtime/src/classes/java/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,44 @@ mod abstract_method_error;
mod arithmetic_exception;
mod array_index_out_of_bounds_exception;
mod array_store_exception;
mod boolean;
mod byte;
mod character;
mod class;
mod class_cast_exception;
mod class_format_error;
mod class_loader;
mod class_not_found_exception;
mod clone_not_supported_exception;
mod cloneable;
mod comparable;
mod double;
mod error;
mod exception;
mod exception_in_initializer_error;
mod float;
mod illegal_argument_exception;
mod incompatible_class_change_error;
mod index_out_of_bounds_exception;
mod instantiation_error;
mod integer;
mod interrupted_exception;
mod linkage_error;
mod long;
mod math;
mod negative_array_size_exception;
mod no_class_def_found_error;
mod no_such_field_error;
mod no_such_method_error;
mod null_pointer_exception;
mod number;
mod number_format_exception;
mod object;
mod runnable;
mod runtime;
mod runtime_exception;
mod security_exception;
mod short;
mod string;
mod string_buffer;
mod string_index_out_of_bounds_exception;
Expand All @@ -41,15 +50,17 @@ mod unsupported_operation_exception;

pub use self::{
abstract_method_error::AbstractMethodError, arithmetic_exception::ArithmeticException,
array_index_out_of_bounds_exception::ArrayIndexOutOfBoundsException, array_store_exception::ArrayStoreException, class::Class,
class_cast_exception::ClassCastException, class_format_error::ClassFormatError, class_loader::ClassLoader,
clone_not_supported_exception::CloneNotSupportedException, cloneable::Cloneable, comparable::Comparable, error::Error, exception::Exception,
exception_in_initializer_error::ExceptionInInitializerError, illegal_argument_exception::IllegalArgumentException,
incompatible_class_change_error::IncompatibleClassChangeError, index_out_of_bounds_exception::IndexOutOfBoundsException,
instantiation_error::InstantiationError, integer::Integer, interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math,
array_index_out_of_bounds_exception::ArrayIndexOutOfBoundsException, array_store_exception::ArrayStoreException, boolean::Boolean, byte::Byte,
character::Character, class::Class, class_cast_exception::ClassCastException, class_format_error::ClassFormatError, class_loader::ClassLoader,
class_not_found_exception::ClassNotFoundException, clone_not_supported_exception::CloneNotSupportedException, cloneable::Cloneable,
comparable::Comparable, double::Double, error::Error, exception::Exception, exception_in_initializer_error::ExceptionInInitializerError,
float::Float, illegal_argument_exception::IllegalArgumentException, incompatible_class_change_error::IncompatibleClassChangeError,
index_out_of_bounds_exception::IndexOutOfBoundsException, instantiation_error::InstantiationError, integer::Integer,
interrupted_exception::InterruptedException, linkage_error::LinkageError, long::Long, math::Math,
negative_array_size_exception::NegativeArraySizeException, no_class_def_found_error::NoClassDefFoundError, no_such_field_error::NoSuchFieldError,
no_such_method_error::NoSuchMethodError, null_pointer_exception::NullPointerException, number_format_exception::NumberFormatException,
object::Object, runnable::Runnable, runtime::Runtime, runtime_exception::RuntimeException, security_exception::SecurityException, string::String,
string_buffer::StringBuffer, string_index_out_of_bounds_exception::StringIndexOutOfBoundsException, system::System, thread::Thread,
throwable::Throwable, unsupported_operation_exception::UnsupportedOperationException,
no_such_method_error::NoSuchMethodError, null_pointer_exception::NullPointerException, number::Number,
number_format_exception::NumberFormatException, object::Object, runnable::Runnable, runtime::Runtime, runtime_exception::RuntimeException,
security_exception::SecurityException, short::Short, string::String, string_buffer::StringBuffer,
string_index_out_of_bounds_exception::StringIndexOutOfBoundsException, system::System, thread::Thread, throwable::Throwable,
unsupported_operation_exception::UnsupportedOperationException,
};
Loading
Loading