// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . use std::{ffi::c_void, mem, ptr, sync::Arc}; use parity_config_from_cli; use parity_destroy; use parity_rpc_worker; use parity_set_logger; use parity_start; use parity_unsubscribe_ws; use parity_ws_worker; use Callback; use ParityParams; use jni::{ objects::{GlobalRef, JClass, JObject, JString, JValue}, sys::{jlong, jobjectArray, va_list}, JNIEnv, JavaVM, }; use parity_ethereum::RunningClient; type CheckedQuery<'a> = (&'a RunningClient, String, JavaVM, GlobalRef); // Creates a Java callback to a static method named `void callback(Object)` struct JavaCallback<'a> { jvm: JavaVM, callback: GlobalRef, method_name: &'a str, method_descriptor: &'a str, } impl<'a> JavaCallback<'a> { fn new(jvm: JavaVM, callback: GlobalRef) -> Self { Self { jvm, callback, method_name: "callback", method_descriptor: "(Ljava/lang/Object;)V", } } } impl<'a> Callback for JavaCallback<'a> { fn call(&self, msg: &str) { let env = self .jvm .attach_current_thread() .expect("JavaVM should have an environment; qed"); let java_str = env .new_string(msg.to_string()) .expect("Rust String is valid JString; qed"); let val = &[JValue::Object(JObject::from(java_str))]; env.call_method( self.callback.as_obj(), self.method_name, self.method_descriptor, val, ) .expect( "The callback must be an instance method and be named \"void callback(Object)\"; qed)", ); } } #[no_mangle] pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_configFromCli( env: JNIEnv, _: JClass, cli: jobjectArray, ) -> jlong { let cli_len = env.get_array_length(cli).expect("invalid Java bindings") as usize; let mut jni_strings = Vec::with_capacity(cli_len); let mut opts = Vec::with_capacity(cli_len); let mut opts_lens = Vec::with_capacity(cli_len); for n in 0..cli_len as i32 { let elem = env .get_object_array_element(cli, n) .expect("invalid Java bindings"); let elem_str: JString = elem.into(); match env.get_string(elem_str) { Ok(s) => { opts.push(s.as_ptr()); opts_lens.push(s.to_bytes().len()); jni_strings.push(s); } Err(err) => { let _ = env.throw_new("java/lang/Exception", err.to_string()); return 0; } }; } let mut out = ptr::null_mut(); match parity_config_from_cli(opts.as_ptr(), opts_lens.as_ptr(), cli_len, &mut out) { 0 => out as jlong, _ => { let _ = env.throw_new("java/lang/Exception", "failed to create config object"); 0 } } } #[no_mangle] pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_build( env: JNIEnv, _: JClass, config: va_list, logger_mode: JString, logger_file: JString, ) -> jlong { let mut params = ParityParams { configuration: config, ..mem::zeroed() }; let logger_mode: String = env .get_string(logger_mode) .expect("valid JString; qed") .into(); let logger_file: String = env .get_string(logger_file) .expect("valid JString; qed") .into(); parity_set_logger( logger_mode.as_ptr(), logger_mode.as_bytes().len(), logger_file.as_ptr(), logger_file.as_bytes().len(), &mut params.logger, ); let mut out = ptr::null_mut(); match parity_start(¶ms, &mut out) { 0 => out as jlong, _ => { let _ = env.throw_new("java/lang/Exception", "failed to start Parity"); 0 } } } #[no_mangle] pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_destroy( _env: JNIEnv, _: JClass, parity: va_list, ) { parity_destroy(parity); } unsafe fn java_query_checker<'a>( client: va_list, rpc: JString, callback: JObject, env: &JNIEnv<'a>, ) -> Result, String> { let query: String = env .get_string(rpc) .map(Into::into) .map_err(|e| e.to_string())?; let client: &RunningClient = &*(client as *const RunningClient); let jvm = env.get_java_vm().map_err(|e| e.to_string())?; let global_ref = env.new_global_ref(callback).map_err(|e| e.to_string())?; Ok((client, query, jvm, global_ref)) } #[no_mangle] pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_rpcQueryNative( env: JNIEnv, _: JClass, parity: va_list, rpc: JString, timeout_ms: jlong, callback: JObject, ) { let _ = java_query_checker(parity, rpc, callback, &env) .map(|(client, query, jvm, global_ref)| { let callback = Arc::new(JavaCallback::new(jvm, global_ref)); parity_rpc_worker(client, &query, callback, timeout_ms as u64); }) .map_err(|e| { let _ = env.throw_new("java/lang/Exception", e); }); } #[no_mangle] pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_subscribeWebSocketNative( env: JNIEnv, _: JClass, parity: va_list, rpc: JString, callback: JObject, ) -> va_list { java_query_checker(parity, rpc, callback, &env) .map(move |(client, query, jvm, global_ref)| { let callback = Arc::new(JavaCallback::new(jvm, global_ref)); parity_ws_worker(client, &query, callback) as va_list }) .unwrap_or_else(|e| { let _ = env.throw_new("java/lang/Exception", e); ptr::null_mut() }) } #[no_mangle] pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_unsubscribeWebSocketNative( _: JNIEnv, _: JClass, session: va_list, ) { parity_unsubscribe_ws(session as *const c_void); }