Skip to content

Commit f59be13

Browse files
committed
jni: implement Throw and ThrowNew
1 parent c6c335c commit f59be13

File tree

3 files changed

+135
-6
lines changed

3 files changed

+135
-6
lines changed

jni/src/env/exceptions.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,54 @@
1+
use crate::error::{JniError, Result};
2+
use crate::objects::{JClass, JThrowable};
3+
use crate::string::JniString;
4+
15
impl super::JniEnv {
2-
// TODO: Throw
3-
// TODO: ThrowNew
6+
/// Causes `exception` to be thrown
7+
///
8+
/// # Errors
9+
///
10+
/// If the VM returns an error, then the exception was likely not thrown.
11+
pub fn throw(&self, exception: JThrowable) -> Result<()> {
12+
let ret;
13+
unsafe {
14+
let invoke_interface = self.as_native_interface();
15+
ret = ((*invoke_interface).Throw)(self.0.cast::<jni_sys::JNIEnv>(), exception.raw());
16+
}
17+
18+
if ret != 0 {
19+
return Err(JniError::Unknown);
20+
}
21+
22+
Ok(())
23+
}
24+
25+
/// Constructs an object of type `class` with the `message` specified, and causes that exception to be thrown.
26+
///
27+
/// ## Parameters
28+
///
29+
/// `class`: a subclass of `java.lang.Throwable`
30+
/// `message`: the message used to construct the java.lang.Throwable object
31+
///
32+
/// ## Errors
33+
///
34+
/// If the VM returns an error, then the exception was likely not thrown.
35+
pub fn throw_new(&self, class: JClass, message: Option<JniString>) -> Result<()> {
36+
let ret;
37+
unsafe {
38+
let invoke_interface = self.as_native_interface();
39+
ret = ((*invoke_interface).ThrowNew)(
40+
self.0.cast::<jni_sys::JNIEnv>(),
41+
class.raw(),
42+
message.map_or_else(core::ptr::null, |message| message.as_cstr().as_ptr()),
43+
);
44+
}
45+
46+
if ret != 0 {
47+
return Err(JniError::Unknown);
48+
}
49+
50+
Ok(())
51+
}
452
// TODO: ExceptionOccurred
553

654
/// Prints an exception and a backtrace of the stack to a system error-reporting channel, such as stderr.

runtime/src/native/java/lang/String.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::objects::instance::class::ClassInstanceRef;
33
use crate::objects::reference::Reference;
44
use crate::symbols::Symbol;
55

6+
use std::borrow::Cow;
67
use std::collections::HashMap;
78
use std::hash::Hash;
89
use std::sync::{LazyLock, RwLock};
@@ -94,6 +95,11 @@ impl<'a> StringHashDerivable<&'a str> for &'a str {
9495
<&[u1] as StringHashDerivable<&[u1]>>::string_hash(&value.as_bytes())
9596
}
9697
}
98+
impl<'a> StringHashDerivable<Cow<'a, str>> for Cow<'a, str> {
99+
fn string_hash(value: &Cow<'a, str>) -> StringHash {
100+
<&str as StringHashDerivable<&str>>::string_hash(&&**value)
101+
}
102+
}
97103
impl<'a> StringHashDerivable<&'a [u1]> for &'a [u1] {
98104
fn string_hash(value: &Self) -> StringHash {
99105
let mut h = 0;

runtime/src/native/jni/exceptions.rs

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,93 @@
1-
use crate::classes;
1+
use crate::native::java;
2+
use crate::native::java::lang::String::StringInterner;
3+
use crate::native::jni::reference_from_jobject;
4+
use crate::objects::method::Method;
5+
use crate::objects::reference::Reference;
6+
use crate::symbols::sym;
27
use crate::thread::JavaThread;
8+
use crate::thread::exceptions::Throws;
9+
use crate::{classes, java_call};
310

411
use core::ffi::c_char;
12+
use std::{ptr, slice};
513

6-
use jni::sys::{JNIEnv, jboolean, jclass, jint, jthrowable};
14+
use ::jni::sys::{JNI_ERR, JNI_OK, JNIEnv, jboolean, jclass, jint, jthrowable};
15+
use common::unicode;
16+
use instructions::Operand;
17+
use libc::strlen;
718

819
#[unsafe(no_mangle)]
920
pub extern "system" fn Throw(env: *mut JNIEnv, obj: jthrowable) -> jint {
10-
unimplemented!("jni::Throw");
21+
let thread = JavaThread::current();
22+
assert_eq!(thread.env().raw(), env);
23+
24+
let Some(throwable) = (unsafe { reference_from_jobject(obj) }) else {
25+
return JNI_ERR;
26+
};
27+
28+
if !throwable.is_instance_of(crate::globals::classes::java_lang_Throwable()) {
29+
return JNI_ERR;
30+
}
31+
32+
thread.set_pending_exception(throwable);
33+
34+
JNI_OK
1135
}
1236

1337
#[unsafe(no_mangle)]
1438
pub extern "system" fn ThrowNew(env: *mut JNIEnv, clazz: jclass, msg: *const c_char) -> jint {
15-
unimplemented!("jni::ThrowNew");
39+
let thread = JavaThread::current();
40+
assert_eq!(thread.env().raw(), env);
41+
42+
let Some(mirror) = (unsafe { reference_from_jobject(clazz) }) else {
43+
return JNI_ERR;
44+
};
45+
46+
let mut message = None;
47+
if !msg.is_null() {
48+
// SAFETY: It's entirely up to the caller to pass in a valid string
49+
let len = unsafe { strlen(msg) };
50+
51+
// SAFETY: c_char is always 8 bits
52+
let utf = unsafe { slice::from_raw_parts(msg.cast::<u8>(), len) };
53+
54+
if let Ok(utf_8) = unicode::decode(utf) {
55+
message = Some(utf_8);
56+
};
57+
}
58+
59+
let throwable_class = mirror.extract_target_class();
60+
61+
let constructor;
62+
if message.is_none() {
63+
constructor = throwable_class
64+
.resolve_method(sym!(object_initializer_name), sym!(void_method_signature));
65+
} else {
66+
constructor = throwable_class
67+
.resolve_method(sym!(object_initializer_name), sym!(String_void_signature));
68+
}
69+
70+
match constructor {
71+
Throws::Ok(constructor) => match message {
72+
Some(message) => {
73+
let string = StringInterner::intern(message);
74+
java_call!(
75+
thread,
76+
constructor,
77+
Operand::Reference(Reference::class(string))
78+
);
79+
},
80+
None => {
81+
java_call!(thread, constructor);
82+
},
83+
},
84+
Throws::Exception(e) => {
85+
e.throw(thread);
86+
return JNI_ERR;
87+
},
88+
}
89+
90+
JNI_OK
1691
}
1792

1893
#[unsafe(no_mangle)]

0 commit comments

Comments
 (0)