Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions java_runtime/src/classes/java/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod jar;
pub mod logging;
pub mod regex;
pub mod zip;

Expand Down
15 changes: 15 additions & 0 deletions java_runtime/src/classes/java/util/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mod console_handler;
mod filter;
mod formatter;
mod handler;
mod level;
mod log_manager;
mod log_record;
mod logger;
mod simple_formatter;
mod stream_handler;

pub use self::{
console_handler::ConsoleHandler, filter::Filter, formatter::Formatter, handler::Handler, level::Level, log_manager::LogManager,
log_record::LogRecord, logger::Logger, simple_formatter::SimpleFormatter, stream_handler::StreamHandler,
};
65 changes: 65 additions & 0 deletions java_runtime/src/classes/java/util/logging/console_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use alloc::vec;

use java_class_proto::JavaMethodProto;
use java_constants::{ClassAccessFlags, MethodAccessFlags};
use jvm::{ClassInstanceRef, Jvm, Result};

use crate::{RuntimeClassProto, RuntimeContext, classes::java::io::OutputStream};

use super::LogRecord;

// public class java.util.logging.ConsoleHandler
pub struct ConsoleHandler;

impl ConsoleHandler {
pub fn as_proto() -> RuntimeClassProto {
RuntimeClassProto {
name: "java/util/logging/ConsoleHandler",
parent_class: Some("java/util/logging/StreamHandler"),
interfaces: vec![],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, MethodAccessFlags::PUBLIC),
JavaMethodProto::new("close", "()V", Self::close, MethodAccessFlags::PUBLIC),
JavaMethodProto::new("publish", "(Ljava/util/logging/LogRecord;)V", Self::publish, MethodAccessFlags::PUBLIC),
],
fields: vec![],
access_flags: ClassAccessFlags::PUBLIC,
}
}

async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
tracing::debug!("java.util.logging.ConsoleHandler::<init>({this:?})");

let _: () = jvm.invoke_special(&this, "java/util/logging/StreamHandler", "<init>", "()V", ()).await?;
let error: ClassInstanceRef<OutputStream> = jvm.get_static_field("java/lang/System", "err", "Ljava/io/PrintStream;").await?;
jvm.invoke_special(
&this,
"java/util/logging/StreamHandler",
"setOutputStream",
"(Ljava/io/OutputStream;)V",
(error,),
)
.await
}

async fn close(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
tracing::debug!("java.util.logging.ConsoleHandler::close({this:?})");

jvm.invoke_special(&this, "java/util/logging/StreamHandler", "flush", "()V", ()).await
}

async fn publish(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, record: ClassInstanceRef<LogRecord>) -> Result<()> {
tracing::debug!("java.util.logging.ConsoleHandler::publish({this:?}, {record:?})");

let _: () = jvm
.invoke_special(
&this,
"java/util/logging/StreamHandler",
"publish",
"(Ljava/util/logging/LogRecord;)V",
(record,),
)
.await?;
jvm.invoke_special(&this, "java/util/logging/StreamHandler", "flush", "()V", ()).await
}
}
26 changes: 26 additions & 0 deletions java_runtime/src/classes/java/util/logging/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use alloc::vec;

use java_class_proto::JavaMethodProto;
use java_constants::{ClassAccessFlags, MethodAccessFlags};

use crate::RuntimeClassProto;

// public interface java.util.logging.Filter
pub struct Filter;

impl Filter {
pub fn as_proto() -> RuntimeClassProto {
RuntimeClassProto {
name: "java/util/logging/Filter",
parent_class: None,
interfaces: vec![],
methods: vec![JavaMethodProto::new_abstract(
"isLoggable",
"(Ljava/util/logging/LogRecord;)Z",
MethodAccessFlags::PUBLIC | MethodAccessFlags::ABSTRACT,
)],
fields: vec![],
access_flags: ClassAccessFlags::PUBLIC | ClassAccessFlags::INTERFACE | ClassAccessFlags::ABSTRACT,
}
}
}
149 changes: 149 additions & 0 deletions java_runtime/src/classes/java/util/logging/formatter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use alloc::{string::String as RustString, vec, vec::Vec};

use java_class_proto::JavaMethodProto;
use java_constants::{ClassAccessFlags, MethodAccessFlags};
use jvm::{Array, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};

use crate::{
RuntimeClassProto, RuntimeContext,
classes::java::lang::{Object, String},
};

use super::LogRecord;

// public abstract class java.util.logging.Formatter
pub struct Formatter;

impl Formatter {
pub fn as_proto() -> RuntimeClassProto {
RuntimeClassProto {
name: "java/util/logging/Formatter",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, MethodAccessFlags::PROTECTED),
JavaMethodProto::new_abstract(
"format",
"(Ljava/util/logging/LogRecord;)Ljava/lang/String;",
MethodAccessFlags::PUBLIC | MethodAccessFlags::ABSTRACT,
),
JavaMethodProto::new(
"formatMessage",
"(Ljava/util/logging/LogRecord;)Ljava/lang/String;",
Self::format_message,
MethodAccessFlags::PUBLIC,
),
JavaMethodProto::new(
"getHead",
"(Ljava/util/logging/Handler;)Ljava/lang/String;",
Self::get_head,
MethodAccessFlags::PUBLIC,
),
JavaMethodProto::new(
"getTail",
"(Ljava/util/logging/Handler;)Ljava/lang/String;",
Self::get_tail,
MethodAccessFlags::PUBLIC,
),
],
fields: vec![],
access_flags: ClassAccessFlags::PUBLIC | ClassAccessFlags::ABSTRACT,
}
}

async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
tracing::debug!("java.util.logging.Formatter::<init>({this:?})");

jvm.invoke_special(&this, "java/lang/Object", "<init>", "()V", ()).await
}

async fn format_message(
jvm: &Jvm,
_: &mut RuntimeContext,
this: ClassInstanceRef<Self>,
record: ClassInstanceRef<LogRecord>,
) -> Result<ClassInstanceRef<String>> {
tracing::debug!("java.util.logging.Formatter::formatMessage({this:?}, {record:?})");

if record.is_null() {
return Err(jvm.exception("java/lang/NullPointerException", "record").await);
}

let message: ClassInstanceRef<String> = jvm.invoke_virtual(&record, "getMessage", "()Ljava/lang/String;", ()).await?;
if message.is_null() {
return Ok(message);
}

let parameters: ClassInstanceRef<Array<Object>> = jvm.invoke_virtual(&record, "getParameters", "()[Ljava/lang/Object;", ()).await?;
if parameters.is_null() {
return Ok(message);
}

let parameter_count = jvm.array_length(&parameters).await?.min(4);
Comment thread
dlunch marked this conversation as resolved.
Outdated
if parameter_count == 0 {
return Ok(message);
}

let parameters: Vec<ClassInstanceRef<Object>> = jvm.load_array(&parameters, 0, parameter_count).await?;
let mut replacements: Vec<Option<RustString>> = vec![None; 4];
for (index, parameter) in parameters.into_iter().enumerate() {
replacements[index] = Some(if parameter.is_null() {
RustString::from("null")
} else {
let value: ClassInstanceRef<String> = jvm.invoke_virtual(&parameter, "toString", "()Ljava/lang/String;", ()).await?;
if value.is_null() {
RustString::from("null")
} else {
JavaLangString::to_rust_string(jvm, &value).await?
}
});
}

let message = JavaLangString::to_rust_string(jvm, &message).await?;
let characters: Vec<char> = message.chars().collect();
let mut formatted = RustString::new();
let mut index = 0;
while index < characters.len() {
if index + 2 < characters.len()
&& characters[index] == '{'
&& ('0'..='3').contains(&characters[index + 1])
&& characters[index + 2] == '}'
{
let parameter_index = characters[index + 1] as usize - '0' as usize;
if let Some(replacement) = &replacements[parameter_index] {
formatted.push_str(replacement);
} else {
formatted.extend(&characters[index..index + 3]);
}
index += 3;
} else {
formatted.push(characters[index]);
index += 1;
}
}

Ok(JavaLangString::from_rust_string(jvm, &formatted).await?.into())
}

async fn get_head(
jvm: &Jvm,
_: &mut RuntimeContext,
this: ClassInstanceRef<Self>,
_: ClassInstanceRef<super::Handler>,
) -> Result<ClassInstanceRef<String>> {
tracing::debug!("java.util.logging.Formatter::getHead({this:?})");

Ok(JavaLangString::from_rust_string(jvm, "").await?.into())
}

async fn get_tail(
jvm: &Jvm,
_: &mut RuntimeContext,
this: ClassInstanceRef<Self>,
_: ClassInstanceRef<super::Handler>,
) -> Result<ClassInstanceRef<String>> {
tracing::debug!("java.util.logging.Formatter::getTail({this:?})");

Ok(JavaLangString::from_rust_string(jvm, "").await?.into())
}
}
Loading