Fix pretty-prints for type-define (#243)

This commit is contained in:
qwreey 2024-10-16 07:57:25 +00:00
parent f094f2b74a
commit 95258e1b51
No known key found for this signature in database
GPG key ID: D28DB79297A214BD
16 changed files with 507 additions and 484 deletions

View file

@ -3,19 +3,13 @@ use std::cell::Ref;
use libffi::middle::Type;
use mlua::prelude::*;
use super::{
association_names::CARR_INNER,
c_helper::{get_conv, libffi_type_from_userdata, pretty_format_userdata},
CPtr,
};
use super::{association_names::CARR_INNER, c_helper, method_provider};
use crate::ffi::{
ffi_association::{get_association, set_association},
FfiBox, GetNativeData, NativeConvert, NativeData, NativeSize,
NativeConvert, NativeData, NativeSize,
};
use crate::libffi_helper::get_ensured_size;
// FIXME: unsized array
// This is a series of some type.
// It provides the final size and the offset of the index,
// but does not allow multidimensional arrays because of API complexity.
@ -26,43 +20,42 @@ use crate::libffi_helper::get_ensured_size;
// See: https://stackoverflow.com/a/43525176
pub struct CArr {
// element_type: Type,
struct_type: Type,
length: usize,
field_size: usize,
size: usize,
conv: *const dyn NativeConvert,
inner_size: usize,
inner_conv: *const dyn NativeConvert,
}
impl CArr {
pub fn new(
element_type: Type,
length: usize,
conv: *const dyn NativeConvert,
inner_conv: *const dyn NativeConvert,
) -> LuaResult<Self> {
let field_size = get_ensured_size(element_type.as_raw_ptr())?;
let inner_size = get_ensured_size(element_type.as_raw_ptr())?;
let struct_type = Type::structure(vec![element_type.clone(); length]);
Ok(Self {
// element_type,
struct_type,
length,
field_size,
size: field_size * length,
conv,
size: inner_size * length,
inner_size,
inner_conv,
})
}
pub fn new_from_lua_userdata<'lua>(
pub fn from_userdata<'lua>(
lua: &'lua Lua,
luatype: &LuaAnyUserData<'lua>,
type_userdata: &LuaAnyUserData<'lua>,
length: usize,
) -> LuaResult<LuaAnyUserData<'lua>> {
let fields = libffi_type_from_userdata(lua, luatype)?;
let conv = unsafe { get_conv(luatype)? };
let fields = c_helper::get_middle_type(type_userdata)?;
let conv = unsafe { c_helper::get_conv(type_userdata)? };
let carr = lua.create_userdata(Self::new(fields, length, conv)?)?;
set_association(lua, CARR_INNER, &carr, luatype)?;
set_association(lua, CARR_INNER, &carr, type_userdata)?;
Ok(carr)
}
@ -70,29 +63,21 @@ impl CArr {
self.length
}
pub fn get_type(&self) -> &Type {
&self.struct_type
pub fn get_type(&self) -> Type {
self.struct_type.clone()
}
// pub fn get_element_type(&self) -> &Type {
// &self.element_type
// }
// Stringify cstruct for pretty printing something like:
// <CStruct( u8, i32, size = 8 )>
// Stringify for pretty printing like:
// <CArr( u8, length = 8 )>
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
let inner: LuaValue = userdata.get("inner")?;
let carr = userdata.borrow::<CArr>()?;
if inner.is_userdata() {
let inner = inner
.as_userdata()
.ok_or(LuaError::external("failed to get inner type userdata."))?;
let this = userdata.borrow::<CArr>()?;
if let Some(LuaValue::UserData(inner_userdata)) =
get_association(lua, CARR_INNER, userdata)?
{
Ok(format!(
"{}*{}",
pretty_format_userdata(lua, inner)?,
carr.length,
" {}, length = {} ",
c_helper::pretty_format(lua, &inner_userdata)?,
this.length,
))
} else {
Err(LuaError::external("failed to get inner type userdata."))
@ -118,10 +103,10 @@ impl NativeConvert for CArr {
return Err(LuaError::external("Value is not a table"));
};
for i in 0..self.length {
let field_offset = (i * self.field_size) as isize;
let field_offset = (i * self.inner_size) as isize;
let data: LuaValue = table.get(i + 1)?;
self.conv.as_ref().unwrap().luavalue_into(
self.inner_conv.as_ref().unwrap().luavalue_into(
lua,
field_offset + offset,
data_handle,
@ -139,10 +124,10 @@ impl NativeConvert for CArr {
) -> LuaResult<LuaValue<'lua>> {
let table = lua.create_table_with_capacity(self.length, 0)?;
for i in 0..self.length {
let field_offset = (i * self.field_size) as isize;
let field_offset = (i * self.inner_size) as isize;
table.set(
i + 1,
self.conv.as_ref().unwrap().luavalue_from(
self.inner_conv.as_ref().unwrap().luavalue_from(
lua,
field_offset + offset,
data_handle,
@ -166,55 +151,23 @@ impl LuaUserData for CArr {
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
// Subtype
method_provider::provide_ptr(methods);
// ToString
method_provider::provide_to_string(methods);
// Realize
method_provider::provide_box(methods);
method_provider::provide_from(methods);
method_provider::provide_into(methods);
methods.add_method("offset", |_, this, offset: isize| {
if this.length > (offset as usize) && offset >= 0 {
Ok(this.field_size * (offset as usize))
Ok(this.inner_size * (offset as usize))
} else {
Err(LuaError::external("Out of index"))
}
});
methods.add_method("box", |lua, this, table: LuaValue| {
let result = lua.create_userdata(FfiBox::new(this.get_size()))?;
unsafe { this.luavalue_into(lua, 0, &result.get_data_handle()?, table)? };
Ok(result)
});
methods.add_method(
"from",
|lua, this, (userdata, offset): (LuaAnyUserData, Option<isize>)| {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
unsafe { this.luavalue_from(lua, offset, data_handle) }
},
);
methods.add_method(
"into",
|lua, this, (userdata, value, offset): (LuaAnyUserData, LuaValue, Option<isize>)| {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.size) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.is_writable() {
return Err(LuaError::external("Unwritable data handle"));
}
unsafe { this.luavalue_into(lua, offset, data_handle, value) }
},
);
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
let pointer = CPtr::new_from_lua_userdata(lua, &this)?;
Ok(pointer)
});
methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| {
let result = CArr::stringify(lua, &this)?;
Ok(result)
});
}
}

View file

@ -3,14 +3,15 @@ use std::ptr;
use libffi::middle::{Cif, Type};
use mlua::prelude::*;
use super::c_helper::{get_size, get_userdata};
use super::{
association_names::{CALLABLE_CFN, CALLABLE_REF, CFN_ARGS, CFN_RESULT},
c_helper::{get_conv, libffi_type_from_userdata, libffi_type_list_from_table},
c_helper, method_provider,
};
use crate::ffi::{
bit_mask::u8_test_not, ffi_association::set_association, FfiCallable, FfiRef, FfiRefFlag,
NativeArgInfo, NativeData, NativeResultInfo,
bit_mask::u8_test_not,
ffi_association::{get_association, set_association},
FfiCallable, FfiRef, FfiRefFlag, NativeArgInfo, NativeData, NativeResultInfo, NativeSignedness,
NativeSize,
};
// cfn is a type declaration for a function.
@ -29,15 +30,24 @@ use crate::ffi::{
// The name cfn is intentional. This is because any *c_void is
// moved to a Lua function or vice versa.
pub struct CFn {
pub struct CFunc {
cif: Cif,
arg_info_list: Vec<NativeArgInfo>,
result_info: NativeResultInfo,
}
// support: Cfn as function pointer
impl NativeSignedness for CFunc {
fn get_signedness(&self) -> bool {
false
}
}
impl NativeSize for CFunc {
fn get_size(&self) -> usize {
size_of::<*mut ()>()
}
}
impl CFn {
impl CFunc {
pub fn new(
args: Vec<Type>,
ret: Type,
@ -53,26 +63,26 @@ impl CFn {
})
}
pub fn new_from_lua_table<'lua>(
pub fn new_from_table<'lua>(
lua: &'lua Lua,
arg_table: LuaTable,
ret: LuaAnyUserData,
) -> LuaResult<LuaAnyUserData<'lua>> {
let args_types = libffi_type_list_from_table(lua, &arg_table)?;
let ret_type = libffi_type_from_userdata(lua, &ret)?;
let args_types = c_helper::get_middle_type_list(&arg_table)?;
let ret_type = c_helper::get_middle_type(&ret)?;
let arg_len = arg_table.raw_len();
let mut arg_info_list = Vec::<NativeArgInfo>::with_capacity(arg_len);
for index in 0..arg_len {
let userdata = get_userdata(arg_table.raw_get(index + 1)?)?;
let userdata = c_helper::get_userdata(arg_table.raw_get(index + 1)?)?;
arg_info_list.push(NativeArgInfo {
conv: unsafe { get_conv(&userdata)? },
size: get_size(&userdata)?,
conv: unsafe { c_helper::get_conv(&userdata)? },
size: c_helper::get_size(&userdata)?,
});
}
let result_info = NativeResultInfo {
conv: unsafe { get_conv(&ret)? },
size: get_size(&ret)?,
conv: unsafe { c_helper::get_conv(&ret)? },
size: c_helper::get_size(&ret)?,
};
let cfn =
@ -84,17 +94,55 @@ impl CFn {
Ok(cfn)
}
// Stringify for pretty printing like:
// <CFunc( (u8, i32) -> u8 )>
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
let mut result = String::from(" (");
if let (Some(LuaValue::Table(arg_table)), Some(LuaValue::UserData(result_userdata))) = (
get_association(lua, CFN_ARGS, userdata)?,
get_association(lua, CFN_RESULT, userdata)?,
) {
let len = arg_table.raw_len();
for arg_index in 1..=len {
let arg_userdata: LuaAnyUserData = arg_table.raw_get(arg_index)?;
let pretty_formatted = c_helper::pretty_format(lua, &arg_userdata)?;
result.push_str(
(if len == arg_index {
pretty_formatted
} else {
format!("{pretty_formatted}, ")
})
.as_str(),
);
}
result.push_str(
format!(") -> {} ", c_helper::pretty_format(lua, &result_userdata)?,).as_str(),
);
Ok(result)
} else {
Err(LuaError::external("failed to get inner type userdata."))
}
}
}
impl LuaUserData for CFn {
impl LuaUserData for CFunc {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
// Subtype
method_provider::provide_ptr(methods);
method_provider::provide_arr(methods);
// ToString
method_provider::provide_to_string(methods);
// Realize
// methods.add_method("closure", |lua, this, func: LuaFunction| {
// lua.create_userdata(FfiClosure::new(this.cif, userdata))
// })
methods.add_function(
"caller",
"callable",
|lua, (cfn, function_ref): (LuaAnyUserData, LuaAnyUserData)| {
let this = cfn.borrow::<CFn>()?;
let this = cfn.borrow::<CFunc>()?;
if !function_ref.is::<FfiRef>() {
return Err(LuaError::external("argument 0 must be ffiref"));

View file

@ -1,14 +1,94 @@
#![allow(clippy::inline_always)]
use libffi::middle::Type;
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
use mlua::prelude::*;
use super::{
types::{get_ctype_conv, get_ctype_size},
CArr, CPtr, CStruct,
};
use crate::ffi::{ffi_association::get_association, NativeConvert, NativeSize};
use super::{c_type_helper, CArr, CFunc, CPtr, CStruct};
use crate::ffi::{FfiBox, GetNativeData, NativeConvert, NativeSize};
pub mod method_provider {
use super::*;
pub fn provide_to_string<'lua, Target, M>(methods: &mut M)
where
M: LuaUserDataMethods<'lua, Target>,
{
methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| {
stringify(lua, &this)
});
}
pub fn provide_ptr<'lua, Target, M>(methods: &mut M)
where
M: LuaUserDataMethods<'lua, Target>,
{
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
CPtr::from_userdata(lua, &this)
});
}
pub fn provide_arr<'lua, Target, M>(methods: &mut M)
where
M: LuaUserDataMethods<'lua, Target>,
{
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
CArr::from_userdata(lua, &this, length)
});
}
pub fn provide_from<'lua, Target, M>(methods: &mut M)
where
Target: NativeSize + NativeConvert,
M: LuaUserDataMethods<'lua, Target>,
{
methods.add_method(
"from",
|lua, this, (userdata, offset): (LuaAnyUserData, Option<isize>)| {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
unsafe { this.luavalue_from(lua, offset, data_handle) }
},
);
}
pub fn provide_into<'lua, Target, M>(methods: &mut M)
where
Target: NativeSize + NativeConvert,
M: LuaUserDataMethods<'lua, Target>,
{
methods.add_method(
"into",
|lua, this, (userdata, value, offset): (LuaAnyUserData, LuaValue, Option<isize>)| {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.is_writable() {
return Err(LuaError::external("Unwritable data handle"));
}
unsafe { this.luavalue_into(lua, offset, data_handle, value) }
},
);
}
pub fn provide_box<'lua, Target, M>(methods: &mut M)
where
Target: NativeSize + NativeConvert,
M: LuaUserDataMethods<'lua, Target>,
{
methods.add_method("box", |lua, this, table: LuaValue| {
let result = lua.create_userdata(FfiBox::new(this.get_size()))?;
unsafe { this.luavalue_into(lua, 0, &result.get_data_handle()?, table)? };
Ok(result)
});
}
}
pub fn get_userdata(value: LuaValue) -> LuaResult<LuaAnyUserData> {
if let LuaValue::UserData(field_type) = value {
@ -28,14 +108,17 @@ pub fn get_userdata(value: LuaValue) -> LuaResult<LuaAnyUserData> {
pub unsafe fn get_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn NativeConvert> {
if userdata.is::<CStruct>() {
Ok(userdata.to_pointer().cast::<CStruct>() as *const dyn NativeConvert)
} else if userdata.is::<CArr>() {
Ok(userdata.to_pointer().cast::<CArr>() as *const dyn NativeConvert)
} else if userdata.is::<CPtr>() {
Ok(userdata.to_pointer().cast::<CPtr>() as *const dyn NativeConvert)
} else {
get_ctype_conv(userdata)
c_type_helper::get_conv(userdata)
// TODO: struct and more
}
}
pub unsafe fn get_conv_list_from_table(
table: &LuaTable,
) -> LuaResult<Vec<*const dyn NativeConvert>> {
pub unsafe fn get_conv_list(table: &LuaTable) -> LuaResult<Vec<*const dyn NativeConvert>> {
let len: usize = table.raw_len();
let mut conv_list = Vec::<*const dyn NativeConvert>::with_capacity(len);
@ -52,44 +135,21 @@ pub fn get_size(this: &LuaAnyUserData) -> LuaResult<usize> {
Ok(this.borrow::<CStruct>()?.get_size())
} else if this.is::<CArr>() {
Ok(this.borrow::<CArr>()?.get_size())
} else if this.is::<CPtr>() {
Ok(this.borrow::<CPtr>()?.get_size())
} else {
get_ctype_size(this)
c_type_helper::get_size(this)
}
}
// get Vec<libffi_type> from table(array) of c-type userdata
pub fn libffi_type_list_from_table(lua: &Lua, table: &LuaTable) -> LuaResult<Vec<Type>> {
let len: usize = table.raw_len();
let mut fields = Vec::with_capacity(len);
for i in 0..len {
// Test required
let value = table.raw_get(i + 1)?;
if let LuaValue::UserData(field_type) = value {
fields.push(libffi_type_from_userdata(lua, &field_type)?);
} else {
return Err(LuaError::external(format!(
"Unexpected field. CStruct, CType or CArr is required for element but got {}",
value.type_name()
)));
}
}
Ok(fields)
}
// get libffi_type from any c-type userdata
pub fn libffi_type_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<Type> {
pub fn get_middle_type(userdata: &LuaAnyUserData) -> LuaResult<Type> {
if userdata.is::<CStruct>() {
Ok(userdata.borrow::<CStruct>()?.get_type().to_owned())
} else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? {
Ok(t.as_userdata()
.ok_or_else(|| LuaError::external("Failed to get static ctype from userdata"))?
.borrow::<CTypeStatic>()?
.libffi_type
.clone())
Ok(userdata.borrow::<CStruct>()?.get_type())
} else if let Some(middle_type) = c_type_helper::get_middle_type(userdata)? {
Ok(middle_type)
} else if userdata.is::<CArr>() {
Ok(userdata.borrow::<CArr>()?.get_type().to_owned())
Ok(userdata.borrow::<CArr>()?.get_type())
} else if userdata.is::<CPtr>() {
Ok(CPtr::get_type())
} else {
@ -105,59 +165,70 @@ pub fn libffi_type_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaRes
}
}
// get Vec<libffi_type> from table(array) of c-type userdata
pub fn get_middle_type_list(table: &LuaTable) -> LuaResult<Vec<Type>> {
let len: usize = table.raw_len();
let mut fields = Vec::with_capacity(len);
for i in 0..len {
// Test required
let value = table.raw_get(i + 1)?;
if let LuaValue::UserData(field_type) = value {
fields.push(get_middle_type(&field_type)?);
} else {
return Err(LuaError::external(format!(
"Unexpected field. CStruct, CType or CArr is required for element but got {}",
value.type_name()
)));
}
}
Ok(fields)
}
// stringify any c-type userdata (for recursive)
pub fn stringify_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
if userdata.is::<CStruct>() {
let name = CStruct::stringify(lua, userdata)?;
Ok(name)
CStruct::stringify(lua, userdata)
} else if userdata.is::<CArr>() {
let name = CArr::stringify(lua, userdata)?;
Ok(name)
CArr::stringify(lua, userdata)
} else if userdata.is::<CPtr>() {
let name: String = CPtr::stringify(lua, userdata)?;
Ok(name)
// Get CTypeStatic from CType<Any>
} else if let Some(t) = get_association(lua, CTYPE_STATIC, userdata)? {
Ok(String::from(
t.as_userdata()
.ok_or_else(|| LuaError::external("Failed to get static ctype from userdata"))?
.borrow::<CTypeStatic>()?
.name
.unwrap_or("unnamed"),
))
CPtr::stringify(lua, userdata)
} else if userdata.is::<CFunc>() {
CFunc::stringify(lua, userdata)
} else if let Some(name) = c_type_helper::get_name(userdata)? {
Ok(String::from(name))
} else {
Ok(String::from("unnamed"))
Ok(String::from("unknown"))
}
}
// get name tag for any c-type userdata
pub fn tagname_from_userdata(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
pub fn get_tag_name(userdata: &LuaAnyUserData) -> LuaResult<String> {
Ok(if userdata.is::<CStruct>() {
String::from("CStruct")
} else if userdata.is::<CArr>() {
String::from("CArr")
} else if userdata.is::<CPtr>() {
String::from("CPtr")
} else if userdata_is_ctype(lua, userdata)? {
} else if userdata.is::<CFunc>() {
String::from("CFunc")
} else if c_type_helper::is_ctype(userdata) {
String::from("CType")
} else {
String::from("unnamed")
String::from("Unknown")
})
}
pub fn userdata_is_ctype(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<bool> {
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<String> {
if userdata_is_ctype(lua, userdata)? {
stringify_userdata(lua, userdata)
pub fn pretty_format(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
if c_type_helper::is_ctype(userdata) {
stringify(lua, userdata)
} else {
Ok(format!(
"<{}({})>",
tagname_from_userdata(lua, userdata)?,
stringify_userdata(lua, userdata)?
get_tag_name(userdata)?,
stringify(lua, userdata)?
))
}
}

View file

@ -1,15 +1,53 @@
use std::cell::Ref;
use libffi::middle::Type;
use mlua::prelude::*;
use super::{association_names::CPTR_INNER, c_helper::pretty_format_userdata, CArr};
use crate::ffi::ffi_association::{get_association, set_association};
use super::{association_names::CPTR_INNER, c_helper, c_type_helper, method_provider};
use crate::ffi::{
ffi_association::{get_association, set_association},
NativeConvert, NativeData, NativeSignedness, NativeSize,
};
pub struct CPtr();
impl NativeSignedness for CPtr {
fn get_signedness(&self) -> bool {
false
}
}
impl NativeSize for CPtr {
fn get_size(&self) -> usize {
size_of::<*mut ()>()
}
}
impl NativeConvert for CPtr {
// Convert luavalue into data, then write into ptr
unsafe fn luavalue_into<'lua>(
&self,
_lua: &'lua Lua,
_offset: isize,
_data_handle: &Ref<dyn NativeData>,
_value: LuaValue<'lua>,
) -> LuaResult<()> {
Err(LuaError::external("Conversion of pointer is not allowed"))
}
// Read data from ptr, then convert into luavalue
unsafe fn luavalue_from<'lua>(
&self,
_lua: &'lua Lua,
_offset: isize,
_data_handle: &Ref<dyn NativeData>,
) -> LuaResult<LuaValue<'lua>> {
Err(LuaError::external("Conversion of pointer is not allowed"))
}
}
impl CPtr {
// Create pointer type with '.inner' field
// inner can be CArr, CType or CStruct
pub fn new_from_lua_userdata<'lua>(
pub fn from_userdata<'lua>(
lua: &'lua Lua,
inner: &LuaAnyUserData,
) -> LuaResult<LuaAnyUserData<'lua>> {
@ -22,13 +60,13 @@ impl CPtr {
// Stringify CPtr with inner ctype
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
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."))?;
pretty_format_userdata(lua, inner)
if let LuaValue::UserData(inner_userdata) = userdata.get("inner")? {
let pretty_formatted = c_helper::pretty_format(lua, &inner_userdata)?;
Ok(if c_type_helper::is_ctype(&inner_userdata) {
pretty_formatted
} else {
format!(" {pretty_formatted} ")
})
} else {
Err(LuaError::external("failed to get inner type userdata."))
}
@ -51,17 +89,11 @@ impl LuaUserData for CPtr {
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
let pointer = CPtr::new_from_lua_userdata(lua, &this)?;
Ok(pointer)
});
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
let carr = CArr::new_from_lua_userdata(lua, &this, length)?;
Ok(carr)
});
methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| {
let name: Result<String, LuaError> = CPtr::stringify(lua, &this);
Ok(name)
});
// Subtype
method_provider::provide_ptr(methods);
method_provider::provide_arr(methods);
// ToString
method_provider::provide_to_string(methods);
}
}

View file

@ -3,41 +3,34 @@ use std::{cell::Ref, vec::Vec};
use libffi::{low, middle::Type, raw};
use mlua::prelude::*;
use super::{
association_names::CSTRUCT_INNER,
c_helper::{get_conv_list_from_table, libffi_type_list_from_table, pretty_format_userdata},
CArr, CPtr,
};
use super::{association_names::CSTRUCT_INNER, c_helper, method_provider, CArr, CPtr};
use crate::ffi::{
ffi_association::{get_association, set_association},
FfiBox, GetNativeData, NativeConvert, NativeData, NativeSignedness, NativeSize,
FFI_STATUS_NAMES,
NativeConvert, NativeData, NativeSignedness, NativeSize, FFI_STATUS_NAMES,
};
pub struct CStruct {
// libffi_cif: Cif,
// fields: Vec<Type>,
struct_type: Type,
offsets: Vec<usize>,
middle_type: Type,
size: usize,
conv: Vec<*const dyn NativeConvert>,
inner_offset_list: Vec<usize>,
inner_conv_list: Vec<*const dyn NativeConvert>,
}
impl CStruct {
pub fn new(fields: Vec<Type>, conv: Vec<*const dyn NativeConvert>) -> LuaResult<Self> {
pub fn new(
fields: Vec<Type>,
inner_conv_list: Vec<*const dyn NativeConvert>,
) -> LuaResult<Self> {
let len = fields.len();
let mut offsets = Vec::<usize>::with_capacity(len);
let struct_type = Type::structure(fields);
// let struct_type = Type::structure(fields.iter().cloned());
// let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
let mut inner_offset_list = Vec::<usize>::with_capacity(len);
let middle_type = Type::structure(fields);
// Get field offsets with ffi_get_struct_offsets
// let mut offsets = Vec::<usize>::with_capacity(fields.len());
unsafe {
let offset_result: raw::ffi_status = raw::ffi_get_struct_offsets(
low::ffi_abi_FFI_DEFAULT_ABI,
struct_type.as_raw_ptr(),
offsets.as_mut_ptr(),
middle_type.as_raw_ptr(),
inner_offset_list.as_mut_ptr(),
);
if offset_result != raw::ffi_status_FFI_OK {
return Err(LuaError::external(format!(
@ -45,33 +38,31 @@ impl CStruct {
FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[offset_result as usize]
)));
}
offsets.set_len(offsets.capacity());
inner_offset_list.set_len(len);
}
// Get tailing padded size of struct
// See http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html
// In here, using get_ensured_size is waste
let size = unsafe { (*struct_type.as_raw_ptr()).size };
// In here, using get_ensured_size is not required
let size = unsafe { (*middle_type.as_raw_ptr()).size };
Ok(Self {
// libffi_cif: libffi_cfi,
// fields,
struct_type,
offsets,
middle_type,
size,
conv,
inner_offset_list,
inner_conv_list,
})
}
// Create new CStruct UserData with LuaTable.
// Lock and hold table for .inner ref
pub fn new_from_lua_table<'lua>(
pub fn new_from_table<'lua>(
lua: &'lua Lua,
table: LuaTable<'lua>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let cstruct = lua.create_userdata(Self::new(
libffi_type_list_from_table(lua, &table)?,
unsafe { get_conv_list_from_table(&table)? },
c_helper::get_middle_type_list(&table)?,
unsafe { c_helper::get_conv_list(&table)? },
)?)?;
table.set_readonly(true);
@ -79,7 +70,7 @@ impl CStruct {
Ok(cstruct)
}
// Stringify cstruct for pretty printing something like:
// Stringify cstruct for pretty printing like:
// <CStruct( u8, i32, size = 8 )>
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
if let LuaValue::Table(fields) = get_association(lua, CSTRUCT_INNER, userdata)?
@ -88,7 +79,8 @@ impl CStruct {
let mut result = String::from(" ");
for i in 0..fields.raw_len() {
let child: LuaAnyUserData = fields.raw_get(i + 1)?;
result.push_str(pretty_format_userdata(lua, &child)?.as_str());
let pretty_formatted = c_helper::pretty_format(lua, &child)?;
result.push_str(format!("{pretty_formatted}, ").as_str());
}
// size of
@ -103,19 +95,15 @@ impl CStruct {
// Get byte offset of nth field
pub fn offset(&self, index: usize) -> LuaResult<usize> {
let offset = self
.offsets
.inner_offset_list
.get(index)
.ok_or(LuaError::external("Out of index"))?
.to_owned();
Ok(offset)
}
// pub fn get_fields(&self) -> &Vec<Type> {
// &self.fields
// }
pub fn get_type(&self) -> &Type {
&self.struct_type
pub fn get_type(&self) -> Type {
self.middle_type.clone()
}
}
@ -141,7 +129,7 @@ impl NativeConvert for CStruct {
let LuaValue::Table(ref table) = value else {
return Err(LuaError::external("Value is not a table"));
};
for (i, conv) in self.conv.iter().enumerate() {
for (i, conv) in self.inner_conv_list.iter().enumerate() {
let field_offset = self.offset(i)? as isize;
let data: LuaValue = table.get(i + 1)?;
@ -158,8 +146,8 @@ impl NativeConvert for CStruct {
offset: isize,
data_handle: &Ref<dyn NativeData>,
) -> LuaResult<LuaValue<'lua>> {
let table = lua.create_table_with_capacity(self.conv.len(), 0)?;
for (i, conv) in self.conv.iter().enumerate() {
let table = lua.create_table_with_capacity(self.inner_conv_list.len(), 0)?;
for (i, conv) in self.inner_conv_list.iter().enumerate() {
let field_offset = self.offset(i)? as isize;
table.set(
i + 1,
@ -177,6 +165,18 @@ impl LuaUserData for CStruct {
fields.add_field_method_get("size", |_, this| Ok(this.get_size()));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
// Subtype
method_provider::provide_ptr(methods);
method_provider::provide_arr(methods);
// ToString
method_provider::provide_to_string(methods);
// Realize
method_provider::provide_box(methods);
method_provider::provide_from(methods);
method_provider::provide_into(methods);
methods.add_method("offset", |_, this, index: usize| {
let offset = this.offset(index)?;
Ok(offset)
@ -193,55 +193,5 @@ impl LuaUserData for CStruct {
Err(LuaError::external("Failed to read field table"))
}
});
methods.add_method("box", |lua, this, table: LuaValue| {
let result = lua.create_userdata(FfiBox::new(this.get_size()))?;
unsafe { this.luavalue_into(lua, 0, &result.get_data_handle()?, table)? };
Ok(result)
});
methods.add_method(
"from",
|lua, this, (userdata, offset): (LuaAnyUserData, Option<isize>)| {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.is_readable() {
return Err(LuaError::external("Unreadable data handle"));
}
unsafe { this.luavalue_from(lua, offset, data_handle) }
},
);
methods.add_method(
"into",
|lua, this, (userdata, value, offset): (LuaAnyUserData, LuaValue, Option<isize>)| {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.is_writable() {
return Err(LuaError::external("Unwritable data handle"));
}
unsafe { this.luavalue_into(lua, offset, data_handle, value) }
},
);
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
let pointer = CPtr::new_from_lua_userdata(lua, &this)?;
Ok(pointer)
});
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
let carr = CArr::new_from_lua_userdata(lua, &this, length)?;
Ok(carr)
});
methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| {
let result = CStruct::stringify(lua, &this)?;
Ok(result)
});
}
}

View file

@ -7,8 +7,11 @@ use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
use mlua::prelude::*;
use super::{CArr, CPtr};
use crate::ffi::{FfiBox, GetNativeData, NativeConvert, NativeData, NativeSignedness, NativeSize};
use crate::libffi_helper::get_ensured_size;
use crate::{
c::method_provider,
ffi::{GetNativeData, NativeConvert, NativeData, NativeSignedness, NativeSize},
libffi_helper::get_ensured_size,
};
// Cast native data
pub trait CTypeCast {
@ -38,34 +41,33 @@ pub trait CTypeCast {
}
}
pub struct CType<T> {
middle_type: Type,
size: usize,
name: &'static str,
_phantom: PhantomData<T>,
}
impl<T> NativeSize for CType<T> {
fn get_size(&self) -> usize {
self.size
}
}
pub struct CType<T> {
// for ffi_ptrarray_to_raw?
// libffi_cif: Cif,
libffi_type: Type,
size: usize,
name: Option<&'static str>,
_phantom: PhantomData<T>,
}
impl<T> CType<T>
where
T: 'static,
Self: CTypeCast + NativeSignedness + NativeConvert,
Self: CTypeCast + NativeSignedness + NativeConvert + NativeSize,
{
pub fn new_with_libffi_type<'lua>(
lua: &'lua Lua,
libffi_type: Type,
name: Option<&'static str>,
name: &'static str,
) -> LuaResult<LuaAnyUserData<'lua>> {
let size = get_ensured_size(libffi_type.as_raw_ptr())?;
let ctype = Self {
libffi_type,
middle_type: libffi_type,
size,
name,
_phantom: PhantomData,
@ -75,18 +77,19 @@ where
Ok(userdata)
}
pub fn stringify(&self) -> &str {
match self.name {
Some(t) => t,
None => "unnamed",
}
pub fn get_name(&self) -> &'static str {
self.name
}
pub fn get_type(&self) -> Type {
self.middle_type.clone()
}
}
impl<T> LuaUserData for CType<T>
where
T: 'static,
Self: CTypeCast + NativeSignedness + NativeConvert,
Self: CTypeCast + NativeSignedness + NativeConvert + NativeSize,
{
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("size", |_, this| Ok(this.get_size()));
@ -95,58 +98,18 @@ where
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
CPtr::new_from_lua_userdata(lua, &this)
});
methods.add_method("box", |lua, this, value: LuaValue| {
let result = lua.create_userdata(FfiBox::new(this.get_size()))?;
// Subtype
method_provider::provide_ptr(methods);
method_provider::provide_arr(methods);
unsafe { this.luavalue_into(lua, 0, &result.get_data_handle()?, value)? };
Ok(result)
});
methods.add_function(
"from",
|lua, (this, userdata, offset): (LuaAnyUserData, LuaAnyUserData, Option<isize>)| {
let ctype = this.borrow::<Self>()?;
let offset = offset.unwrap_or(0);
// ToString
method_provider::provide_to_string(methods);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, ctype.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.is_readable() {
return Err(LuaError::external("Unreadable data handle"));
}
// Realize
method_provider::provide_box(methods);
method_provider::provide_from(methods);
method_provider::provide_into(methods);
unsafe { ctype.luavalue_from(lua, offset, data_handle) }
},
);
methods.add_function(
"into",
|lua,
(this, userdata, value, offset): (
LuaAnyUserData,
LuaAnyUserData,
LuaValue,
Option<isize>,
)| {
let ctype = this.borrow::<Self>()?;
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, ctype.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.is_writable() {
return Err(LuaError::external("Unwritable data handle"));
}
unsafe { ctype.luavalue_into(lua, offset, data_handle, value) }
},
);
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
CArr::new_from_lua_userdata(lua, &this, length)
});
methods.add_function(
"cast",
|_,
@ -164,8 +127,5 @@ where
)
},
);
methods.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| {
lua.create_string(this.stringify())
});
}
}

View file

@ -1,5 +1,5 @@
mod c_arr;
mod c_fn;
mod c_func;
pub mod c_helper;
mod c_ptr;
mod c_string;
@ -9,14 +9,14 @@ mod types;
pub use self::{
c_arr::CArr,
c_fn::CFn,
c_func::CFunc,
c_helper::method_provider,
c_ptr::CPtr,
c_struct::CStruct,
c_type::{CType, CTypeCast},
types::{c_type_helper, export_ctypes},
};
pub use types::export_ctypes;
// Named registry table names
mod association_names {
pub const CPTR_INNER: &str = "__cptr_inner";

View file

@ -30,7 +30,7 @@ macro_rules! create_ctypes {
($lua:ident, $(( $name:expr, $rust_type:ty, $libffi_type:expr ),)* ) => {
Ok(vec![$((
$name,
CType::<$rust_type>::new_with_libffi_type($lua, $libffi_type, Some($name))?,
CType::<$rust_type>::new_with_libffi_type($lua, $libffi_type, $name)?,
),)*])
};
}
@ -118,58 +118,80 @@ where
}
}
// To prevent drop NativeConvert, we must use ffi_association to ensure children keep alive
macro_rules! define_get_conv {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok($userdata.to_pointer().cast::<CType<$rust_type>>() as *const dyn NativeConvert)
} else )* {
Err(LuaError::external("Unexpected type"))
}
};
}
pub fn get_ctype_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn NativeConvert> {
define_get_conv!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
pub mod c_type_helper {
use super::*;
// Get size of ctype (not includes struct, arr, ... only CType<*>)
macro_rules! define_get_size {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok($userdata.borrow::<CType<$rust_type>>()?.get_size())
} else )* {
Err(LuaError::external("Unexpected type"))
}
};
}
pub fn get_ctype_size(userdata: &LuaAnyUserData) -> LuaResult<usize> {
define_get_size!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
// To prevent drop NativeConvert, we must use ffi_association to ensure children keep alive
macro_rules! define_get_conv {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok($userdata.to_pointer().cast::<CType<$rust_type>>() as *const dyn NativeConvert)
} else )* {
Err(LuaError::external("Unexpected type"))
}
};
}
#[inline]
pub fn get_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn NativeConvert> {
define_get_conv!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
// Get name of ctype
macro_rules! define_get_name {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok($userdata.borrow::<CType<$rust_type>>()?.stringify())
} else )* {
Err(LuaError::external("Unexpected type"))
}
};
}
pub fn get_ctype_name(userdata: &LuaAnyUserData) -> LuaResult<&str> {
define_get_name!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
// Get size of ctype (not includes struct, arr, ... only CType<*>)
macro_rules! define_get_size {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok($userdata.borrow::<CType<$rust_type>>()?.get_size())
} else )* {
Err(LuaError::external("Unexpected type"))
}
};
}
#[inline]
pub fn get_size(userdata: &LuaAnyUserData) -> LuaResult<usize> {
define_get_size!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
// Get libffi_type of ctype
macro_rules! define_get_libffi_type {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok($userdata.borrow::<CType<$rust_type>>()?.get_size())
} else )* {
Err(LuaError::external("Unexpected type"))
}
};
}
pub fn get_ctype_libffi_type(userdata: &LuaAnyUserData) -> LuaResult<usize> {
define_get_libffi_type!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
// Get name of ctype
macro_rules! define_get_name {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok(Some($userdata.borrow::<CType<$rust_type>>()?.get_name()))
} else )* {
Ok(None)
}
};
}
#[inline]
pub fn get_name(userdata: &LuaAnyUserData) -> LuaResult<Option<&'static str>> {
define_get_name!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
// Get libffi_type of ctype
macro_rules! define_get_middle_type {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
Ok(Some($userdata.borrow::<CType<$rust_type>>()?.get_type()))
} else )* {
Ok(None)
}
};
}
#[inline]
pub fn get_middle_type(userdata: &LuaAnyUserData) -> LuaResult<Option<Type>> {
define_get_middle_type!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
macro_rules! define_is_ctype {
($userdata:ident, $( $rust_type:ty )*) => {
$( if $userdata.is::<CType<$rust_type>>() {
true
} else )* {
false
}
};
}
#[inline]
pub fn is_ctype(userdata: &LuaAnyUserData) -> bool {
define_is_ctype!(userdata, u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 usize isize)
}
}

View file

@ -1,5 +1,3 @@
#![allow(clippy::inline_always)]
use mlua::prelude::*;
// This is a small library that helps you set the dependencies of data in Lua.
@ -30,7 +28,7 @@ use mlua::prelude::*;
// use a table with a different name.
// You can delete the relationship by changing 'associated' to nil
#[inline(always)]
#[inline]
pub fn set_association<'lua, T, U>(
lua: &'lua Lua,
regname: &str,
@ -62,7 +60,7 @@ where
// returns the Lua value that 'value' keeps.
// If there is no table in registry, it returns None.
// If there is no value in table, it returns LuaNil.
#[inline(always)]
#[inline]
pub fn get_association<'lua, T>(
lua: &'lua Lua,
regname: &str,
@ -81,7 +79,7 @@ where
// Allows reading of registry tables for debugging.
// This helps keep track of data being gc'd.
// However, for security and safety reasons,
// this will not be allowed unless it is a debug build.
// this will not be allowed unless debug build.
#[cfg(debug_assertions)]
pub fn get_table<'lua>(lua: &'lua Lua, regname: &str) -> LuaResult<Option<LuaTable<'lua>>> {
match lua.named_registry_value::<LuaValue>(regname)? {

View file

@ -1,5 +1,3 @@
use core::ffi::c_void;
use dlopen2::raw::Library;
use mlua::prelude::*;
@ -16,15 +14,6 @@ const LIB_REF_FLAGS: u8 = FfiRefFlag::Offsetable.value()
pub struct FfiLib(Library);
// COMMENT HERE
// For convenience, it would be nice to provide a way to get
// symbols from a table with type and field names specified.
// But right now, we are starting from the lowest level, so we will make it later.
// I wanted to provide something like cdef,
// but that is beyond the scope of lune's support.
// Higher-level bindings for convenience are much preferable written in Lua.
impl FfiLib {
pub fn new(libname: String) -> LuaResult<Self> {
match Library::open(libname) {
@ -41,21 +30,16 @@ impl FfiLib {
let lib = this.borrow::<FfiLib>()?;
let sym = unsafe {
lib.0
.symbol::<*const c_void>(name.as_str())
.symbol::<*const ()>(name.as_str())
.map_err(|err| LuaError::external(format!("{err}")))?
};
let ptr = sym.cast::<()>().cast_mut();
// unsafe {
// let f = transmute::<*mut (), unsafe extern "C" fn(i32, i32) -> i32>(ptr);
// dbg!(f(1, 2));
// }
let ffi_ref =
lua.create_userdata(FfiRef::new(sym.cast_mut(), LIB_REF_FLAGS, UNSIZED_BOUNDS))?;
let luasym = lua.create_userdata(FfiRef::new(ptr, LIB_REF_FLAGS, UNSIZED_BOUNDS))?;
set_association(lua, SYM_INNER, &ffi_ref, &this)?;
set_association(lua, SYM_INNER, &luasym, &this)?;
Ok(luasym)
Ok(ffi_ref)
}
}

View file

@ -1,20 +0,0 @@
use mlua::prelude::*;
// This is raw data coming from outside.
// Users must convert it to a Lua value, reference, or box to use it.
// The biggest reason for providing this is to allow the user to
// decide whether to move the data to a heap that Lua can manage (box),
// move it directly to Lua's data, or think of it as a pointer.
// This will help you distinguish between safe operations and
// relatively insecure operations, and help ensure that as little
// data copy as possible occurs, while allowing you to do little restrictions.
pub struct FfiRaw(*const ());
impl FfiRaw {
pub fn new(pointer: *const ()) -> Self {
Self(pointer)
}
}
impl LuaUserData for FfiRaw {}

View file

@ -4,7 +4,6 @@ mod ffi_callable;
mod ffi_closure;
mod ffi_lib;
mod ffi_native;
mod ffi_raw;
mod ffi_ref;
use mlua::prelude::*;

View file

@ -9,7 +9,7 @@ mod ffi;
mod libffi_helper;
use crate::{
c::{export_ctypes, CFn, CStruct},
c::{export_ctypes, CFunc, CStruct},
ffi::{create_nullptr, is_integer, FfiBox, FfiLib},
};
@ -27,13 +27,13 @@ pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
.with_function("box", |_lua, size: usize| Ok(FfiBox::new(size)))?
.with_function("open", |_lua, name: String| FfiLib::new(name))?
.with_function("structInfo", |lua, types: LuaTable| {
CStruct::new_from_lua_table(lua, types)
CStruct::new_from_table(lua, types)
})?
.with_function("uninitRef", |_lua, ()| Ok(FfiRef::new_uninit()))?
.with_function("isInteger", |_lua, num: LuaValue| Ok(is_integer(num)))?
.with_function(
"funcInfo",
|lua, (args, ret): (LuaTable, LuaAnyUserData)| CFn::new_from_lua_table(lua, args, ret),
|lua, (args, ret): (LuaTable, LuaAnyUserData)| CFunc::new_from_table(lua, args, ret),
)?;
#[cfg(debug_assertions)]

12
print-ignore-me.luau Normal file
View file

@ -0,0 +1,12 @@
local ffi = require("@lune/ffi")
print(ffi.int)
print(ffi.int:ptr())
print(ffi.int:arr(5):ptr())
print(ffi.int:arr(5))
print(ffi.funcInfo({ ffi.int }, ffi.int))
print(ffi.funcInfo({ ffi.int, ffi.double }, ffi.int:ptr()))
print(ffi.funcInfo({ ffi.int, ffi.double }, ffi.int:ptr():ptr()))
print(ffi.structInfo({ ffi.int, ffi.char }))

View file

@ -0,0 +1,29 @@
local ffi = require("@lune/ffi")
assert(typeof(ffi.int) == "CType")
assert(tostring(ffi.int) == "int")
assert(typeof(ffi.int:ptr()) == "CPtr")
assert(tostring(ffi.int:ptr()) == "int")
assert(tostring(ffi.int:arr(5):ptr()) == " <CArr( int, length = 5 )> ")
assert(typeof(ffi.int:arr(5)) == "CArr")
assert(tostring(ffi.int:arr(5)) == " int, length = 5 ")
assert(tostring(ffi.int:ptr():arr(5)) == " <CPtr(int)>, length = 5 ")
assert(typeof(ffi.funcInfo({ ffi.int }, ffi.int)) == "CFunc")
assert(tostring(ffi.funcInfo({ ffi.int }, ffi.int)) == " (int) -> int ")
assert(tostring(ffi.funcInfo({ ffi.int, ffi.double }, ffi.int)) == " (int, double) -> int ")
assert(tostring(ffi.funcInfo({ ffi.int:ptr() }, ffi.int)) == " (<CPtr(int)>) -> int ")
assert(tostring(ffi.funcInfo({ ffi.int }, ffi.int:ptr())) == " (int) -> <CPtr(int)> ")
assert(tostring(ffi.funcInfo({ ffi.int:ptr() }, ffi.int:ptr())) == " (<CPtr(int)>) -> <CPtr(int)> ")
assert(
tostring(ffi.funcInfo({ ffi.int:ptr(), ffi.int:ptr() }, ffi.int:ptr()))
== " (<CPtr(int)>, <CPtr(int)>) -> <CPtr(int)> "
)
assert(typeof(ffi.structInfo({ ffi.int, ffi.char })) == "CStruct")
assert(
tostring(ffi.structInfo({ ffi.int, ffi.char:ptr() }))
== ` int, <CPtr(char)>, size = {ffi.structInfo({ ffi.int, ffi.char:ptr() }).size} `
)

View file

@ -1,15 +0,0 @@
--!nocheck
--!nolint
local ffi = require("@lune/ffi")
local box = ffi.box(ffi.i32.size)
local ref = box:ref()
local wt = setmetatable({}, { __mode = "v" })
wt[1] = box
box = nll
collectgarbage("collect")
assert(wt[1] ~= nil, "ref hold box failed")