fix(jni): 全部 18 个 JNI 导出函数加 catch_unwind 防护
问题:vnt-jni 的 extern "system" 导出函数没有任何 panic 防护, core 内任何 panic(unwrap/索引越界/断言等)跨 FFI unwind 会直接 abort 宿主 JVM。 修复:新增 catch_jni_panic(catch_unwind + AssertUnwindSafe)与 jni_guard! 宏,包裹全部 18 个导出函数:panic 时提取 payload 消息、 向 JVM 抛 RuntimeException 并返回该类型的默认值(jboolean/jint 0、 jlong -1、指针 null_mut、void 直接返回)。业务逻辑与签名不变。 说明:排查中发现报告举例的 core/mod.rs:140 expect 在当前代码下 实际不可达(no_tun=true 时 internal_nat_inbound 必为 Some), 但 FFI panic 防护本身仍然必要。 测试:3 个 catch_jni_panic 单测(正常值透传、&str/String payload 消息提取);cargo clippy --workspace --all-targets -- -D warnings 通过。
This commit is contained in:
+98
-3
@@ -1,7 +1,7 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use jni::JNIEnv;
|
||||||
use jni::objects::{JClass, JObject, JString};
|
use jni::objects::{JClass, JObject, JString};
|
||||||
use jni::sys::{jboolean, jint, jlong, jstring};
|
use jni::sys::{jboolean, jint, jlong, jstring};
|
||||||
use jni::JNIEnv;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
@@ -49,12 +49,47 @@ lazy_static::lazy_static! {
|
|||||||
static ref GLOBAL_STATE: Mutex<Option<GlobalState>> = Mutex::new(None);
|
static ref GLOBAL_STATE: Mutex<Option<GlobalState>> = Mutex::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 从 panic payload 中提取错误消息
|
||||||
|
fn panic_message(e: Box<dyn std::any::Any + Send>) -> String {
|
||||||
|
if let Some(s) = e.downcast_ref::<&str>() {
|
||||||
|
s.to_string()
|
||||||
|
} else if let Some(s) = e.downcast_ref::<String>() {
|
||||||
|
s.clone()
|
||||||
|
} else {
|
||||||
|
"unknown panic".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 捕获闭包内的 panic,转为 Err(消息),防止 panic 跨 FFI unwind 导致宿主 abort
|
||||||
|
fn catch_jni_panic<F, T>(f: F) -> Result<T, String>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(panic_message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JNI 导出函数的 panic 防护:panic 时向 JVM 抛出异常并返回默认值
|
||||||
|
macro_rules! jni_guard {
|
||||||
|
($env:ident, $default_ret:expr, { $($body:tt)* }) => {{
|
||||||
|
match catch_jni_panic(|| {
|
||||||
|
$($body)*
|
||||||
|
}) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(msg) => {
|
||||||
|
let _ = $env.throw(format!("VNT native panic: {}", msg));
|
||||||
|
$default_ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
/// 初始化JNI模块
|
/// 初始化JNI模块
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "system" fn Java_com_vnt_VntManager_nativeInit(
|
pub extern "system" fn Java_com_vnt_VntManager_nativeInit(
|
||||||
mut env: JNIEnv,
|
mut env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
|
jni_guard!(env, 0, {
|
||||||
let mut state = GLOBAL_STATE.lock();
|
let mut state = GLOBAL_STATE.lock();
|
||||||
if state.is_some() {
|
if state.is_some() {
|
||||||
return 1; // 已经初始化
|
return 1; // 已经初始化
|
||||||
@@ -70,13 +105,16 @@ pub extern "system" fn Java_com_vnt_VntManager_nativeInit(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 销毁JNI模块
|
/// 销毁JNI模块
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "system" fn Java_com_vnt_VntManager_nativeDestroy(_env: JNIEnv, _class: JClass) {
|
pub extern "system" fn Java_com_vnt_VntManager_nativeDestroy(_env: JNIEnv, _class: JClass) {
|
||||||
|
let _ = catch_jni_panic(|| {
|
||||||
let mut state = GLOBAL_STATE.lock();
|
let mut state = GLOBAL_STATE.lock();
|
||||||
*state = None;
|
*state = None;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 创建网络实例
|
/// 创建网络实例
|
||||||
@@ -86,6 +124,7 @@ pub extern "system" fn Java_com_vnt_VntManager_nativeCreateNetwork<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
config_json: JString<'local>,
|
config_json: JString<'local>,
|
||||||
) -> jlong {
|
) -> jlong {
|
||||||
|
jni_guard!(env, -1, {
|
||||||
let result: anyhow::Result<i64> = (|| {
|
let result: anyhow::Result<i64> = (|| {
|
||||||
let mut global_state = GLOBAL_STATE.lock();
|
let mut global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_mut().context("VNT not initialized")?;
|
let state = global_state.as_mut().context("VNT not initialized")?;
|
||||||
@@ -129,6 +168,7 @@ pub extern "system" fn Java_com_vnt_VntManager_nativeCreateNetwork<'local>(
|
|||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 注册网络
|
/// 注册网络
|
||||||
@@ -138,6 +178,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeRegister<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
handle: jlong,
|
handle: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let (network_manager_arc, runtime) = {
|
let (network_manager_arc, runtime) = {
|
||||||
let mut global_state = GLOBAL_STATE.lock();
|
let mut global_state = GLOBAL_STATE.lock();
|
||||||
@@ -193,6 +234,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeRegister<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 启动TUN设备(Android使用,需要传入fd)
|
/// 启动TUN设备(Android使用,需要传入fd)
|
||||||
@@ -203,6 +245,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStartTun(
|
|||||||
handle: jlong,
|
handle: jlong,
|
||||||
tun_fd: jint,
|
tun_fd: jint,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
|
jni_guard!(env, 0, {
|
||||||
let result: anyhow::Result<()> = (|| {
|
let result: anyhow::Result<()> = (|| {
|
||||||
let (network_manager_arc, runtime) = {
|
let (network_manager_arc, runtime) = {
|
||||||
let mut global_state = GLOBAL_STATE.lock();
|
let mut global_state = GLOBAL_STATE.lock();
|
||||||
@@ -245,6 +288,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStartTun(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 设置网络IP(非Android系统)
|
/// 设置网络IP(非Android系统)
|
||||||
@@ -256,6 +300,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>(
|
|||||||
ip: JString<'local>,
|
ip: JString<'local>,
|
||||||
prefix_len: jint,
|
prefix_len: jint,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
|
jni_guard!(env, 0, {
|
||||||
let result: anyhow::Result<()> = (|| {
|
let result: anyhow::Result<()> = (|| {
|
||||||
let (network_manager_arc, runtime) = {
|
let (network_manager_arc, runtime) = {
|
||||||
let mut global_state = GLOBAL_STATE.lock();
|
let mut global_state = GLOBAL_STATE.lock();
|
||||||
@@ -281,8 +326,9 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>(
|
|||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
{
|
{
|
||||||
runtime
|
runtime.block_on(async {
|
||||||
.block_on(async { manager.set_tun_network_ip(ip_addr, prefix_len as u8).await })?;
|
manager.set_tun_network_ip(ip_addr, prefix_len as u8).await
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
@@ -301,6 +347,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取VntApi实例
|
/// 获取VntApi实例
|
||||||
@@ -310,6 +357,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeGetApi(
|
|||||||
_class: JClass,
|
_class: JClass,
|
||||||
handle: jlong,
|
handle: jlong,
|
||||||
) -> jlong {
|
) -> jlong {
|
||||||
|
jni_guard!(env, -1, {
|
||||||
let result: anyhow::Result<i64> = (|| {
|
let result: anyhow::Result<i64> = (|| {
|
||||||
let mut global_state = GLOBAL_STATE.lock();
|
let mut global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_mut().context("VNT not initialized")?;
|
let state = global_state.as_mut().context("VNT not initialized")?;
|
||||||
@@ -339,6 +387,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeGetApi(
|
|||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查是否为无TUN模式
|
/// 检查是否为无TUN模式
|
||||||
@@ -348,6 +397,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeIsNoTun(
|
|||||||
_class: JClass,
|
_class: JClass,
|
||||||
handle: jlong,
|
handle: jlong,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
|
jni_guard!(env, 0, {
|
||||||
let result: anyhow::Result<bool> = (|| {
|
let result: anyhow::Result<bool> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -379,6 +429,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeIsNoTun(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 关闭网络
|
/// 关闭网络
|
||||||
@@ -388,6 +439,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStop(
|
|||||||
_class: JClass,
|
_class: JClass,
|
||||||
handle: jlong,
|
handle: jlong,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
|
jni_guard!(env, 0, {
|
||||||
let result: anyhow::Result<()> = (|| {
|
let result: anyhow::Result<()> = (|| {
|
||||||
let mut global_state = GLOBAL_STATE.lock();
|
let mut global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_mut().context("VNT not initialized")?;
|
let state = global_state.as_mut().context("VNT not initialized")?;
|
||||||
@@ -414,6 +466,7 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStop(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== VntApi 接口 ====================
|
// ==================== VntApi 接口 ====================
|
||||||
@@ -425,6 +478,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetClientList<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -457,6 +511,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetClientList<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取网络配置信息
|
/// 获取网络配置信息
|
||||||
@@ -466,6 +521,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNetwork<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -498,6 +554,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNetwork<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取NAT信息
|
/// 获取NAT信息
|
||||||
@@ -507,6 +564,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNatInfo<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -538,6 +596,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNatInfo<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取服务器节点列表
|
/// 获取服务器节点列表
|
||||||
@@ -547,6 +606,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetServerList<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -583,6 +643,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetServerList<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取路由表
|
/// 获取路由表
|
||||||
@@ -592,6 +653,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetRouteTable<'local>(
|
|||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -636,6 +698,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetRouteTable<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查目标IP是否直连
|
/// 检查目标IP是否直连
|
||||||
@@ -646,6 +709,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeIsDirect<'local>(
|
|||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
ip: JString<'local>,
|
ip: JString<'local>,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
|
jni_guard!(env, 0, {
|
||||||
let result: anyhow::Result<bool> = (|| {
|
let result: anyhow::Result<bool> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -674,6 +738,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeIsDirect<'local>(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取对端NAT信息
|
/// 获取对端NAT信息
|
||||||
@@ -684,6 +749,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPeerNatInfo<'local>(
|
|||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
ip: JString<'local>,
|
ip: JString<'local>,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -718,6 +784,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPeerNatInfo<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取丢包信息
|
/// 获取丢包信息
|
||||||
@@ -728,6 +795,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPacketLoss<'local>(
|
|||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
ip: JString<'local>,
|
ip: JString<'local>,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -763,6 +831,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPacketLoss<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取流量信息
|
/// 获取流量信息
|
||||||
@@ -773,6 +842,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetTrafficInfo<'local>(
|
|||||||
api_handle: jlong,
|
api_handle: jlong,
|
||||||
ip: JString<'local>,
|
ip: JString<'local>,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
jni_guard!(env, std::ptr::null_mut(), {
|
||||||
let result: anyhow::Result<String> = (|| {
|
let result: anyhow::Result<String> = (|| {
|
||||||
let global_state = GLOBAL_STATE.lock();
|
let global_state = GLOBAL_STATE.lock();
|
||||||
let state = global_state.as_ref().context("VNT not initialized")?;
|
let state = global_state.as_ref().context("VNT not initialized")?;
|
||||||
@@ -807,6 +877,7 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetTrafficInfo<'local>(
|
|||||||
JObject::null().into_raw()
|
JObject::null().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 辅助函数 ====================
|
// ==================== 辅助函数 ====================
|
||||||
@@ -938,3 +1009,27 @@ fn parse_config_from_json(json_str: &str) -> anyhow::Result<Config> {
|
|||||||
tunnel_port: cfg.tunnel_port,
|
tunnel_port: cfg.tunnel_port,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn catch_jni_panic_returns_value_unchanged() {
|
||||||
|
let result = catch_jni_panic(|| 42);
|
||||||
|
assert_eq!(result, Ok(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn catch_jni_panic_captures_str_message() {
|
||||||
|
let result: Result<(), String> = catch_jni_panic(|| panic!("boom"));
|
||||||
|
let err = result.unwrap_err();
|
||||||
|
assert!(err.contains("boom"), "unexpected message: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn catch_jni_panic_captures_string_message() {
|
||||||
|
let result: Result<(), String> = catch_jni_panic(|| panic!("{}", "kaboom"));
|
||||||
|
assert_eq!(result.unwrap_err(), "kaboom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user