use crate::Result;
use crate::thread::Thread;
use ristretto_classloader::Class;
use std::sync::Arc;
pub trait Assignable {
async fn is_assignable_from(&self, thread: &Thread, other: &Arc<Class>) -> Result<bool>;
}
impl Assignable for Arc<Class> {
async fn is_assignable_from(&self, thread: &Thread, other: &Arc<Class>) -> Result<bool> {
if Arc::ptr_eq(self, other) {
return Ok(true);
}
let self_name = self.name();
if self_name == "java/lang/Object" {
return Ok(true);
}
if other.is_array() && self.is_array() {
let self_dimensions = self.array_dimensions();
let other_dimensions = other.array_dimensions();
if self_dimensions == other_dimensions {
let other_name = other.name();
let self_is_primitive = !self_name.ends_with(';');
let other_is_primitive = !other_name.ends_with(';');
if self_is_primitive && other_is_primitive {
return Ok(self_name == other_name);
}
if self_is_primitive != other_is_primitive {
return Ok(false);
}
let (Some(self_component), Some(other_component)) =
(self.component_type(), other.component_type())
else {
return Ok(false);
};
let self_component_class = thread.class(self_component).await?;
let other_component_class = thread.class(other_component).await?;
return Box::pin(
self_component_class.is_assignable_from(thread, &other_component_class),
)
.await;
} else if self_dimensions < other_dimensions && self_name == "[Ljava/lang/Object;" {
return Ok(true);
}
return Ok(false);
}
if let Some(parent) = other.parent()?
&& Box::pin(self.is_assignable_from(thread, &parent)).await?
{
return Ok(true);
}
for interface in other.interfaces()? {
if Box::pin(self.is_assignable_from(thread, &interface)).await? {
return Ok(true);
}
}
Ok(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_object_is_assignable_from_object() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_class = thread.class("java.lang.Object").await?;
assert!(
object_class
.is_assignable_from(&thread, &object_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_is_assignable_from_string() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let string_class = thread.class("java.lang.String").await?;
let object_class = thread.class("java.lang.Object").await?;
assert!(
object_class
.is_assignable_from(&thread, &string_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_string_not_is_assignable_from_object() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let string_class = thread.class("java.lang.String").await?;
let object_class = thread.class("java.lang.Object").await?;
assert!(
!string_class
.is_assignable_from(&thread, &object_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_string_not_is_assignable_from_serializable() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let string_class = thread.class("java.lang.String").await?;
let serializable_class = thread.class("java.io.Serializable").await?;
assert!(
!string_class
.is_assignable_from(&thread, &serializable_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_is_assignable_from_serializable_class() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_class = thread.class("java.lang.Object").await?;
let serializable_class = thread.class("java.io.Serializable").await?;
assert!(
object_class
.is_assignable_from(&thread, &serializable_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_array_not_is_assignable_from_object() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
let object_class = thread.class("java.lang.Object").await?;
assert!(
!object_array_class
.is_assignable_from(&thread, &object_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_is_assignable_from_int_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_class = thread.class("java.lang.Object").await?;
let int_array_class = thread.class("[I").await?;
assert!(
object_class
.is_assignable_from(&thread, &int_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_int_array_is_assignable_from_int_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let int_array_class = thread.class("[I").await?;
assert!(
int_array_class
.is_assignable_from(&thread, &int_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_is_assignable_from_object_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_class = thread.class("java.lang.Object").await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
assert!(
object_class
.is_assignable_from(&thread, &object_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_array_is_assignable_from_object_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
assert!(
object_array_class
.is_assignable_from(&thread, &object_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_array_not_is_assignable_from_int_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
let int_array_class = thread.class("[I").await?;
assert!(
!object_array_class
.is_assignable_from(&thread, &int_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_int_array_not_is_assignable_from_object_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let int_array_class = thread.class("[I").await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
assert!(
!int_array_class
.is_assignable_from(&thread, &object_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_array_is_assignable_from_string_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
let string_array_class = thread.class("[Ljava/lang/String;").await?;
assert!(
object_array_class
.is_assignable_from(&thread, &string_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_string_array_not_is_assignable_from_object_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let string_array_class = thread.class("[Ljava/lang/String;").await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
assert!(
!string_array_class
.is_assignable_from(&thread, &object_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_object_array_is_assignable_from_multiple_dimension_object_array() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
let two_dimension_object_array_class = thread.class("[[Ljava/lang/Object;").await?;
assert!(
object_array_class
.is_assignable_from(&thread, &two_dimension_object_array_class)
.await?
);
Ok(())
}
#[tokio::test]
async fn test_multiple_dimension_object_array_not_is_assignable_from_object_array() -> Result<()>
{
let (_vm, thread) = crate::test::thread().await?;
let two_dimension_object_array_class = thread.class("[[Ljava/lang/Object;").await?;
let object_array_class = thread.class("[Ljava/lang/Object;").await?;
assert!(
!two_dimension_object_array_class
.is_assignable_from(&thread, &object_array_class)
.await?
);
Ok(())
}
}