diff --git a/REPORT.md b/REPORT.md index 9c2b77fa..1b88e8dd 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,5 +1,19 @@ # REPORT +## [2026-07-22] 미지원 charset 패닉 → UnsupportedEncodingException (rustjava-unsupported-charset-exception) +- 무엇을: `String.getBytes(charset)`/`new String(byte[], charset)`/`InputStreamReader.read()`의 + `unimplemented!()` 패닉을 `java.io.UnsupportedEncodingException`(신설, IOException 하위) throw 로 + 전환하고, String↔Reader 의 지원 charset 목록을 공용 `charset::Charset` 으로 일원화했다. +- 왜: charset 이름은 자바 코드가 넘기는 완전한 사용자 입력인데 미지원 이름 한 줄에 호스트 + 프로세스가 죽었다. Reader 쪽은 ISO-8859-1 조차 못 받는 String 쪽과의 불일치도 있었다. +- 사용자 영향: `"hi".getBytes("UTF-16")` 류가 이제 try/catch 로 잡히는 자바 예외가 되고, + `file.encoding=ISO-8859-1` 후 InputStreamReader 도 정상 동작한다. 부수 교정: + `System.setProperty` 반환 시그니처를 JDK 규격(`...)Ljava/lang/String;`)으로 수정, + `Throwable.getMessage()` 신설. +- 후속 추천: ① `Charset` 공용화를 계기로 UTF-16/Shift_JIS 등 실제 인코딩 추가는 별건 티켓으로. + ② InputStreamReader 가 read 마다 스트림 디코더를 새로 만들어 버퍼 경계의 multibyte 부분 + 시퀀스가 유실될 수 있는 기존 문제(EUC-KR)가 남아 있다 — 별건 조사 권장. + ## [2026-07-22] tracing-attributes 상한 핀 제거 (rustjava-tracing-attributes-pin-removal) - 무엇을: 워크스페이스 유일의 `#[tracing::instrument]`(thread.rs, "java thread" span)를 `tracing::info_span!` + `Instrument` 수동 span 으로 대체하고, `java_runtime` 의 diff --git a/STATE.md b/STATE.md index 528b972d..695a6e75 100644 --- a/STATE.md +++ b/STATE.md @@ -18,13 +18,12 @@ `java.io.UnsupportedEncodingException`(신설) throw 로 전환, String↔InputStreamReader 지원 charset 을 공용 `charset::Charset` 으로 일치(ISO-8859-1/US-ASCII 가 Reader 에서도 동작). 부수: `System.setProperty` 반환 시그니처 JDK 규격화(Object→String, jvm 부트스트랩 포함), - `Throwable.getMessage()` 신설, 픽스처 `test_data/UnsupportedCharset`. 브랜치 - `unsupported-charset-exception`, PR #5 게이트② 대기. + `Throwable.getMessage()` 신설, 픽스처 `test_data/UnsupportedCharset`. + ★게이트③ 완료: PR #5 approve 핀 `dd9fcdf` 확인(이후 이동분은 문서화된 main 동기화 머지 + 3건뿐, diff-of-diffs 로 코드 동일성 검증) → 스쿼시 머지(2026-07-23), 브랜치 정리 완료. ## 다음 -- 잔여 PR: #5(unsupported-charset) 게이트② approve 후 머지, 브랜치 정리 - (`gh pr merge --delete-branch` → `git branch -D` → `git fetch --prune`) -- ★#5 착지 전 후행 브랜치에 `git merge main` + superset 채택으로 STATE/REPORT 충돌 해소. +- 발권 대기 태스크 없음 — 4개 티켓 전부 게이트③ 착지, 열린 PR/작업 브랜치 0. - ★PR 발권 시 `--repo Jun025/RustJava` 명시(2026-07-22 upstream 오발행 사고 재발 방지). - (범위 밖 잔여) `jvm_rust/src/interpreter.rs:629` `todo!()` (invokedynamic) — 별건 티켓 필요 - (신규 발견) javac 21 산출 익명 내부 클래스(.class)가 "Malformed class file" 로 파싱 실패 — diff --git a/java_runtime/src/charset.rs b/java_runtime/src/charset.rs new file mode 100644 index 00000000..cb6f3863 --- /dev/null +++ b/java_runtime/src/charset.rs @@ -0,0 +1,84 @@ +use alloc::{ + string::{String as RustString, ToString}, + vec::Vec, +}; + +use jvm::{Jvm, Result}; + +// Charsets shared by java.lang.String and java.io.InputStreamReader so both support the same set. +#[derive(Clone, Copy)] +pub enum Charset { + Utf8, + EucKr, + Iso8859_1, + UsAscii, +} + +impl Charset { + pub fn from_name(name: &str) -> Option { + match name.to_ascii_uppercase().replace('_', "-").as_str() { + "UTF-8" | "UTF8" => Some(Self::Utf8), + "EUC-KR" | "EUCKR" | "KS-C-5601-1987" | "MS949" | "CP949" => Some(Self::EucKr), + "ISO-8859-1" | "LATIN1" => Some(Self::Iso8859_1), + "US-ASCII" | "ASCII" => Some(Self::UsAscii), + _ => None, + } + } + + pub async fn resolve(jvm: &Jvm, name: &str) -> Result { + match Self::from_name(name) { + Some(x) => Ok(x), + None => Err(jvm.exception("java/io/UnsupportedEncodingException", name).await), + } + } + + pub fn decode(&self, bytes: &[u8]) -> RustString { + match self { + Self::Utf8 => RustString::from_utf8_lossy(bytes).into_owned(), + Self::EucKr => encoding_rs::EUC_KR.decode(bytes).0.to_string(), + Self::Iso8859_1 | Self::UsAscii => bytes.iter().map(|&b| b as char).collect(), + } + } + + pub fn encode(&self, string: &str) -> Vec { + match self { + Self::Utf8 => string.as_bytes().to_vec(), + Self::EucKr => encoding_rs::EUC_KR.encode(string).0.to_vec(), + Self::Iso8859_1 => string.chars().map(|c| if (c as u32) <= 0xff { c as u8 } else { b'?' }).collect(), + Self::UsAscii => string.chars().map(|c| if c.is_ascii() { c as u8 } else { b'?' }).collect(), + } + } + + pub fn new_stream_decoder(&self) -> CharsetStreamDecoder { + match self { + Self::Utf8 => CharsetStreamDecoder::EncodingRs(encoding_rs::UTF_8.new_decoder_without_bom_handling()), + Self::EucKr => CharsetStreamDecoder::EncodingRs(encoding_rs::EUC_KR.new_decoder_without_bom_handling()), + Self::Iso8859_1 | Self::UsAscii => CharsetStreamDecoder::ByteToChar, + } + } +} + +pub enum CharsetStreamDecoder { + EncodingRs(encoding_rs::Decoder), + // single-byte charsets where each byte maps to the same code point + ByteToChar, +} + +impl CharsetStreamDecoder { + // Returns (bytes consumed, utf-16 code units written), like encoding_rs's decode_to_utf16. + pub fn decode_to_utf16(&mut self, src: &[u8], dst: &mut [u16], last: bool) -> (usize, usize) { + match self { + Self::EncodingRs(decoder) => { + let (_, read, written, _) = decoder.decode_to_utf16(src, dst, last); + (read, written) + } + Self::ByteToChar => { + let len = core::cmp::min(src.len(), dst.len()); + for (d, &s) in dst.iter_mut().zip(src) { + *d = s as u16; + } + (len, len) + } + } + } +} diff --git a/java_runtime/src/classes/java/io.rs b/java_runtime/src/classes/java/io.rs index ab67823e..7f5c0250 100644 --- a/java_runtime/src/classes/java/io.rs +++ b/java_runtime/src/classes/java/io.rs @@ -23,6 +23,7 @@ mod random_access_file; mod reader; mod serializable; mod string_writer; +mod unsupported_encoding_exception; mod writer; pub use self::{ @@ -32,5 +33,5 @@ pub use self::{ file_not_found_exception::FileNotFoundException, file_output_stream::FileOutputStream, filter_input_stream::FilterInputStream, filter_output_stream::FilterOutputStream, input_stream::InputStream, input_stream_reader::InputStreamReader, io_exception::IOException, output_stream::OutputStream, print_stream::PrintStream, print_writer::PrintWriter, random_access_file::RandomAccessFile, reader::Reader, - serializable::Serializable, string_writer::StringWriter, writer::Writer, + serializable::Serializable, string_writer::StringWriter, unsupported_encoding_exception::UnsupportedEncodingException, writer::Writer, }; diff --git a/java_runtime/src/classes/java/io/input_stream_reader.rs b/java_runtime/src/classes/java/io/input_stream_reader.rs index 09c9763a..b7ed46a8 100644 --- a/java_runtime/src/classes/java/io/input_stream_reader.rs +++ b/java_runtime/src/classes/java/io/input_stream_reader.rs @@ -3,13 +3,13 @@ use core::cmp::min; use alloc::vec; use bytemuck::{cast_slice, cast_vec}; -use encoding_rs::{EUC_KR, UTF_8}; use java_class_proto::{JavaFieldProto, JavaMethodProto}; use jvm::{Array, ClassInstanceRef, JavaChar, Jvm, Result, runtime::JavaLangString}; use crate::{ RuntimeClassProto, RuntimeContext, + charset::Charset, classes::java::{io::InputStream, lang::System}, }; @@ -107,16 +107,10 @@ impl InputStreamReader { let charset_ref = jvm.get_field(&this, "charset", "Ljava/lang/String;").await?; let charset = JavaLangString::to_rust_string(jvm, &charset_ref).await?; - let mut decoder = if charset == "UTF-8" { - UTF_8.new_decoder_without_bom_handling() - } else if charset == "EUC-KR" { - EUC_KR.new_decoder_without_bom_handling() - } else { - unimplemented!("unsupported charset: {}", charset) - }; + let mut decoder = Charset::resolve(jvm, &charset).await?.new_stream_decoder(); let mut decoded = vec![0; BUF_SIZE * 3]; - let (_, read, wrote, _) = decoder.decode_to_utf16(&cast_vec(read_buf_data), &mut decoded, false); + let (read, wrote) = decoder.decode_to_utf16(&cast_vec(read_buf_data), &mut decoded, false); // advance readBuf let _: () = jvm diff --git a/java_runtime/src/classes/java/io/unsupported_encoding_exception.rs b/java_runtime/src/classes/java/io/unsupported_encoding_exception.rs new file mode 100644 index 00000000..315db907 --- /dev/null +++ b/java_runtime/src/classes/java/io/unsupported_encoding_exception.rs @@ -0,0 +1,43 @@ +use alloc::vec; + +use java_class_proto::JavaMethodProto; +use jvm::{ClassInstanceRef, Jvm, Result}; + +use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String}; + +// class java.io.UnsupportedEncodingException +pub struct UnsupportedEncodingException; + +impl UnsupportedEncodingException { + pub fn as_proto() -> RuntimeClassProto { + RuntimeClassProto { + name: "java/io/UnsupportedEncodingException", + parent_class: Some("java/io/IOException"), + interfaces: vec![], + methods: vec![ + JavaMethodProto::new("", "()V", Self::init, Default::default()), + JavaMethodProto::new("", "(Ljava/lang/String;)V", Self::init_with_message, Default::default()), + ], + fields: vec![], + access_flags: Default::default(), + } + } + + async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.io.UnsupportedEncodingException::({this:?})"); + + let _: () = jvm.invoke_special(&this, "java/io/IOException", "", "()V", ()).await?; + + Ok(()) + } + + async fn init_with_message(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, message: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.io.UnsupportedEncodingException::({this:?}, {message:?})"); + + let _: () = jvm + .invoke_special(&this, "java/io/IOException", "", "(Ljava/lang/String;)V", (message,)) + .await?; + + Ok(()) + } +} diff --git a/java_runtime/src/classes/java/lang/string.rs b/java_runtime/src/classes/java/lang/string.rs index 18fb9d0f..477c16a6 100644 --- a/java_runtime/src/classes/java/lang/string.rs +++ b/java_runtime/src/classes/java/lang/string.rs @@ -15,6 +15,7 @@ use jvm::{Array, ClassInstanceRef, JavaChar, Jvm, Result, runtime::JavaLangStrin use crate::{ RuntimeClassProto, RuntimeContext, + charset::Charset, classes::java::lang::{Object, System}, }; @@ -166,7 +167,7 @@ impl String { let bytes: Vec = jvm.load_array(&value, offset as _, count as _).await?; let charset = System::get_charset(jvm).await?; - let string = Self::decode_str(&charset, cast_slice(&bytes)); + let string = Charset::resolve(jvm, &charset).await?.decode(cast_slice(&bytes)); let utf16 = string.encode_utf16().collect::>(); @@ -280,7 +281,7 @@ impl String { let string = JavaLangString::to_rust_string(jvm, &this.clone()).await?; let charset = System::get_charset(jvm).await?; - let bytes = cast_vec(Self::encode_str(&charset, &string)); + let bytes = cast_vec(Charset::resolve(jvm, &charset).await?.encode(&string)); let mut byte_array = jvm.instantiate_array("B", bytes.len()).await?; jvm.array_raw_buffer_mut(&mut byte_array).await?.write(0, &bytes)?; @@ -563,7 +564,7 @@ impl String { let bytes: Vec = jvm.load_array(&value, offset as _, count as _).await?; let charset = JavaLangString::to_rust_string(jvm, &charset_name).await?; - let string = Self::decode_str(&charset, cast_slice(&bytes)); + let string = Charset::resolve(jvm, &charset).await?.decode(cast_slice(&bytes)); let utf16 = string.encode_utf16().collect::>(); @@ -603,7 +604,7 @@ impl String { let string = JavaLangString::to_rust_string(jvm, &this).await?; let charset = JavaLangString::to_rust_string(jvm, &charset_name).await?; - let bytes = cast_vec(Self::encode_str(&charset, &string)); + let bytes = cast_vec(Charset::resolve(jvm, &charset).await?.encode(&string)); let mut byte_array = jvm.instantiate_array("B", bytes.len()).await?; jvm.array_raw_buffer_mut(&mut byte_array).await?.write(0, &bytes)?; @@ -778,23 +779,4 @@ impl String { Ok(new_string.into()) } - - fn decode_str(charset: &str, bytes: &[u8]) -> RustString { - match charset.to_ascii_uppercase().replace('_', "-").as_str() { - "UTF-8" | "UTF8" => RustString::from_utf8_lossy(bytes).into_owned(), - "EUC-KR" | "EUCKR" | "KS-C-5601-1987" | "MS949" | "CP949" => encoding_rs::EUC_KR.decode(bytes).0.to_string(), - "ISO-8859-1" | "LATIN1" | "US-ASCII" | "ASCII" => bytes.iter().map(|&b| b as char).collect(), - _ => unimplemented!("unsupported charset: {}", charset), - } - } - - fn encode_str(charset: &str, string: &str) -> Vec { - match charset.to_ascii_uppercase().replace('_', "-").as_str() { - "UTF-8" | "UTF8" => string.as_bytes().to_vec(), - "EUC-KR" | "EUCKR" | "KS-C-5601-1987" | "MS949" | "CP949" => encoding_rs::EUC_KR.encode(string).0.to_vec(), - "ISO-8859-1" | "LATIN1" => string.chars().map(|c| if (c as u32) <= 0xff { c as u8 } else { b'?' }).collect(), - "US-ASCII" | "ASCII" => string.chars().map(|c| if c.is_ascii() { c as u8 } else { b'?' }).collect(), - _ => unimplemented!("unsupported charset: {}", charset), - } - } } diff --git a/java_runtime/src/classes/java/lang/system.rs b/java_runtime/src/classes/java/lang/system.rs index 6b9f8451..7bc08cda 100644 --- a/java_runtime/src/classes/java/lang/system.rs +++ b/java_runtime/src/classes/java/lang/system.rs @@ -43,7 +43,7 @@ impl System { ), JavaMethodProto::new( "setProperty", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", Self::set_property, MethodAccessFlags::STATIC, ), diff --git a/java_runtime/src/classes/java/lang/throwable.rs b/java_runtime/src/classes/java/lang/throwable.rs index 7cc3f08d..9c2c04f6 100644 --- a/java_runtime/src/classes/java/lang/throwable.rs +++ b/java_runtime/src/classes/java/lang/throwable.rs @@ -30,6 +30,7 @@ impl Throwable { Self::init_with_message_and_cause, Default::default(), ), + JavaMethodProto::new("getMessage", "()Ljava/lang/String;", Self::get_message, Default::default()), JavaMethodProto::new("getCause", "()Ljava/lang/Throwable;", Self::get_cause, Default::default()), JavaMethodProto::new( "initCause", @@ -126,6 +127,12 @@ impl Throwable { Ok(()) } + async fn get_message(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result> { + tracing::debug!("java.lang.Throwable::getMessage({this:?})"); + + jvm.get_field(&this, "detailMessage", "Ljava/lang/String;").await + } + async fn get_cause(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result> { tracing::debug!("java.lang.Throwable::getCause({this:?})"); diff --git a/java_runtime/src/lib.rs b/java_runtime/src/lib.rs index f8bcfe3a..d59696d3 100644 --- a/java_runtime/src/lib.rs +++ b/java_runtime/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] extern crate alloc; +mod charset; pub mod classes; mod loader; mod runtime; diff --git a/java_runtime/src/loader.rs b/java_runtime/src/loader.rs index 4bc809d6..ca21bb8e 100644 --- a/java_runtime/src/loader.rs +++ b/java_runtime/src/loader.rs @@ -31,6 +31,7 @@ pub fn get_runtime_class_proto(name: &str) -> Option { crate::classes::java::io::Reader::as_proto(), crate::classes::java::io::Serializable::as_proto(), crate::classes::java::io::StringWriter::as_proto(), + crate::classes::java::io::UnsupportedEncodingException::as_proto(), crate::classes::java::io::Writer::as_proto(), crate::classes::java::lang::AbstractMethodError::as_proto(), crate::classes::java::lang::ArithmeticException::as_proto(), diff --git a/java_runtime/tests/classes/java/io/test_input_stream_reader.rs b/java_runtime/tests/classes/java/io/test_input_stream_reader.rs index dc0608ca..35396918 100644 --- a/java_runtime/tests/classes/java/io/test_input_stream_reader.rs +++ b/java_runtime/tests/classes/java/io/test_input_stream_reader.rs @@ -1,6 +1,7 @@ use alloc::{vec, vec::Vec}; -use jvm::{JavaChar, Result}; +use java_runtime::classes::java::lang::Object; +use jvm::{ClassInstanceRef, JavaChar, JavaError, Result, runtime::JavaLangString}; use test_utils::test_jvm; @@ -29,3 +30,63 @@ async fn test_isr() -> Result<()> { Ok(()) } + +async fn set_file_encoding(jvm: &jvm::Jvm, encoding: &str) -> Result<()> { + let key = JavaLangString::from_rust_string(jvm, "file.encoding").await?; + let value = JavaLangString::from_rust_string(jvm, encoding).await?; + let _: ClassInstanceRef = jvm + .invoke_static( + "java/lang/System", + "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (key, value), + ) + .await?; + + Ok(()) +} + +#[tokio::test] +async fn test_isr_iso_8859_1() -> Result<()> { + let jvm = test_jvm().await?; + + set_file_encoding(&jvm, "ISO-8859-1").await?; + + let mut buffer = jvm.instantiate_array("B", 4).await?; + jvm.array_raw_buffer_mut(&mut buffer).await?.write(0, &[0x61, 0xe9, 0xfc, 0x62])?; + + let is = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (buffer,)).await?; + let isr = jvm.new_class("java/io/InputStreamReader", "(Ljava/io/InputStream;)V", (is,)).await?; + + let buf = jvm.instantiate_array("C", 10).await?; + let read: i32 = jvm.invoke_virtual(&isr, "read", "([CII)I", (buf.clone(), 0, 4)).await?; + + assert_eq!(read, 4); + let buf_data: Vec = jvm.load_array(&buf, 0, 4).await?; + assert_eq!(buf_data, vec![0x61, 0xe9, 0xfc, 0x62]); + + Ok(()) +} + +#[tokio::test] +async fn test_isr_unsupported_charset_throws() -> Result<()> { + let jvm = test_jvm().await?; + + set_file_encoding(&jvm, "UTF-16").await?; + + let mut buffer = jvm.instantiate_array("B", 2).await?; + jvm.array_raw_buffer_mut(&mut buffer).await?.write(0, b"hi")?; + + let is = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (buffer,)).await?; + let isr = jvm.new_class("java/io/InputStreamReader", "(Ljava/io/InputStream;)V", (is,)).await?; + + let buf = jvm.instantiate_array("C", 10).await?; + let result: Result = jvm.invoke_virtual(&isr, "read", "([CII)I", (buf, 0, 2)).await; + let Err(JavaError::JavaException(exception)) = result else { + panic!("Expected JavaException, got {:?}", result); + }; + assert!(jvm.is_instance(&*exception, "java/io/UnsupportedEncodingException")); + assert!(jvm.is_instance(&*exception, "java/io/IOException")); + + Ok(()) +} diff --git a/java_runtime/tests/classes/java/lang/test_string.rs b/java_runtime/tests/classes/java/lang/test_string.rs index 8289b91b..0f99ed46 100644 --- a/java_runtime/tests/classes/java/lang/test_string.rs +++ b/java_runtime/tests/classes/java/lang/test_string.rs @@ -413,3 +413,44 @@ async fn test_get_bytes_ascii_replaces_non_ascii() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn test_get_bytes_unsupported_charset_throws() -> Result<()> { + let jvm = test_jvm().await?; + + let string = JavaLangString::from_rust_string(&jvm, "hi").await?; + let charset = JavaLangString::from_rust_string(&jvm, "UTF-16").await?; + + let result: Result>> = jvm.invoke_virtual(&string, "getBytes", "(Ljava/lang/String;)[B", (charset,)).await; + let Err(JavaError::JavaException(exception)) = result else { + panic!("Expected JavaException, got {:?}", result); + }; + assert!(jvm.is_instance(&*exception, "java/io/UnsupportedEncodingException")); + assert!(jvm.is_instance(&*exception, "java/io/IOException")); + + let message: ClassInstanceRef = jvm.invoke_virtual(&exception, "getMessage", "()Ljava/lang/String;", ()).await?; + assert_eq!(JavaLangString::to_rust_string(&jvm, &message).await?, "UTF-16"); + + Ok(()) +} + +#[tokio::test] +async fn test_new_string_unsupported_charset_throws() -> Result<()> { + let jvm = test_jvm().await?; + + let mut bytes = jvm.instantiate_array("B", 2).await?; + jvm.array_raw_buffer_mut(&mut bytes).await?.write(0, b"hi")?; + + let charset = JavaLangString::from_rust_string(&jvm, "Shift_JIS").await?; + + let result = jvm.new_class("java/lang/String", "([BLjava/lang/String;)V", (bytes, charset)).await; + let Err(JavaError::JavaException(exception)) = result else { + panic!("Expected JavaException, got {:?}", result); + }; + assert!(jvm.is_instance(&*exception, "java/io/UnsupportedEncodingException")); + + let message: ClassInstanceRef = jvm.invoke_virtual(&exception, "getMessage", "()Ljava/lang/String;", ()).await?; + assert_eq!(JavaLangString::to_rust_string(&jvm, &message).await?, "Shift_JIS"); + + Ok(()) +} diff --git a/jvm/src/jvm.rs b/jvm/src/jvm.rs index 41d965d9..dd95b589 100644 --- a/jvm/src/jvm.rs +++ b/jvm/src/jvm.rs @@ -97,7 +97,7 @@ impl Jvm { .invoke_static( "java/lang/System", "setProperty", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (key, value), ) .await?; diff --git a/test_data/UnsupportedCharset.class b/test_data/UnsupportedCharset.class new file mode 100644 index 00000000..b8748f08 Binary files /dev/null and b/test_data/UnsupportedCharset.class differ diff --git a/test_data/UnsupportedCharset.txt b/test_data/UnsupportedCharset.txt new file mode 100644 index 00000000..89833293 --- /dev/null +++ b/test_data/UnsupportedCharset.txt @@ -0,0 +1,8 @@ +Caught getBytes +UTF-16 +Caught String ctor +Shift_JIS +3 +aéb +Caught reader +UTF-16 diff --git a/test_data/src/UnsupportedCharset.java b/test_data/src/UnsupportedCharset.java new file mode 100644 index 00000000..4ecd8f4f --- /dev/null +++ b/test_data/src/UnsupportedCharset.java @@ -0,0 +1,42 @@ +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; + +class UnsupportedCharset { + public static void main(String[] args) throws Exception { + try { + "hi".getBytes("UTF-16"); + System.out.println("Should not be executed"); + } catch (UnsupportedEncodingException e) { + System.out.println("Caught getBytes"); + System.out.println(e.getMessage()); + } + + try { + new String(new byte[] {104, 105}, "Shift_JIS"); + System.out.println("Should not be executed"); + } catch (UnsupportedEncodingException e) { + System.out.println("Caught String ctor"); + System.out.println(e.getMessage()); + } + + System.setProperty("file.encoding", "ISO-8859-1"); + ByteArrayInputStream latin1Stream = new ByteArrayInputStream(new byte[] {0x61, (byte) 0xe9, 0x62}); + InputStreamReader latin1Reader = new InputStreamReader(latin1Stream); + char[] buf = new char[3]; + int read = latin1Reader.read(buf, 0, 3); + System.out.println(read); + System.out.println(new String(buf, 0, read)); + + System.setProperty("file.encoding", "UTF-16"); + ByteArrayInputStream utf16Stream = new ByteArrayInputStream(new byte[] {104, 105}); + InputStreamReader utf16Reader = new InputStreamReader(utf16Stream); + try { + utf16Reader.read(buf, 0, 2); + System.out.println("Should not be executed"); + } catch (UnsupportedEncodingException e) { + System.out.println("Caught reader"); + System.out.println(e.getMessage()); + } + } +}