From 6d0db930f6d4f02a045a9be0f661fe3edb00a42b Mon Sep 17 00:00:00 2001 From: qwreey Date: Mon, 26 Aug 2024 09:14:31 +0000 Subject: [PATCH] Implement ctype casting (#243) --- Cargo.lock | 58 ++++++++++ crates/lune-std-ffi/Cargo.toml | 1 + crates/lune-std-ffi/src/c/c_arr.rs | 34 ++---- crates/lune-std-ffi/src/c/c_cast.rs | 1 - crates/lune-std-ffi/src/c/c_fn.rs | 12 +- crates/lune-std-ffi/src/c/c_helper.rs | 71 ++++++++---- crates/lune-std-ffi/src/c/c_ptr.rs | 18 ++- crates/lune-std-ffi/src/c/c_struct.rs | 25 +---- crates/lune-std-ffi/src/c/c_type.rs | 103 ++++++++++++++++-- crates/lune-std-ffi/src/c/mod.rs | 1 + crates/lune-std-ffi/src/c/types/c_char.rs | 68 ++++++++++++ crates/lune-std-ffi/src/c/types/c_int.rs | 37 ++++++- crates/lune-std-ffi/src/c/types/mod.rs | 3 +- .../lune-std-ffi/src/ffi/ffi_association.rs | 6 +- crates/lune-std-ffi/src/ffi/ffi_box.rs | 8 +- crates/lune-std-ffi/src/ffi/ffi_helper.rs | 1 + crates/lune-std-ffi/src/ffi/ffi_lib.rs | 2 +- crates/lune-std-ffi/src/ffi/ffi_ref.rs | 36 ++++-- crates/lune-std-ffi/src/lib.rs | 4 +- crates/lune-std-ffi/todo.md | 2 + 20 files changed, 381 insertions(+), 110 deletions(-) delete mode 100644 crates/lune-std-ffi/src/c/c_cast.rs create mode 100644 crates/lune-std-ffi/src/c/types/c_char.rs diff --git a/Cargo.lock b/Cargo.lock index 4e4b654..4035b8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1637,6 +1637,7 @@ dependencies = [ "lune-utils", "mlua", "mlua-sys", + "num", ] [[package]] @@ -1933,6 +1934,40 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1948,6 +1983,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" diff --git a/crates/lune-std-ffi/Cargo.toml b/crates/lune-std-ffi/Cargo.toml index b867401..0420f1d 100644 --- a/crates/lune-std-ffi/Cargo.toml +++ b/crates/lune-std-ffi/Cargo.toml @@ -15,6 +15,7 @@ workspace = true [dependencies] mlua = { version = "0.9.9", features = ["luau"] } mlua-sys = { version = "0.6.2", features = ["luau"] } +num = "0.3.1" dlopen2 = "0.6" libffi = "3.2.0" diff --git a/crates/lune-std-ffi/src/c/c_arr.rs b/crates/lune-std-ffi/src/c/c_arr.rs index 2a42019..ba81bf7 100644 --- a/crates/lune-std-ffi/src/c/c_arr.rs +++ b/crates/lune-std-ffi/src/c/c_arr.rs @@ -1,14 +1,9 @@ -use std::any::Any; - use libffi::middle::Type; use mlua::prelude::*; use super::association_names::CARR_INNER; -use super::c_helper::{ - get_ensured_size, name_from_userdata, stringify_userdata, type_from_userdata, -}; +use super::c_helper::{get_ensured_size, pretty_format_userdata, type_from_userdata}; use super::c_ptr::CPtr; -use super::c_type::CType; use crate::ffi::ffi_association::{get_association, set_association}; // This is a series of some type. @@ -49,7 +44,7 @@ impl CArr { luatype: &LuaAnyUserData<'lua>, length: usize, ) -> LuaResult> { - let fields = type_from_userdata(luatype)?; + let fields = type_from_userdata(lua, luatype)?; let carr = lua.create_userdata(Self::new(fields, length)?)?; set_association(lua, CARR_INNER, &carr, luatype)?; @@ -66,7 +61,7 @@ impl CArr { // Stringify cstruct for pretty printing something like: // - pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult { + pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { let inner: LuaValue = userdata.get("inner")?; let carr = userdata.borrow::()?; @@ -75,20 +70,11 @@ impl CArr { .as_userdata() .ok_or(LuaError::external("failed to get inner type userdata."))?; - if inner.is::>() { - Ok(format!( - " {} ; {} ", - stringify_userdata(inner)?, - carr.length - )) - } else { - Ok(format!( - " <{}({})> ; {} ", - name_from_userdata(inner), - stringify_userdata(inner)?, - carr.length - )) - } + Ok(format!( + "{}*{}", + pretty_format_userdata(lua, inner)?, + carr.length, + )) } else { Err(LuaError::external("failed to get inner type userdata.")) } @@ -119,8 +105,8 @@ impl LuaUserData for CArr { let pointer = CPtr::from_lua_userdata(lua, &this)?; Ok(pointer) }); - methods.add_meta_function(LuaMetaMethod::ToString, |_, this: LuaAnyUserData| { - let result = CArr::stringify(&this)?; + methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| { + let result = CArr::stringify(lua, &this)?; Ok(result) }); } diff --git a/crates/lune-std-ffi/src/c/c_cast.rs b/crates/lune-std-ffi/src/c/c_cast.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/lune-std-ffi/src/c/c_cast.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/lune-std-ffi/src/c/c_fn.rs b/crates/lune-std-ffi/src/c/c_fn.rs index 315497e..3dfe3a4 100644 --- a/crates/lune-std-ffi/src/c/c_fn.rs +++ b/crates/lune-std-ffi/src/c/c_fn.rs @@ -35,11 +35,15 @@ impl CFn { } } - pub fn from_lua_table(args: LuaTable, ret: LuaAnyUserData) -> LuaResult { - let args = type_list_from_table(&args)?; - let ret = type_from_userdata(&ret)?; + pub fn from_lua_table(lua: &Lua, args: LuaTable, ret: LuaAnyUserData) -> LuaResult { + let args = type_list_from_table(lua, &args)?; + let ret = type_from_userdata(lua, &ret)?; Ok(Self::new(args, ret)) } } -impl LuaUserData for CFn {} +impl LuaUserData for CFn { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + // methods.add_method("from", | this, |) + } +} diff --git a/crates/lune-std-ffi/src/c/c_helper.rs b/crates/lune-std-ffi/src/c/c_helper.rs index 2a18096..2310180 100644 --- a/crates/lune-std-ffi/src/c/c_helper.rs +++ b/crates/lune-std-ffi/src/c/c_helper.rs @@ -1,18 +1,19 @@ -use std::any::Any; use std::ptr::{self, null_mut}; use libffi::{low, middle::Type, raw}; use lune_utils::fmt::{pretty_format_value, ValueFormatConfig}; use mlua::prelude::*; +use super::association_names::CTYPE_STATIC; use super::c_arr::CArr; use super::c_ptr::CPtr; use super::c_struct::CStruct; -use super::c_type::CType; +use super::c_type::CTypeStatic; +use crate::ffi::ffi_association::get_association; use crate::ffi::ffi_helper::FFI_STATUS_NAMES; // get Vec from table(array) of c-types userdata -pub fn type_list_from_table(table: &LuaTable) -> LuaResult> { +pub fn type_list_from_table(lua: &Lua, table: &LuaTable) -> LuaResult> { let len: usize = table.raw_len(); let mut fields = Vec::with_capacity(len); @@ -21,7 +22,7 @@ pub fn type_list_from_table(table: &LuaTable) -> LuaResult> { let value = table.raw_get(i + 1)?; match value { LuaValue::UserData(field_type) => { - fields.push(type_from_userdata(&field_type)?); + fields.push(type_from_userdata(lua, &field_type)?); } _ => { return Err(LuaError::external(format!( @@ -36,11 +37,17 @@ pub fn type_list_from_table(table: &LuaTable) -> LuaResult> { } // get libffi_type from any c-type userdata -pub fn type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult { +pub fn type_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { if userdata.is::() { Ok(userdata.borrow::()?.get_type().to_owned()) - } else if userdata.is::>() { - Ok(userdata.borrow::>()?.get_type().to_owned()) + } else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? { + Ok(t.as_userdata() + .ok_or(LuaError::external( + "Failed to get static ctype from userdata", + ))? + .borrow::()? + .libffi_type + .clone()) } else if userdata.is::() { Ok(userdata.borrow::()?.get_type().to_owned()) } else if userdata.is::() { @@ -59,37 +66,61 @@ pub fn type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult { } // stringify any c-type userdata (for recursive) -pub fn stringify_userdata(userdata: &LuaAnyUserData) -> LuaResult { - if userdata.is::>() { - Ok(String::from( - userdata.borrow::>()?.stringify(), - )) - } else if userdata.is::() { - let name = CStruct::stringify(userdata)?; +pub fn stringify_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { + if userdata.is::() { + let name = CStruct::stringify(lua, userdata)?; Ok(name) } else if userdata.is::() { - let name = CArr::stringify(userdata)?; + let name = CArr::stringify(lua, userdata)?; Ok(name) } else if userdata.is::() { - let name: String = CPtr::stringify(userdata)?; + let name: String = CPtr::stringify(lua, userdata)?; Ok(name) + // Get CTypeStatic from CType + } else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? { + Ok(String::from( + t.as_userdata() + .ok_or(LuaError::external( + "Failed to get static ctype from userdata", + ))? + .borrow::()? + .name + .unwrap_or("unnamed"), + )) } else { Ok(String::from("unnamed")) } } // get name tag for any c-type userdata -pub fn name_from_userdata(userdata: &LuaAnyUserData) -> String { - if userdata.is::() { +pub fn tagname_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { + Ok(if userdata.is::() { String::from("CStruct") - } else if userdata.is::>() { - String::from("CType") } else if userdata.is::() { String::from("CArr") } else if userdata.is::() { String::from("CPtr") + } else if userdata_is_ctype(lua, userdata)? { + String::from("CType") } else { String::from("unnamed") + }) +} + +pub fn userdata_is_ctype(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { + Ok(get_association(lua, CTYPE_STATIC, userdata)?.is_some()) +} + +// emulate 'print' for ctype userdata, but ctype is simplified +pub fn pretty_format_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { + if userdata_is_ctype(lua, userdata)? { + stringify_userdata(lua, userdata) + } else { + Ok(format!( + "<{}({})>", + tagname_from_userdata(lua, userdata)?, + stringify_userdata(lua, userdata)? + )) } } diff --git a/crates/lune-std-ffi/src/c/c_ptr.rs b/crates/lune-std-ffi/src/c/c_ptr.rs index 00a730d..00d4e20 100644 --- a/crates/lune-std-ffi/src/c/c_ptr.rs +++ b/crates/lune-std-ffi/src/c/c_ptr.rs @@ -5,7 +5,7 @@ use mlua::prelude::*; use super::association_names::CPTR_INNER; use super::c_arr::CArr; -use super::c_helper::{name_from_userdata, stringify_userdata}; +use super::c_helper::pretty_format_userdata; use crate::ffi::ffi_association::{get_association, set_association}; pub struct CPtr(); @@ -16,8 +16,8 @@ impl CPtr { pub fn from_lua_userdata<'lua>( lua: &'lua Lua, inner: &LuaAnyUserData, - ) -> LuaResult> { - let value = Self().into_lua(lua)?; + ) -> LuaResult> { + let value = lua.create_userdata(Self())?; set_association(lua, CPTR_INNER, &value, inner)?; @@ -25,18 +25,14 @@ impl CPtr { } // Stringify CPtr with inner ctype - pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult { + pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { let inner: LuaValue = userdata.get("inner")?; if inner.is_userdata() { let inner = inner .as_userdata() .ok_or(LuaError::external("failed to get inner type userdata."))?; - Ok(format!( - " <{}({})> ", - name_from_userdata(inner), - stringify_userdata(inner)?, - )) + pretty_format_userdata(lua, inner) } else { Err(LuaError::external("failed to get inner type userdata.")) } @@ -67,8 +63,8 @@ impl LuaUserData for CPtr { let carr = CArr::from_lua_userdata(lua, &this, length)?; Ok(carr) }); - methods.add_meta_function(LuaMetaMethod::ToString, |_, this: LuaAnyUserData| { - let name: Result = CPtr::stringify(&this); + methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| { + let name: Result = CPtr::stringify(lua, &this); Ok(name) }); } diff --git a/crates/lune-std-ffi/src/c/c_struct.rs b/crates/lune-std-ffi/src/c/c_struct.rs index 8812eb5..24cb008 100644 --- a/crates/lune-std-ffi/src/c/c_struct.rs +++ b/crates/lune-std-ffi/src/c/c_struct.rs @@ -1,6 +1,5 @@ #![allow(clippy::cargo_common_metadata)] -use std::any::Any; use std::vec::Vec; use libffi::{low, middle::Type, raw}; @@ -8,9 +7,8 @@ use mlua::prelude::*; use super::association_names::CSTRUCT_INNER; use super::c_arr::CArr; -use super::c_helper::{name_from_userdata, stringify_userdata, type_list_from_table}; +use super::c_helper::{pretty_format_userdata, type_list_from_table}; use super::c_ptr::CPtr; -use super::c_type::CType; use crate::ffi::ffi_association::{get_association, set_association}; use crate::ffi::ffi_helper::FFI_STATUS_NAMES; @@ -63,7 +61,7 @@ impl CStruct { lua: &'lua Lua, table: LuaTable<'lua>, ) -> LuaResult> { - let fields = type_list_from_table(&table)?; + let fields = type_list_from_table(lua, &table)?; let cstruct = lua.create_userdata(Self::new(fields)?)?; table.set_readonly(true); set_association(lua, CSTRUCT_INNER, &cstruct, table)?; @@ -72,7 +70,7 @@ impl CStruct { // Stringify cstruct for pretty printing something like: // - pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult { + pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult { let field: LuaValue = userdata.get("inner")?; if field.is_table() { let table = field @@ -82,18 +80,7 @@ impl CStruct { let mut result = String::from(" "); for i in 0..table.raw_len() { let child: LuaAnyUserData = table.raw_get(i + 1)?; - if child.is::>() { - result.push_str(format!("{}, ", stringify_userdata(&child)?).as_str()); - } else { - result.push_str( - format!( - "<{}({})>, ", - name_from_userdata(&child), - stringify_userdata(&child)? - ) - .as_str(), - ); - } + result.push_str(pretty_format_userdata(lua, &child)?.as_str()); } // size of @@ -151,8 +138,8 @@ impl LuaUserData for CStruct { let carr = CArr::from_lua_userdata(lua, &this, length)?; Ok(carr) }); - methods.add_meta_function(LuaMetaMethod::ToString, |_, this: LuaAnyUserData| { - let result = CStruct::stringify(&this)?; + methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| { + let result = CStruct::stringify(lua, &this)?; Ok(result) }); } diff --git a/crates/lune-std-ffi/src/c/c_type.rs b/crates/lune-std-ffi/src/c/c_type.rs index 3e4ee42..30f73e2 100644 --- a/crates/lune-std-ffi/src/c/c_type.rs +++ b/crates/lune-std-ffi/src/c/c_type.rs @@ -1,13 +1,17 @@ #![allow(clippy::cargo_common_metadata)] +use lune_utils::fmt::{pretty_format_value, ValueFormatConfig}; +use num::cast::{AsPrimitive, NumCast}; use std::marker::PhantomData; use libffi::middle::Type; use mlua::prelude::*; +use super::association_names::CTYPE_STATIC; use super::c_arr::CArr; use super::c_helper::get_ensured_size; use super::c_ptr::CPtr; +use crate::ffi::ffi_association::set_association; use crate::ffi::ffi_helper::get_ptr_from_userdata; pub struct CType { @@ -16,23 +20,57 @@ pub struct CType { libffi_type: Type, size: usize, name: Option<&'static str>, + signedness: bool, _phantom: PhantomData, } +// Static CType, for borrow, is operation +pub struct CTypeStatic { + pub libffi_type: Type, + pub size: usize, + pub name: Option<&'static str>, + pub signedness: bool, +} +impl CTypeStatic { + fn new(ctype: &CType) -> Self { + Self { + libffi_type: ctype.libffi_type.clone(), + size: ctype.size, + name: ctype.name, + signedness: ctype.signedness, + } + } +} +impl LuaUserData for CTypeStatic {} + impl CType where - T: ?Sized, + Self: CTypeConvert, + T: 'static, { - pub fn new_with_libffi_type(libffi_type: Type, name: Option<&'static str>) -> LuaResult { + pub fn new_with_libffi_type<'lua>( + lua: &'lua Lua, + libffi_type: Type, + signedness: bool, + name: Option<&'static str>, + ) -> LuaResult> { // let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); let size = get_ensured_size(libffi_type.as_raw_ptr())?; - Ok(Self { + + let ctype = Self { // libffi_cif: libffi_cfi, libffi_type, size, name, - _phantom: PhantomData {}, - }) + signedness, + _phantom: PhantomData, + }; + let userdata_static = lua.create_any_userdata(CTypeStatic::new::(&ctype))?; + let userdata = lua.create_userdata(ctype)?; + + set_association(lua, CTYPE_STATIC, &userdata, &userdata_static)?; + + Ok(userdata) } pub fn get_type(&self) -> &Type { @@ -45,9 +83,19 @@ where None => "unnamed", } } + + pub fn cast_failed_with(&self, into_ctype: &LuaAnyUserData) -> LuaError { + let config = ValueFormatConfig::new(); + LuaError::external(format!( + "Cannot cast to {}", + self.stringify(), + pretty_format_value(&LuaValue::UserData(into_ctype.to_owned()), &config) + )) + } } -pub trait PtrHandle { +// Handle C data, provide type conversion between luavalue and c-type +pub trait CTypeConvert { // Convert luavalue into data, then write into ptr fn luavalue_into_ptr(value: LuaValue, ptr: *mut ()) -> LuaResult<()>; @@ -79,12 +127,53 @@ pub trait PtrHandle { } } +pub trait CTypeNumCast +where + T: NumCast, +{ + // Cast T as U + fn cast_userdata(from: &LuaAnyUserData, into: &LuaAnyUserData) -> LuaResult<()> + where + T: AsPrimitive, + U: 'static + Copy, + { + let from_ptr = unsafe { get_ptr_from_userdata(from, None)?.cast::() }; + let into_ptr = unsafe { get_ptr_from_userdata(into, None)?.cast::() }; + + unsafe { + *into_ptr = (*from_ptr).as_(); + } + + Ok(()) + } + + fn cast_userdata_if_type_match( + ctype: &LuaAnyUserData, + from: &LuaAnyUserData, + into: &LuaAnyUserData, + ) -> LuaResult> + where + T: AsPrimitive, + U: 'static + Copy, + { + if ctype.is::>() { + Self::cast_userdata(from, into)?; + Ok(Some(())) + } else { + Ok(None) + } + } +} + impl LuaUserData for CType where - Self: Sized + PtrHandle, + Self: CTypeConvert, + T: 'static, { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("size", |_, this| Ok(this.size)); + fields.add_meta_field(LuaMetaMethod::Type, "CType"); + fields.add_field_method_get("signedness", |_, this| Ok(this.signedness)); } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { diff --git a/crates/lune-std-ffi/src/c/mod.rs b/crates/lune-std-ffi/src/c/mod.rs index be3364d..35ba063 100644 --- a/crates/lune-std-ffi/src/c/mod.rs +++ b/crates/lune-std-ffi/src/c/mod.rs @@ -14,4 +14,5 @@ mod association_names { pub const CPTR_INNER: &str = "__cptr_inner"; pub const CARR_INNER: &str = "__carr_inner"; pub const CSTRUCT_INNER: &str = "__cstruct_inner"; + pub const CTYPE_STATIC: &str = "__ctype_static"; } diff --git a/crates/lune-std-ffi/src/c/types/c_char.rs b/crates/lune-std-ffi/src/c/types/c_char.rs new file mode 100644 index 0000000..18c0a67 --- /dev/null +++ b/crates/lune-std-ffi/src/c/types/c_char.rs @@ -0,0 +1,68 @@ +use core::ffi::*; + +use libffi::middle::Type; +use mlua::prelude::*; + +use super::super::c_type::{CType, CTypeConvert, CTypeNumCast}; +use crate::ffi::ffi_platform::CHAR_IS_SIGNED; + +impl CTypeConvert for CType { + fn luavalue_into_ptr(value: LuaValue, ptr: *mut ()) -> LuaResult<()> { + let value = match value { + LuaValue::Integer(t) => t, + _ => { + return Err(LuaError::external(format!( + "Integer expected, got {}", + value.type_name() + ))) + } + } as c_char; + unsafe { + *(ptr.cast::()) = value; + } + Ok(()) + } + fn ptr_into_luavalue(lua: &Lua, ptr: *mut ()) -> LuaResult { + let value = unsafe { (*ptr.cast::()).into_lua(lua)? }; + Ok(value) + } +} + +impl CType { + fn cast( + &self, + into_ctype: &LuaAnyUserData, + from: &LuaAnyUserData, + into: &LuaAnyUserData, + ) -> LuaResult<()> { + Self::cast_userdata_if_type_match::(into_ctype, from, into)? + .or(Self::cast_userdata_if_type_match::( + into_ctype, from, into, + )?) + .or(Self::cast_userdata_if_type_match::( + into_ctype, from, into, + )?) + .or(Self::cast_userdata_if_type_match::( + into_ctype, from, into, + )?) + .ok_or_else(|| self.cast_failed_with(into_ctype)) + } +} + +impl CTypeNumCast for CType {} + +pub fn get_export(lua: &Lua) -> LuaResult<(&'static str, LuaAnyUserData)> { + Ok(( + "char", + CType::::new_with_libffi_type( + lua, + if CHAR_IS_SIGNED { + Type::c_schar() + } else { + Type::c_uchar() + }, + CHAR_IS_SIGNED, + Some("char"), + )?, + )) +} diff --git a/crates/lune-std-ffi/src/c/types/c_int.rs b/crates/lune-std-ffi/src/c/types/c_int.rs index 3e36a69..c11a8eb 100644 --- a/crates/lune-std-ffi/src/c/types/c_int.rs +++ b/crates/lune-std-ffi/src/c/types/c_int.rs @@ -1,11 +1,11 @@ -use core::ffi::c_int; +use core::ffi::*; use libffi::middle::Type; use mlua::prelude::*; -use super::super::c_type::{CType, PtrHandle}; +use super::super::c_type::{CType, CTypeConvert, CTypeNumCast}; -impl PtrHandle for CType { +impl CTypeConvert for CType { fn luavalue_into_ptr(value: LuaValue, ptr: *mut ()) -> LuaResult<()> { let value = match value { LuaValue::Integer(t) => t, @@ -28,11 +28,36 @@ impl PtrHandle for CType { } impl CType { - fn new() -> LuaResult { - Self::new_with_libffi_type(Type::c_int(), Some("int")) + fn cast( + &self, + into_ctype: &LuaAnyUserData, + from: &LuaAnyUserData, + into: &LuaAnyUserData, + ) -> LuaResult<()> { + Self::cast_userdata_if_type_match::(into_ctype, from, into)? + .or(Self::cast_userdata_if_type_match::( + into_ctype, from, into, + )?) + .or(Self::cast_userdata_if_type_match::( + into_ctype, from, into, + )?) + .or(Self::cast_userdata_if_type_match::( + into_ctype, from, into, + )?) + .ok_or_else(|| self.cast_failed_with(into_ctype)) } } +impl CTypeNumCast for CType {} + pub fn get_export(lua: &Lua) -> LuaResult<(&'static str, LuaAnyUserData)> { - Ok(("int", lua.create_userdata(CType::::new()?)?)) + Ok(( + "int", + CType::::new_with_libffi_type( + lua, + Type::c_int(), + c_int::MIN.unsigned_abs() != 0, + Some("int"), + )?, + )) } diff --git a/crates/lune-std-ffi/src/c/types/mod.rs b/crates/lune-std-ffi/src/c/types/mod.rs index 0d4986d..a543f97 100644 --- a/crates/lune-std-ffi/src/c/types/mod.rs +++ b/crates/lune-std-ffi/src/c/types/mod.rs @@ -1,8 +1,9 @@ +mod c_char; mod c_int; use mlua::prelude::*; // export all default c-types pub fn create_all_types(lua: &Lua) -> LuaResult> { - Ok(vec![c_int::get_export(lua)?]) + Ok(vec![c_int::get_export(lua)?, c_char::get_export(lua)?]) } diff --git a/crates/lune-std-ffi/src/ffi/ffi_association.rs b/crates/lune-std-ffi/src/ffi/ffi_association.rs index 49f9c8a..be242de 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_association.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_association.rs @@ -10,10 +10,14 @@ // uservalue operations cannot be performed directly, // so this is the best solution for now. // If the dependency is deep, the value may be completely destroyed when -// gc is performed multiple times. As an example, there is the following case: +// gc is performed multiple times. To prevent this situation, FFI 'copies' +// dependency if possible. // // ffi.i32:ptr():ptr() +// Something like this, every pointer type will have various inner field. +// // box:ref():ref() +// But, in this case, // // Since the outermost pointer holds the definition for the pointer // type inside it, only the outermost type will be removed on the first gc. diff --git a/crates/lune-std-ffi/src/ffi/ffi_box.rs b/crates/lune-std-ffi/src/ffi/ffi_box.rs index f7d593c..d25362d 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_box.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_box.rs @@ -74,11 +74,15 @@ impl FfiBox { t ))); } - ptr = unsafe { target.get_ptr().offset(t) }; + ptr = unsafe { target.get_ptr().byte_offset(t) }; bounds = bounds.offset(t); } - let luaref = lua.create_userdata(FfiRef::new(ptr.cast(), Some(bounds)))?; + // Lua should not be able to deref a box that refers to a box managed by Lua. + // To deref a box space is to allow lua to read any space, + // which has security issues and is ultimately dangerous. + // Therefore, box:ref():deref() is not allowed. + let luaref = lua.create_userdata(FfiRef::new(ptr.cast(), false, Some(bounds)))?; // Makes box alive longer then ref set_association(lua, REF_INNER, &luaref, &this)?; diff --git a/crates/lune-std-ffi/src/ffi/ffi_helper.rs b/crates/lune-std-ffi/src/ffi/ffi_helper.rs index 3e9f48a..00605e7 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_helper.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_helper.rs @@ -11,6 +11,7 @@ pub const FFI_STATUS_NAMES: [&str; 4] = [ "ffi_status_FFI_BAD_ARGTYPE", ]; +// TODO: using trait // Get raw pointer from userdata // TODO: boundary check pub unsafe fn get_ptr_from_userdata( diff --git a/crates/lune-std-ffi/src/ffi/ffi_lib.rs b/crates/lune-std-ffi/src/ffi/ffi_lib.rs index 70c81c6..a4a657f 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_lib.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_lib.rs @@ -39,7 +39,7 @@ impl FfiLib { .map_err(|err| LuaError::external(format!("{err}")))? }; - let luasym = lua.create_userdata(FfiRef::new((*sym).cast(), None))?; + let luasym = lua.create_userdata(FfiRef::new((*sym).cast(), true, None))?; set_association(lua, SYM_INNER, &luasym, &this)?; diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref.rs b/crates/lune-std-ffi/src/ffi/ffi_ref.rs index 5e9a9f8..8700e72 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_ref.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_ref.rs @@ -16,12 +16,17 @@ use super::ffi_bounds::FfiRefBounds; pub struct FfiRef { ptr: *mut (), + dereferenceable: bool, range: Option, } impl FfiRef { - pub fn new(ptr: *mut (), range: Option) -> Self { - Self { ptr, range } + pub fn new(ptr: *mut (), dereferenceable: bool, range: Option) -> Self { + Self { + ptr, + dereferenceable, + range, + } } // Make FfiRef from ref @@ -33,16 +38,15 @@ impl FfiRef { let luaref = lua.create_userdata(FfiRef::new( ptr::from_ref(&target.ptr) as *mut (), + true, Some(FfiRefBounds { low: 0, high: size_of::(), }), ))?; - // If the ref holds a box, make sure the new ref also holds the box - if let Some(t) = get_association(lua, REF_INNER, &this)? { - set_association(lua, REF_INNER, &luaref, t)?; - } + // If the ref holds a box, make sure the new ref also holds the box by holding ref + set_association(lua, REF_INNER, &luaref, &this)?; Ok(luaref) } @@ -52,7 +56,8 @@ impl FfiRef { } pub unsafe fn deref(&self) -> Self { - Self::new(*self.ptr.cast::<*mut ()>(), None) + // FIXME + Self::new(*self.ptr.cast::<*mut ()>(), true, None) } pub unsafe fn offset(&self, offset: isize) -> LuaResult { @@ -67,7 +72,8 @@ impl FfiRef { let range = self.range.as_ref().map(|t| t.offset(offset)); Ok(Self::new( - self.ptr.cast::().offset(offset).cast(), + self.ptr.byte_offset(offset), + self.dereferenceable, range, )) } @@ -81,14 +87,22 @@ impl LuaUserData for FfiRef { let result = lua.create_userdata(unsafe { ffiref.deref() })?; if let Some(t) = inner { + // if let Some(u) = get_association(lua, regname, value) {} set_association(lua, REF_INNER, &result, &t)?; } Ok(result) }); - methods.add_method("offset", |_, this, offset: isize| { - let ffiref = unsafe { this.offset(offset)? }; - Ok(ffiref) + methods.add_function("offset", |lua, (this, offset): (LuaAnyUserData, isize)| { + let ffiref = unsafe { this.borrow::()?.offset(offset)? }; + let userdata = lua.create_userdata(ffiref)?; + + // If the ref holds a box, make sure the new ref also holds the box + if let Some(t) = get_association(lua, REF_INNER, &this)? { + set_association(lua, REF_INNER, &userdata, t)?; + } + + Ok(userdata) }); methods.add_function("ref", |lua, this: LuaAnyUserData| { let ffiref = FfiRef::luaref(lua, this)?; diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index 4a51975..87ea3ea 100644 --- a/crates/lune-std-ffi/src/lib.rs +++ b/crates/lune-std-ffi/src/lib.rs @@ -33,8 +33,8 @@ pub fn module(lua: &Lua) -> LuaResult { let cstruct = CStruct::from_lua_table(lua, types)?; Ok(cstruct) })? - .with_function("fn", |_, (args, ret): (LuaTable, LuaAnyUserData)| { - let cfn = CFn::from_lua_table(args, ret)?; + .with_function("fn", |lua, (args, ret): (LuaTable, LuaAnyUserData)| { + let cfn = CFn::from_lua_table(lua, args, ret)?; Ok(cfn) })?; diff --git a/crates/lune-std-ffi/todo.md b/crates/lune-std-ffi/todo.md index f0a0ab8..fbd8999 100644 --- a/crates/lune-std-ffi/todo.md +++ b/crates/lune-std-ffi/todo.md @@ -3,6 +3,8 @@ - [ ] Add docs - [ ] Typing +pragma pack? + # Raw - [ ] Raw:toRef()