Skip to content

Commit 9059b67

Browse files
committed
Implement checkcast
1 parent bf0937f commit 9059b67

File tree

8 files changed

+86
-9
lines changed

8 files changed

+86
-9
lines changed

java_runtime/src/classes/java/lang.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod arithmetic_exception;
22
mod array_index_out_of_bounds_exception;
33
mod class;
4+
mod class_cast_exception;
45
mod class_loader;
56
mod clone_not_supported_exception;
67
mod cloneable;
@@ -33,12 +34,12 @@ mod unsupported_operation_exception;
3334

3435
pub use self::{
3536
arithmetic_exception::ArithmeticException, array_index_out_of_bounds_exception::ArrayIndexOutOfBoundsException, class::Class,
36-
class_loader::ClassLoader, clone_not_supported_exception::CloneNotSupportedException, cloneable::Cloneable, comparable::Comparable, error::Error,
37-
exception::Exception, illegal_argument_exception::IllegalArgumentException, incompatible_class_change_error::IncompatibleClassChangeError,
38-
index_out_of_bounds_exception::IndexOutOfBoundsException, instantiation_error::InstantiationError, integer::Integer,
39-
interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math, no_class_def_found_error::NoClassDefFoundError,
40-
no_such_field_error::NoSuchFieldError, no_such_method_error::NoSuchMethodError, null_pointer_exception::NullPointerException, object::Object,
41-
runnable::Runnable, runtime::Runtime, runtime_exception::RuntimeException, security_exception::SecurityException, string::String,
42-
string_buffer::StringBuffer, system::System, thread::Thread, throwable::Throwable,
37+
class_cast_exception::ClassCastException, class_loader::ClassLoader, clone_not_supported_exception::CloneNotSupportedException,
38+
cloneable::Cloneable, comparable::Comparable, error::Error, exception::Exception, illegal_argument_exception::IllegalArgumentException,
39+
incompatible_class_change_error::IncompatibleClassChangeError, index_out_of_bounds_exception::IndexOutOfBoundsException,
40+
instantiation_error::InstantiationError, integer::Integer, interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math,
41+
no_class_def_found_error::NoClassDefFoundError, no_such_field_error::NoSuchFieldError, no_such_method_error::NoSuchMethodError,
42+
null_pointer_exception::NullPointerException, object::Object, runnable::Runnable, runtime::Runtime, runtime_exception::RuntimeException,
43+
security_exception::SecurityException, string::String, string_buffer::StringBuffer, system::System, thread::Thread, throwable::Throwable,
4344
unsupported_operation_exception::UnsupportedOperationException,
4445
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use alloc::vec;
2+
3+
use java_class_proto::JavaMethodProto;
4+
use jvm::{ClassInstanceRef, Jvm, Result};
5+
6+
use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String};
7+
8+
// class java.lang.ClassCastException
9+
pub struct ClassCastException;
10+
11+
impl ClassCastException {
12+
pub fn as_proto() -> RuntimeClassProto {
13+
RuntimeClassProto {
14+
name: "java/lang/ClassCastException",
15+
parent_class: Some("java/lang/Exception"),
16+
interfaces: vec![],
17+
methods: vec![
18+
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
19+
JavaMethodProto::new("<init>", "(Ljava/lang/String;)V", Self::init_with_message, Default::default()),
20+
],
21+
fields: vec![],
22+
access_flags: Default::default(),
23+
}
24+
}
25+
26+
async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
27+
tracing::debug!("java.lang.ClassCastException::<init>({:?})", &this);
28+
29+
let _: () = jvm.invoke_special(&this, "java/lang/Exception", "<init>", "()V", ()).await?;
30+
31+
Ok(())
32+
}
33+
34+
async fn init_with_message(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, message: ClassInstanceRef<String>) -> Result<()> {
35+
tracing::debug!("java.lang.ClassCastException::<init>({:?}, {:?})", &this, &message);
36+
37+
let _: () = jvm
38+
.invoke_special(&this, "java/lang/Exception", "<init>", "(Ljava/lang/String;)V", (message,))
39+
.await?;
40+
41+
Ok(())
42+
}
43+
}

java_runtime/src/loader.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub fn get_runtime_class_proto(name: &str) -> Option<RuntimeClassProto> {
3434
crate::classes::java::io::Writer::as_proto(),
3535
crate::classes::java::lang::ArrayIndexOutOfBoundsException::as_proto(),
3636
crate::classes::java::lang::Class::as_proto(),
37+
crate::classes::java::lang::ClassCastException::as_proto(),
3738
crate::classes::java::lang::ClassLoader::as_proto(),
3839
crate::classes::java::lang::Cloneable::as_proto(),
3940
crate::classes::java::lang::CloneNotSupportedException::as_proto(),

jvm/src/value.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ impl From<JavaValue> for f64 {
9090
}
9191
}
9292

93+
impl<'a> From<&'a JavaValue> for &'a Option<Box<dyn ClassInstance>> {
94+
fn from(x: &'a JavaValue) -> Self {
95+
match x {
96+
JavaValue::Object(x) => x,
97+
_ => panic!("Expected object, got {:?}", x),
98+
}
99+
}
100+
}
101+
93102
impl From<JavaValue> for Option<Box<dyn ClassInstance>> {
94103
fn from(x: JavaValue) -> Self {
95104
match x {

jvm_rust/src/interpreter.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,11 @@ impl Interpreter {
153153
}
154154
Opcode::Bipush(x) => stack_frame.operand_stack.push(JavaValue::Int(*x as i32)),
155155
Opcode::Checkcast(x) => {
156-
let top_stack = stack_frame.operand_stack.last().unwrap();
156+
let top_stack: &Option<Box<dyn ClassInstance>> = stack_frame.operand_stack.last().unwrap().into();
157157

158-
tracing::warn!("Unimplemented checkcast: {:?} {:?}", top_stack, x);
158+
if !top_stack.is_none() && !jvm.is_instance(&**top_stack.as_ref().unwrap(), x.as_class()) {
159+
return Err(jvm.exception("java/lang/ClassCastException", "Invalid cast").await);
160+
}
159161
}
160162
Opcode::D2f => {
161163
let value: f64 = stack_frame.operand_stack.pop().unwrap().into();

test_data/CheckCast.class

807 Bytes
Binary file not shown.

test_data/CheckCast.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Successfully cast to String
2+
Failed to cast to Integer

test_data/src/CheckCast.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
public class CheckCast {
2+
public static void main(String[] args) {
3+
Object obj = "This is a string";
4+
5+
try {
6+
String str = (String) obj; // This should succeed
7+
System.out.println("Successfully cast to String");
8+
} catch (ClassCastException e) {
9+
System.out.println("Failed to cast to String");
10+
}
11+
12+
try {
13+
Integer num = (Integer) obj; // This should fail
14+
System.out.println("Successfully cast to Integer");
15+
} catch (ClassCastException e) {
16+
System.out.println("Failed to cast to Integer");
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)