Skip to content

Commit

Permalink
Merge pull request #161 from theseus-rs/classloader-impl
Browse files Browse the repository at this point in the history
fix: implement java.lang.ClassLoader methods
  • Loading branch information
brianheineman authored Dec 4, 2024
2 parents bfb5ec9 + e628ceb commit be18244
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 58 deletions.
36 changes: 36 additions & 0 deletions ristretto_vm/src/java_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ pub enum JavaError {
source_class_name: String,
target_class_name: String,
},
/// `ClassFormatError`
/// See: <https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/ClassFormatError.html>
#[error("{0}")]
ClassFormatError(String),
/// `IndexOutOfBoundsException`
/// See: <https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/IndexOutOfBoundsException.html>
#[error("Index: {index}, Size {size}")]
IndexOutOfBoundsException { index: i32, size: i32 },
/// `NoClassDefFoundError`
/// See: <https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/NoClassDefFoundError.html>
#[error("{0}")]
NoClassDefFoundError(String),
/// `NullPointerException`
/// See: <https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/NullPointerException.html>
#[error("{0}")]
Expand All @@ -32,6 +44,9 @@ impl JavaError {
}
JavaError::ArithmeticException(_) => "java/lang/ArithmeticException",
JavaError::ClassCastException { .. } => "java/lang/ClassCastException",
JavaError::ClassFormatError(_) => "java/lang/ClassFormatError",
JavaError::IndexOutOfBoundsException { .. } => "java/lang/IndexOutOfBoundsException",
JavaError::NoClassDefFoundError(_) => "java/lang/NoClassDefFoundError",
JavaError::NullPointerException(_) => "java/lang/NullPointerException",
}
}
Expand Down Expand Up @@ -80,6 +95,27 @@ mod tests {
);
}

#[test]
fn test_class_format_error() {
let error = JavaError::ClassFormatError("invalid class format".to_string());
assert_eq!(error.class_name(), "java/lang/ClassFormatError");
assert_eq!(error.message(), "invalid class format");
}

#[test]
fn test_index_out_of_bounds_exception() {
let error = JavaError::IndexOutOfBoundsException { index: 5, size: 3 };
assert_eq!(error.class_name(), "java/lang/IndexOutOfBoundsException");
assert_eq!(error.message(), "Index: 5, Size 3");
}

#[test]
fn test_no_class_def_found_error() {
let error = JavaError::NoClassDefFoundError("java/lang/String".to_string());
assert_eq!(error.class_name(), "java/lang/NoClassDefFoundError");
assert_eq!(error.message(), "java/lang/String");
}

#[test]
fn test_null_pointer_exception() {
let error = JavaError::NullPointerException("null".to_string());
Expand Down
63 changes: 23 additions & 40 deletions ristretto_vm/src/native_methods/java_lang_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,7 @@ async fn get_class_access_flags_raw_0(
thread: Arc<Thread>,
mut arguments: Arguments,
) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError(
"getClassAccessFlagsRaw0: no arguments".to_string(),
));
};
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
let class_file = class.class_file();
let access_flags = &class_file.access_flags;
Expand All @@ -306,11 +302,7 @@ async fn get_class_file_version_0(
thread: Arc<Thread>,
mut arguments: Arguments,
) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError(
"getClassFileVersion0: no arguments".to_string(),
));
};
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
let class_file = class.class_file();
let version = &class_file.version;
Expand All @@ -327,12 +319,7 @@ async fn get_component_type(
thread: Arc<Thread>,
mut arguments: Arguments,
) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError(
"getComponentType: no class reference".to_string(),
));
};

let object = arguments.pop_object()?;
let class = object.class();
if class.is_array() {
return Ok(Some(Value::Object(None)));
Expand Down Expand Up @@ -474,9 +461,7 @@ async fn get_interfaces_0(_thread: Arc<Thread>, _arguments: Arguments) -> Result

#[async_recursion(?Send)]
async fn get_modifiers(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError("getModifiers: no class".to_string()));
};
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
let class_file = class.class_file();
let access_flags = &class_file.access_flags.bits();
Expand All @@ -494,8 +479,12 @@ async fn get_modifiers(thread: Arc<Thread>, mut arguments: Arguments) -> Result<
}

#[async_recursion(?Send)]
async fn get_name_0(_thread: Arc<Thread>, _arguments: Arguments) -> Result<Option<Value>> {
todo!()
async fn get_name_0(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
let class_name = class.name().replace('/', ".");
let value = class_name.to_value();
Ok(Some(value))
}

#[async_recursion(?Send)]
Expand All @@ -513,11 +502,7 @@ async fn get_permitted_subclasses_0(
thread: Arc<Thread>,
mut arguments: Arguments,
) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError(
"getPermittedSubclasses0: no arguments".to_string(),
));
};
let object = arguments.pop_object()?;
let _class = get_class(&thread, &object).await?;
// TODO: add support for sealed classes
Ok(None)
Expand Down Expand Up @@ -596,9 +581,7 @@ async fn get_simple_binary_name_0(

#[async_recursion(?Send)]
async fn get_superclass(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError("getSuperclass: no arguments".to_string()));
};
let object = arguments.pop_object()?;
let class = object.class();
match class.parent()? {
Some(parent) => {
Expand All @@ -613,15 +596,18 @@ async fn get_superclass(thread: Arc<Thread>, mut arguments: Arguments) -> Result
}

#[async_recursion(?Send)]
async fn init_class_name(_thread: Arc<Thread>, _arguments: Arguments) -> Result<Option<Value>> {
todo!()
async fn init_class_name(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
// TODO: implement support for hidden classes
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
let class_name = class.name().replace('/', ".");
let value = class_name.to_value();
Ok(Some(value))
}

#[async_recursion(?Send)]
async fn is_array(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError("isArray: no arguments".to_string()));
};
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
if class.is_array() {
Ok(Some(Value::from(true)))
Expand Down Expand Up @@ -654,6 +640,7 @@ async fn is_assignable_from(

#[async_recursion(?Send)]
async fn is_hidden(_thread: Arc<Thread>, _arguments: Arguments) -> Result<Option<Value>> {
// TODO: implement support for hidden classes
Ok(Some(Value::from(false)))
}

Expand All @@ -664,9 +651,7 @@ async fn is_instance(_thread: Arc<Thread>, _arguments: Arguments) -> Result<Opti

#[async_recursion(?Send)]
async fn is_interface(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError("isInterface: no arguments".to_string()));
};
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
if class.is_interface() {
Ok(Some(Value::from(true)))
Expand All @@ -677,9 +662,7 @@ async fn is_interface(thread: Arc<Thread>, mut arguments: Arguments) -> Result<O

#[async_recursion(?Send)]
async fn is_primitive(thread: Arc<Thread>, mut arguments: Arguments) -> Result<Option<Value>> {
let Some(Reference::Object(object)) = arguments.pop_reference()? else {
return Err(InternalError("isPrimitive: no arguments".to_string()));
};
let object = arguments.pop_object()?;
let class = get_class(&thread, &object).await?;
if class.is_primitive() {
Ok(Some(Value::from(true)))
Expand Down
Loading

0 comments on commit be18244

Please sign in to comment.