Fixes for FFI, refactor FFI Error to be easier to implement
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2023-06-25 16:28:51 +02:00
parent 8d49e44e94
commit 0163c7a105
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
12 changed files with 71 additions and 65 deletions

View File

@ -3,7 +3,7 @@ use crate::dynamic_data::{
}; };
use crate::ffi::dynamic_data::models::native_event_hook::NativeEventHook; use crate::ffi::dynamic_data::models::native_event_hook::NativeEventHook;
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult; use crate::ffi::{FFIResult, OwnedPtrString};
use anyhow::anyhow; use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString}; use std::ffi::{c_char, CStr, CString};
use std::ops::Deref; use std::ops::Deref;
@ -204,13 +204,13 @@ extern "C" fn battle_set_weather(ptr: FFIHandle<Battle>, weather: *const c_char)
/// Gets the current weather of the battle. If no weather is present, this returns nullptr. /// Gets the current weather of the battle. If no weather is present, this returns nullptr.
#[no_mangle] #[no_mangle]
extern "C" fn battle_weather_name(ptr: FFIHandle<Battle>) -> FFIResult<*mut c_char> { extern "C" fn battle_weather_name(ptr: FFIHandle<Battle>) -> FFIResult<OwnedPtrString> {
match ptr.from_ffi_handle().weather_name() { match ptr.from_ffi_handle().weather_name() {
Ok(Some(w)) => match CString::new(w.str()) { Ok(Some(w)) => match CString::new(w.str()) {
Ok(s) => s.into_raw().into(), Ok(s) => OwnedPtrString(s.into_raw()).into(),
Err(e) => FFIResult::err(anyhow!("Failed to convert weather name to CString: {}", e)), Err(e) => FFIResult::err(anyhow!("Failed to convert weather name to CString: {}", e)),
}, },
Ok(None) => std::ptr::null_mut::<c_char>().into(), Ok(None) => OwnedPtrString(std::ptr::null_mut()).into(),
Err(e) => FFIResult::err(e), Err(e) => FFIResult::err(e),
} }
} }

View File

@ -1,7 +1,7 @@
use crate::defines::LevelInt; use crate::defines::LevelInt;
use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, LearnedMove, MoveLearnMethod, Pokemon}; use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, LearnedMove, MoveLearnMethod, Pokemon};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult; use crate::ffi::{FFIResult, OwnedPtrString};
use crate::static_data::{ use crate::static_data::{
Ability, AbilityIndex, Form, Gender, Item, Nature, Species, Statistic, StatisticSet, TypeIdentifier, Ability, AbilityIndex, Form, Gender, Item, Nature, Species, Statistic, StatisticSet, TypeIdentifier,
}; };
@ -180,16 +180,16 @@ extern "C" fn pokemon_height(handle: FFIHandle<Pokemon>) -> f32 {
/// An optional nickname of the Pokemon. /// An optional nickname of the Pokemon.
#[no_mangle] #[no_mangle]
extern "C" fn pokemon_nickname(handle: FFIHandle<Pokemon>) -> FFIResult<*mut c_char> { extern "C" fn pokemon_nickname(handle: FFIHandle<Pokemon>) -> FFIResult<OwnedPtrString> {
let form = handle.from_ffi_handle(); let form = handle.from_ffi_handle();
let name = form.nickname(); let name = form.nickname();
if let Some(v) = name { if let Some(v) = name {
match CString::new(v.as_str()) { match CString::new(v.as_str()) {
Ok(v) => FFIResult::ok(v.into_raw()), Ok(v) => FFIResult::ok(OwnedPtrString(v.into_raw())),
Err(err) => FFIResult::err(anyhow!("Could not convert nickname to CString: {}", err)), Err(err) => FFIResult::err(anyhow!("Could not convert nickname to CString: {}", err)),
} }
} else { } else {
FFIResult::ok(std::ptr::null_mut()) FFIResult::ok(OwnedPtrString(std::ptr::null_mut()))
} }
} }

View File

@ -45,6 +45,15 @@ impl<T> Clone for FFIHandle<T> {
impl<T> Copy for FFIHandle<T> {} impl<T> Copy for FFIHandle<T> {}
impl<T> Default for FFIHandle<T> {
fn default() -> Self {
Self {
handle: 0,
_marker: std::marker::PhantomData,
}
}
}
/// An FFIObject we can fetch from the FFIHandle. We store this in a hashmap to be able to fetch /// An FFIObject we can fetch from the FFIHandle. We store this in a hashmap to be able to fetch
/// the object from the handle, and to be able to drop the object when the FFI is done with the handle /// the object from the handle, and to be able to drop the object when the FFI is done with the handle
#[derive(Clone)] #[derive(Clone)]
@ -98,7 +107,7 @@ unsafe impl Send for FFIObject {}
unsafe impl Sync for FFIObject {} unsafe impl Sync for FFIObject {}
/// The next handle to be used. /// The next handle to be used.
static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(0); static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1);
/// A lookup to get an actual object from a handle. /// A lookup to get an actual object from a handle.
static FFI_OBJECTS: LazyLock<RwLock<HashMap<usize, FFIObject>>> = LazyLock::new(|| RwLock::new(HashMap::new())); static FFI_OBJECTS: LazyLock<RwLock<HashMap<usize, FFIObject>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
/// A lookup to get a handle from an object. /// A lookup to get a handle from an object.
@ -137,7 +146,7 @@ impl<T> FFIHandle<T> {
FFI_OBJECTS FFI_OBJECTS
.read() .read()
.get(&self.handle) .get(&self.handle)
.ok_or(anyhow!("Unable to get handle")) .ok_or(anyhow!("Unable to get handle {} from FFI_OBJECTS", self.handle))
.unwrap() .unwrap()
.clone() .clone()
} }

View File

@ -8,10 +8,19 @@ mod static_data;
pub(self) use ffi_handle::*; pub(self) use ffi_handle::*;
/// Helper type for clearer functions. /// Helper type for clearer functions.
type OwnedPtrString = *mut c_char; #[repr(C)]
#[derive(Debug, Copy, Clone)]
struct OwnedPtrString(*mut c_char);
/// Helper type for clearer functions. /// Helper type for clearer functions.
type NonOwnedPtrString = *const c_char; type NonOwnedPtrString = *const c_char;
impl Default for OwnedPtrString {
fn default() -> Self {
OwnedPtrString(std::ptr::null_mut())
}
}
/// Generates a basic getter foreign function interface. /// Generates a basic getter foreign function interface.
macro_rules! ffi_handle_arc_dyn_getter { macro_rules! ffi_handle_arc_dyn_getter {
( (
@ -71,7 +80,7 @@ macro_rules! ffi_handle_vec_stringkey_getters {
} }
#[no_mangle] #[no_mangle]
extern "C" fn [< $type:lower _ $func _get>](ptr: FFIHandle<Arc<dyn $type>>, index: usize) -> OwnedPtrString { extern "C" fn [< $type:lower _ $func _get>](ptr: FFIHandle<Arc<dyn $type>>, index: usize) -> OwnedPtrString {
CString::new(ptr.from_ffi_handle().$func().get(index).unwrap().str()).unwrap().into_raw() OwnedPtrString(CString::new(ptr.from_ffi_handle().$func().get(index).unwrap().str()).unwrap().into_raw())
} }
} }
}; };
@ -102,40 +111,30 @@ impl<T: ?Sized> ExternPointer<T> {
} }
} }
/// Helper utility class to give either the data or an error to a FFI.
#[repr(C)]
union ResultUnion<T: Copy> {
/// If the result is ok, this contains the value.
ok: T,
/// If the result is an error, this contains the error message.
err: NonOwnedPtrString,
}
/// The result of a FFI call that can either be an error or a value. /// The result of a FFI call that can either be an error or a value.
#[repr(C)] #[repr(C)]
pub struct FFIResult<T: Copy> { #[repr(packed)]
/// If the result is ok, this is 1, otherwise 0. pub struct FFIResult<T: Copy + Default> {
ok: u8, /// If the result is ok, this is null, otherwise this is a pointer to the error string.
err: NonOwnedPtrString,
/// The value or error. /// The value or error.
value: ResultUnion<T>, value: T,
} }
impl<T: Copy> FFIResult<T> { impl<T: Copy + Default> FFIResult<T> {
/// Creates a new NativeResult with the given value. /// Creates a new NativeResult with the given value.
pub fn ok(value: T) -> Self { pub fn ok(value: T) -> Self {
Self { Self {
ok: 1, err: std::ptr::null(),
value: ResultUnion { ok: value }, value,
} }
} }
/// Creates a new NativeResult with the given error. /// Creates a new NativeResult with the given error.
#[allow(clippy::unwrap_used)] // We know for certain this is not empty. #[allow(clippy::unwrap_used)] // We know for certain this is not empty.
pub fn err(err: anyhow_ext::Error) -> Self { pub fn err(err: anyhow_ext::Error) -> Self {
Self { Self {
ok: 0,
value: ResultUnion {
err: CString::new(err.to_string()).unwrap().into_raw(), err: CString::new(err.to_string()).unwrap().into_raw(),
}, value: Default::default(),
} }
} }
@ -143,15 +142,13 @@ impl<T: Copy> FFIResult<T> {
#[allow(clippy::unwrap_used)] // We know for certain this is not empty. #[allow(clippy::unwrap_used)] // We know for certain this is not empty.
pub fn err_from_str(err: &str) -> Self { pub fn err_from_str(err: &str) -> Self {
Self { Self {
ok: 0,
value: ResultUnion {
err: CString::new(err).unwrap().into_raw(), err: CString::new(err).unwrap().into_raw(),
}, value: Default::default(),
} }
} }
} }
impl<T: Copy> From<anyhow::Result<T>> for FFIResult<T> { impl<T: Copy + Default> From<anyhow::Result<T>> for FFIResult<T> {
fn from(value: anyhow::Result<T>) -> Self { fn from(value: anyhow::Result<T>) -> Self {
match value { match value {
Ok(v) => Self::ok(v), Ok(v) => Self::ok(v),
@ -160,7 +157,7 @@ impl<T: Copy> From<anyhow::Result<T>> for FFIResult<T> {
} }
} }
impl<T: Copy> From<T> for FFIResult<T> { impl<T: Copy + Default> From<T> for FFIResult<T> {
fn from(value: T) -> Self { fn from(value: T) -> Self {
Self::ok(value) Self::ok(value)
} }

View File

@ -38,7 +38,7 @@ unsafe extern "C" fn ability_new(
#[no_mangle] #[no_mangle]
unsafe extern "C" fn ability_name(handle: FFIHandle<Arc<dyn Ability>>) -> FFIResult<OwnedPtrString> { unsafe extern "C" fn ability_name(handle: FFIHandle<Arc<dyn Ability>>) -> FFIResult<OwnedPtrString> {
match CString::new(handle.from_ffi_handle().name().str()) { match CString::new(handle.from_ffi_handle().name().str()) {
Ok(s) => FFIResult::ok(s.into_raw()), Ok(s) => FFIResult::ok(OwnedPtrString(s.into_raw())),
Err(_) => FFIResult::err(anyhow!("Failed to convert name to CString")), Err(_) => FFIResult::err(anyhow!("Failed to convert name to CString")),
} }
} }
@ -47,7 +47,7 @@ unsafe extern "C" fn ability_name(handle: FFIHandle<Arc<dyn Ability>>) -> FFIRes
#[no_mangle] #[no_mangle]
unsafe extern "C" fn ability_effect(ptr: FFIHandle<Arc<dyn Ability>>) -> FFIResult<OwnedPtrString> { unsafe extern "C" fn ability_effect(ptr: FFIHandle<Arc<dyn Ability>>) -> FFIResult<OwnedPtrString> {
match CString::new(ptr.from_ffi_handle().effect().str()) { match CString::new(ptr.from_ffi_handle().effect().str()) {
Ok(s) => FFIResult::ok(s.into_raw()), Ok(s) => FFIResult::ok(OwnedPtrString(s.into_raw())),
Err(_) => FFIResult::err(anyhow!("Failed to convert effect to CString")), Err(_) => FFIResult::err(anyhow!("Failed to convert effect to CString")),
} }
} }

View File

@ -75,7 +75,7 @@ unsafe extern "C" fn form_name(ptr: FFIHandle<Arc<dyn Form>>) -> FFIResult<Owned
let obj = ptr.from_ffi_handle(); let obj = ptr.from_ffi_handle();
let name = obj.name(); let name = obj.name();
match CString::new(name.str()) { match CString::new(name.str()) {
Ok(name) => FFIResult::ok(name.into_raw()), Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())),
Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())), Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())),
} }
} }

View File

@ -41,7 +41,7 @@ unsafe extern "C" fn item_name(ptr: FFIHandle<Arc<dyn Item>>) -> FFIResult<Owned
let item = ptr.from_ffi_handle(); let item = ptr.from_ffi_handle();
let name = item.name(); let name = item.name();
match CString::new(name.str()) { match CString::new(name.str()) {
Ok(name) => FFIResult::ok(name.into_raw()), Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())),
Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())), Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())),
} }
} }

View File

@ -53,9 +53,9 @@ macro_rules! library_interface {
let lib = ptr.from_ffi_handle(); let lib = ptr.from_ffi_handle();
let v = lib.get_key_by_index(index); let v = lib.get_key_by_index(index);
if let Some(value) = v { if let Some(value) = v {
std::ffi::CString::new(value.str()).unwrap().into_raw() OwnedPtrString(std::ffi::CString::new(value.str()).unwrap().into_raw())
} else { } else {
std::ptr::null_mut() OwnedPtrString(std::ptr::null_mut())
} }
} }

View File

@ -68,7 +68,7 @@ unsafe extern "C" fn nature_library_get_nature_name(
let name = ptr.from_ffi_handle().get_nature_name(&nature); let name = ptr.from_ffi_handle().get_nature_name(&nature);
match name { match name {
Ok(name) => match CString::new(name.str()) { Ok(name) => match CString::new(name.str()) {
Ok(cstr) => FFIResult::ok(cstr.into_raw()), Ok(cstr) => FFIResult::ok(OwnedPtrString(cstr.into_raw())),
Err(_) => FFIResult::err(anyhow!("Failed to convert nature name to C string")), Err(_) => FFIResult::err(anyhow!("Failed to convert nature name to C string")),
}, },
Err(e) => FFIResult::err(e), Err(e) => FFIResult::err(e),

View File

@ -1,7 +1,7 @@
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{FFIResult, NonOwnedPtrString}; use crate::ffi::{FFIResult, NonOwnedPtrString, OwnedPtrString};
use crate::static_data::{TypeIdentifier, TypeLibrary, TypeLibraryImpl}; use crate::static_data::{TypeIdentifier, TypeLibrary, TypeLibraryImpl};
use std::ffi::{c_char, CStr, CString}; use std::ffi::{CStr, CString};
use std::sync::Arc; use std::sync::Arc;
/// Instantiates a new type library with a specific capacity. /// Instantiates a new type library with a specific capacity.
@ -33,17 +33,17 @@ unsafe extern "C" fn type_library_get_type_name(
ptr: FFIHandle<Arc<dyn TypeLibrary>>, ptr: FFIHandle<Arc<dyn TypeLibrary>>,
type_id: TypeIdentifier, type_id: TypeIdentifier,
found: *mut bool, found: *mut bool,
) -> FFIResult<*mut c_char> { ) -> FFIResult<OwnedPtrString> {
if let Some(v) = ptr.from_ffi_handle().get_type_name(type_id) { if let Some(v) = ptr.from_ffi_handle().get_type_name(type_id) {
*found = true; *found = true;
match CString::new(v.str()) { match CString::new(v.str()) {
Ok(v) => FFIResult::ok(v.into_raw()), Ok(v) => FFIResult::ok(OwnedPtrString(v.into_raw())),
Err(e) => FFIResult::err(e.into()), Err(e) => FFIResult::err(e.into()),
} }
} else { } else {
*found = false; *found = false;
FFIResult::ok(std::ptr::null_mut()) FFIResult::ok(OwnedPtrString(std::ptr::null_mut()))
} }
} }

View File

@ -108,7 +108,7 @@ extern "C" fn effect_parameter_get_as_string(ptr: FFIHandle<Arc<EffectParameter>
let p = ptr.from_ffi_handle(); let p = ptr.from_ffi_handle();
if let EffectParameter::String(b) = p.deref() { if let EffectParameter::String(b) = p.deref() {
match CString::new(b.str().to_string()) { match CString::new(b.str().to_string()) {
Ok(cstr) => FFIResult::ok(cstr.into_raw()), Ok(cstr) => FFIResult::ok(OwnedPtrString(cstr.into_raw())),
Err(_) => FFIResult::err(PkmnError::InvalidCString.into()), Err(_) => FFIResult::err(PkmnError::InvalidCString.into()),
} }
} else { } else {

View File

@ -1,5 +1,5 @@
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{ffi_handle_arc_dyn_getter, ExternPointer, FFIResult, NonOwnedPtrString, OwnedPtrString}; use crate::ffi::{ffi_handle_arc_dyn_getter, FFIResult, NonOwnedPtrString, OwnedPtrString};
use crate::static_data::{ use crate::static_data::{
EffectParameter, MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffect, SecondaryEffectImpl, EffectParameter, MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffect, SecondaryEffectImpl,
TypeIdentifier, TypeIdentifier,
@ -64,7 +64,7 @@ unsafe extern "C" fn move_data_name(ptr: FFIHandle<Arc<dyn MoveData>>) -> FFIRes
let move_data = ptr.from_ffi_handle(); let move_data = ptr.from_ffi_handle();
let name = move_data.name(); let name = move_data.name();
match CString::new(name.str()) { match CString::new(name.str()) {
Ok(name) => FFIResult::ok(name.into_raw()), Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())),
Err(_) => FFIResult::err_from_str("Unable to convert name to string"), Err(_) => FFIResult::err_from_str("Unable to convert name to string"),
} }
} }
@ -90,9 +90,9 @@ unsafe extern "C" fn move_data_secondary_effect(
/// Arbitrary flags that can be applied to the move. /// Arbitrary flags that can be applied to the move.
#[no_mangle] #[no_mangle]
unsafe extern "C" fn move_data_has_flag(ptr: ExternPointer<Arc<dyn MoveData>>, flag: *const c_char) -> u8 { unsafe extern "C" fn move_data_has_flag(ptr: FFIHandle<Arc<dyn MoveData>>, flag: *const c_char) -> u8 {
let flag = CStr::from_ptr(flag).into(); let flag = CStr::from_ptr(flag).into();
u8::from(ptr.as_ref().has_flag(&flag)) u8::from(ptr.from_ffi_handle().has_flag(&flag))
} }
/// Instantiates a new Secondary Effect. /// Instantiates a new Secondary Effect.
@ -119,37 +119,37 @@ unsafe extern "C" fn secondary_effect_new(
/// The chance the effect triggers. /// The chance the effect triggers.
#[no_mangle] #[no_mangle]
unsafe extern "C" fn secondary_effect_chance(ptr: ExternPointer<Box<dyn SecondaryEffect>>) -> f32 { unsafe extern "C" fn secondary_effect_chance(ptr: FFIHandle<Arc<dyn SecondaryEffect>>) -> f32 {
ptr.as_ref().chance() ptr.from_ffi_handle().chance()
} }
/// The name of the effect. /// The name of the effect.
#[no_mangle] #[no_mangle]
unsafe extern "C" fn secondary_effect_effect_name( unsafe extern "C" fn secondary_effect_effect_name(
ptr: ExternPointer<Box<dyn SecondaryEffect>>, ptr: FFIHandle<Arc<dyn SecondaryEffect>>,
) -> FFIResult<OwnedPtrString> { ) -> FFIResult<OwnedPtrString> {
match CString::new(ptr.as_ref().effect_name().str()) { match CString::new(ptr.from_ffi_handle().effect_name().str()) {
Ok(name) => FFIResult::ok(name.into_raw()), Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())),
Err(_) => FFIResult::err(anyhow!( Err(_) => FFIResult::err(anyhow!(
"Unable to convert effect name '{}' to CString", "Unable to convert effect name '{}' to CString",
ptr.as_ref().effect_name() ptr.from_ffi_handle().effect_name()
)), )),
} }
} }
/// The length of parameters of the effect. /// The length of parameters of the effect.
#[no_mangle] #[no_mangle]
unsafe extern "C" fn secondary_effect_parameter_length(ptr: ExternPointer<Box<dyn SecondaryEffect>>) -> usize { unsafe extern "C" fn secondary_effect_parameter_length(ptr: FFIHandle<Arc<dyn SecondaryEffect>>) -> usize {
ptr.as_ref().parameters().len() ptr.from_ffi_handle().parameters().len()
} }
/// Get a parameter of the effect. /// Get a parameter of the effect.
#[no_mangle] #[no_mangle]
unsafe extern "C" fn secondary_effect_parameter_get( unsafe extern "C" fn secondary_effect_parameter_get(
ptr: ExternPointer<Box<dyn SecondaryEffect>>, ptr: FFIHandle<Arc<dyn SecondaryEffect>>,
index: usize, index: usize,
) -> FFIHandle<Arc<EffectParameter>> { ) -> FFIHandle<Arc<EffectParameter>> {
if let Some(v) = ptr.as_ref().parameters().get(index) { if let Some(v) = ptr.from_ffi_handle().parameters().get(index) {
FFIHandle::get_handle(v.clone().into()) FFIHandle::get_handle(v.clone().into())
} else { } else {
FFIHandle::none() FFIHandle::none()