mirror of
https://github.com/lune-org/lune.git
synced 2025-04-03 01:50:55 +01:00
Merge 8f248ff624
into 5d1401cdf6
This commit is contained in:
commit
e307b45c47
96 changed files with 6684 additions and 8 deletions
4
.gitattributes
vendored
4
.gitattributes
vendored
|
@ -6,3 +6,7 @@
|
|||
|
||||
# Ensure all txt files within tests use LF
|
||||
tests/**/*.txt eol=lf
|
||||
|
||||
# Remove test c and typescript from language list
|
||||
tests/ffi/**/*.c linguist-vendored
|
||||
tests/ffi/**/*.ts linguist-vendored
|
||||
|
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -22,6 +22,15 @@ luneDocs.json
|
|||
luneTypes.d.luau
|
||||
|
||||
# Files generated by runtime or build scripts
|
||||
|
||||
scripts/brick_color.rs
|
||||
scripts/font_enum_map.rs
|
||||
scripts/physical_properties_enum_map.rs
|
||||
|
||||
# Files generated by tests
|
||||
|
||||
/tests/ffi/**/*.so
|
||||
|
||||
# Core dump file
|
||||
|
||||
/core
|
||||
|
|
115
Cargo.lock
generated
115
Cargo.lock
generated
|
@ -787,6 +787,29 @@ dependencies = [
|
|||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6"
|
||||
dependencies = [
|
||||
"dlopen2_derive",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2_derive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
|
@ -1422,9 +1445,28 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.160"
|
||||
version = "0.2.162"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
|
||||
[[package]]
|
||||
name = "libffi"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libffi-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libffi-sys"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
@ -1557,6 +1599,7 @@ name = "lune-std"
|
|||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"lune-std-datetime",
|
||||
"lune-std-ffi",
|
||||
"lune-std-fs",
|
||||
"lune-std-luau",
|
||||
"lune-std-net",
|
||||
|
@ -1585,6 +1628,19 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lune-std-ffi"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"dlopen2",
|
||||
"libc",
|
||||
"libffi",
|
||||
"lune-utils",
|
||||
"mlua",
|
||||
"mlua-sys",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lune-std-fs"
|
||||
version = "0.1.2"
|
||||
|
@ -1880,6 +1936,39 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
|
@ -1895,6 +1984,28 @@ 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.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
|
|
@ -15,6 +15,7 @@ members = [
|
|||
"crates/lune-std-serde",
|
||||
"crates/lune-std-stdio",
|
||||
"crates/lune-std-task",
|
||||
"crates/lune-std-ffi",
|
||||
"crates/lune-utils",
|
||||
"crates/mlua-luau-scheduler",
|
||||
]
|
||||
|
|
24
crates/lune-std-ffi/Cargo.toml
Normal file
24
crates/lune-std-ffi/Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "lune-std-ffi"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/lune-org/lune"
|
||||
description = "Lune standard library - FFI"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.9", features = ["luau"] }
|
||||
mlua-sys = { version = "0.6.2", features = ["luau"] }
|
||||
num = "0.4.3"
|
||||
dlopen2 = "0.7.0"
|
||||
libc = "0.2.162"
|
||||
|
||||
libffi = "3.2.0"
|
||||
|
||||
lune-utils = { version = "0.1.3", path = "../lune-utils" }
|
146
crates/lune-std-ffi/README.md
Normal file
146
crates/lune-std-ffi/README.md
Normal file
|
@ -0,0 +1,146 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
|
||||
# `lune-std-ffi`
|
||||
|
||||
## Tests & Benchmarks
|
||||
|
||||
See [tests/ffi](../../tests/ffi/README.md)
|
||||
|
||||
## TODO
|
||||
|
||||
- [CString](./src/c/string_info.rs)
|
||||
- Add buffer as owned data support
|
||||
- Add math operation for numeric types
|
||||
> Provide related methods: `CTypeInfo:add(target, from1, from2, ...)` and `:sub` `:mul` `:div` `:mod` `:pow` `:max` `:min` `:gt` `:lt`
|
||||
> Luau cannot handle f64, i64 or i128, so we should provide math operation for it
|
||||
- Add bit operation for box/ref
|
||||
> Luau only supports 32bit bit operations
|
||||
- Add wchar and wstring support
|
||||
> For windows API support
|
||||
- Add varargs support
|
||||
- Array argument in cfn
|
||||
- [More box/ref methods](./src/data/helper.rs)
|
||||
- writeString
|
||||
- readString
|
||||
- writeBase64
|
||||
- readBase64
|
||||
|
||||
## Code structure
|
||||
|
||||
### /c
|
||||
|
||||
Define C-ABI type information and provide conversion and casting
|
||||
|
||||
**Structs:** C ABI type informations
|
||||
|
||||
- [**Struct `CArrInfo`:**](./src/c/arr_info.rs) Represents C Array type
|
||||
- [**Struct `CPtrInfo`:**](./src/c/ptr_info.rs) Represents C Pointer type
|
||||
- [**Struct `CFnInfo`:**](./src/c/fn_info.rs) Represents C Function signature
|
||||
> provide `CallableData` and `ClosureData` creator
|
||||
- [**Struct `CStructInfo`:**](./src/c/struct_info.rs) Represents C Struct type
|
||||
- [**Struct `CTypeInfo<T>`:**](./src/c/type_info.rs) Represents C type, extended in `/c/types`
|
||||
|
||||
<details><summary><a href="./src/c/helper.rs"><strong>Mod <code>helper.rs</code>:</strong></a> C ABI type helper</summary>
|
||||
|
||||
- **Function `get_conv`, `get_conv_list`:**
|
||||
get `FfiConvert` from userdata (CStruct, CArr, CPtr, CTypes)
|
||||
- **Function `get_middle_type`, `get_middle_type_list`:**
|
||||
get **`libffi::middle::Type`:** from userdata (CFn, CStruct, CArr, CPtr, CTypes)
|
||||
- **Function `get_size`:**
|
||||
get size from userdata
|
||||
- **Function `has_void`:**
|
||||
check table has void type
|
||||
- **Function `stringify`:**
|
||||
stringify any type userdata
|
||||
- **Function `get_name`:**
|
||||
get type name from ctype userdata, used for pretty-print
|
||||
- **Function `is_ctype`:** check userdata is ctype
|
||||
- **Mod `method_provider`:** provide common userdata method implements
|
||||
|
||||
</details>
|
||||
|
||||
#### /c/types
|
||||
|
||||
Export fixed-size source time known types and non-fixed compile time known types
|
||||
mod.rs implememts type-casting for all CTypes
|
||||
|
||||
<details><summary><a href="./src/c/types/mod.rs"><strong>Mod <code>ctype_helper</code>:</strong></a> CTypeInfo helper</summary>
|
||||
|
||||
- **Function `get_conv`:**
|
||||
get `FfiConvert` from ctype userdata, used for struct and array conversion
|
||||
- **Function `get_middle_type`:**
|
||||
get **`libffi::middle::Type`:** from ctype userdata
|
||||
- **Function `get_size`:**
|
||||
get size from ctype userdata
|
||||
- **Function `get_name`:**
|
||||
get type name from ctype userdata, used for pretty-print
|
||||
- **Function `is_ctype`:** check userdata is ctype
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### /data
|
||||
|
||||
**Structs:** Provide memory userdata
|
||||
|
||||
- [**Struct `BoxData`:**](./src/data/box_data/mod.rs) A heap allocated memory with user definable lifetime
|
||||
- [**Struct `LibData`:**](./src/data/lib_data.rs) A dynamic opened library
|
||||
- [**Struct `RefData`:**](./src/data/ref_data/mod.rs) A reference that can be used for receiving return data from external function or pass pointer arguments
|
||||
|
||||
**Structs:** Provide function(pointer) userdata
|
||||
|
||||
- [**Struct `CallableData`:**](./src/data/callable_data.rs) A callable function, which can be created from function pointer
|
||||
- [**Struct `ClosureData`:**](./src/data/closure_data.rs) A closure pointer, which can be created from lua function and can be used for callback
|
||||
|
||||
---
|
||||
|
||||
### /ffi
|
||||
|
||||
**Traits:** Provide ABI shared common type information trait
|
||||
|
||||
- **Trait `FfiSize`**
|
||||
- **Trait `FfiSignedness`**
|
||||
|
||||
<ul><li><details><summary><strong>Trait <code>FfiConvert</code>:</strong> Provide methods for read LuaValue from FfiData or write LuaValue into FfiData</summary>
|
||||
|
||||
- **Method `value_into_data`:** set data with lua value
|
||||
- **Method `value_from_data`:** get lua value from data
|
||||
- **Method `copy_data`:** copy sized data into another data
|
||||
- **Method `stringify_data`:** stringify data with specific type
|
||||
|
||||
</details></li></ul>
|
||||
|
||||
**Structs:** Provide call information
|
||||
|
||||
- **Struct `FfiArg`:** Used for argument boundary checking and callback argument ref flag
|
||||
- **Struct `FfiResult`:** Used for result boundary checking
|
||||
|
||||
<details><summary><strong>Trait <code>FfiData</code>:</strong> Provide common data handle, including methods below</summary>
|
||||
|
||||
- **Method `check_inner_boundary`:** check boundary with offset and size
|
||||
- **Method `get_inner_pointer`:** returns raw pointer `*mut ()`
|
||||
- **Method `is_writable`**
|
||||
- **Method `is_readable`**
|
||||
- **Method `copy_from`** copy data from another data
|
||||
|
||||
</details>
|
||||
|
||||
> Note: `GetFfiData` trait in `data/mod.rs` provides `(LuaValue | LuaAnyUserData).get_data_handle() -> FfiData` method
|
||||
|
||||
**Mods:** Provide common helper functions
|
||||
|
||||
- [**Mod `association.rs`:**](./src/ffi/association.rs) GC utility, used for inner, ret and arg type holding in subtype
|
||||
- [**Mod `bit_mask.rs`:**](./src/ffi/bit_mask.rs) u8 bitfield helper
|
||||
- [**Mod `cast.rs`:**](./src/ffi/cast.rs) num cast library wrapper
|
||||
- **Function `num_cast<From, Into>(from: FfiData, from: FfiData)`:**
|
||||
Cast number type value inno another number type
|
||||
|
||||
<ul><li><details><summary><a href="./src/c/struct_info.rs"><strong>Mod <code>libffi_helper.rs</code>:</strong></a> libffi library helper</summary>
|
||||
|
||||
- **Const `FFI_STATUS_NAMES`:** Stringify `ffi_status`
|
||||
- **Function `get_ensured_size`:** Returns ensured size of `ffi_type`
|
||||
- **Const `SIZE_OF_POINTER`:** Platform specific pointer size (Compile time known)
|
||||
- **Function `ffi_status_assert`:** Convert `ffi_status` to `LuaResult<()>`
|
||||
|
||||
</details></li></ul>
|
180
crates/lune-std-ffi/src/c/arr_info.rs
Normal file
180
crates/lune-std-ffi/src/c/arr_info.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use libffi::middle::Type;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{association_names::CARR_INNER, helper, method_provider};
|
||||
use crate::ffi::{association, libffi_helper::get_ensured_size, FfiConvert, FfiData, FfiSize};
|
||||
|
||||
// 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.
|
||||
// Multidimensional arrays can be implemented
|
||||
// because they are a series of transcribed one-dimensional arrays. (flatten)
|
||||
// We can simply provide array type with struct.
|
||||
// See: https://stackoverflow.com/a/43525176
|
||||
pub struct CArrInfo {
|
||||
struct_type: Type,
|
||||
length: usize,
|
||||
size: usize,
|
||||
inner_size: usize,
|
||||
inner_conv: *const dyn FfiConvert,
|
||||
}
|
||||
|
||||
impl CArrInfo {
|
||||
pub fn new(
|
||||
element_type: Type,
|
||||
length: usize,
|
||||
inner_conv: *const dyn FfiConvert,
|
||||
) -> LuaResult<Self> {
|
||||
let inner_size = get_ensured_size(element_type.as_raw_ptr())?;
|
||||
let struct_type = Type::structure(vec![element_type.clone(); length]);
|
||||
|
||||
Ok(Self {
|
||||
struct_type,
|
||||
length,
|
||||
size: inner_size * length,
|
||||
inner_size,
|
||||
inner_conv,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_userdata<'lua>(
|
||||
lua: &'lua Lua,
|
||||
type_userdata: &LuaAnyUserData<'lua>,
|
||||
length: usize,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let fields = helper::get_middle_type(type_userdata)?;
|
||||
let conv = unsafe { helper::get_conv(type_userdata)? };
|
||||
let carr = lua.create_userdata(Self::new(fields, length, conv)?)?;
|
||||
|
||||
association::set(lua, CARR_INNER, &carr, type_userdata)?;
|
||||
Ok(carr)
|
||||
}
|
||||
|
||||
pub fn get_length(&self) -> usize {
|
||||
self.length
|
||||
}
|
||||
|
||||
pub fn get_middle_type(&self) -> Type {
|
||||
self.struct_type.clone()
|
||||
}
|
||||
|
||||
// Stringify for pretty-print
|
||||
// ex: <CArr( u8, length = 8 )>
|
||||
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
|
||||
let this = userdata.borrow::<CArrInfo>()?;
|
||||
if let Some(LuaValue::UserData(inner_userdata)) =
|
||||
association::get(lua, CARR_INNER, userdata)?
|
||||
{
|
||||
Ok(format!(
|
||||
" {}, length = {} ",
|
||||
helper::pretty_format(lua, &inner_userdata)?,
|
||||
this.length,
|
||||
))
|
||||
} else {
|
||||
Err(LuaError::external("Failed to retrieve inner type"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiSize for CArrInfo {
|
||||
fn get_size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CArrInfo {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let LuaValue::Table(ref table) = value else {
|
||||
return Err(LuaError::external("Value is not a table"));
|
||||
};
|
||||
for i in 0..self.length {
|
||||
let field_offset = (i * self.inner_size) as isize;
|
||||
let data: LuaValue = table.get(i + 1)?;
|
||||
|
||||
self.inner_conv.as_ref().unwrap().value_into_data(
|
||||
lua,
|
||||
field_offset + offset,
|
||||
data_handle,
|
||||
data,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let table = lua.create_table_with_capacity(self.length, 0)?;
|
||||
for i in 0..self.length {
|
||||
let field_offset = (i * self.inner_size) as isize;
|
||||
table.set(
|
||||
i + 1,
|
||||
self.inner_conv.as_ref().unwrap().value_from_data(
|
||||
lua,
|
||||
field_offset + offset,
|
||||
data_handle,
|
||||
)?,
|
||||
)?;
|
||||
}
|
||||
Ok(LuaValue::Table(table))
|
||||
}
|
||||
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
dst.get_inner_pointer().byte_offset(dst_offset).copy_from(
|
||||
src.get_inner_pointer().byte_offset(src_offset),
|
||||
self.get_size(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CArrInfo {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("size", |_lua, this| Ok(this.get_size()));
|
||||
fields.add_field_method_get("length", |_lua, this| Ok(this.get_length()));
|
||||
fields.add_field_function_get("inner", |lua, this: LuaAnyUserData| {
|
||||
association::get(lua, CARR_INNER, this)?
|
||||
.ok_or_else(|| LuaError::external("Failed to retrieve inner field"))
|
||||
});
|
||||
}
|
||||
|
||||
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_read_data(methods);
|
||||
method_provider::provide_write_data(methods);
|
||||
method_provider::provide_copy_data(methods);
|
||||
|
||||
methods.add_method("offset", |_lua, this, offset: isize| {
|
||||
if this.length > (offset as usize) && offset >= 0 {
|
||||
Ok(this.inner_size * (offset as usize))
|
||||
} else {
|
||||
Err(LuaError::external("Out of index"))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
218
crates/lune-std-ffi/src/c/fn_info.rs
Normal file
218
crates/lune-std-ffi/src/c/fn_info.rs
Normal file
|
@ -0,0 +1,218 @@
|
|||
use libffi::middle::{Cif, Type};
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{
|
||||
association_names::{
|
||||
CALLABLE_CFN, CALLABLE_REF, CFN_ARGS, CFN_RESULT, CLOSURE_CFN, CLOSURE_FUNC,
|
||||
},
|
||||
ctype_helper::is_ctype,
|
||||
helper, method_provider, CArrInfo, CPtrInfo, CStructInfo,
|
||||
};
|
||||
use crate::{
|
||||
data::{CallableData, ClosureData, RefData, RefFlag},
|
||||
ffi::{
|
||||
association, bit_field::*, libffi_helper::SIZE_OF_POINTER, FfiArg, FfiData, FfiResult,
|
||||
FfiSignedness, FfiSize,
|
||||
},
|
||||
};
|
||||
|
||||
// Function pointer type
|
||||
pub struct CFnInfo {
|
||||
cif: Cif,
|
||||
arg_info_list: Vec<FfiArg>,
|
||||
result_info: FfiResult,
|
||||
}
|
||||
|
||||
impl FfiSignedness for CFnInfo {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiSize for CFnInfo {
|
||||
fn get_size(&self) -> usize {
|
||||
SIZE_OF_POINTER
|
||||
}
|
||||
}
|
||||
|
||||
const CALLBACK_ARG_REF_FLAG_TYPE: u8 = RefFlag::Readable.value();
|
||||
const CALLBACK_ARG_REF_FLAG_PTR: u8 = RefFlag::Dereferenceable.value() | RefFlag::Readable.value();
|
||||
const CALLBACK_ARG_REF_FLAG_ARR: u8 = RefFlag::Readable.value() | RefFlag::Offsetable.value();
|
||||
const CALLBACK_ARG_REF_FLAG_STRUCT: u8 = RefFlag::Readable.value() | RefFlag::Offsetable.value();
|
||||
const CALLBACK_ARG_REF_FLAG_CFN: u8 = RefFlag::Function.value();
|
||||
|
||||
fn create_arg_info(userdata: &LuaAnyUserData) -> LuaResult<FfiArg> {
|
||||
let callback_ref_flag = if is_ctype(userdata) {
|
||||
CALLBACK_ARG_REF_FLAG_TYPE
|
||||
} else if userdata.is::<CPtrInfo>() {
|
||||
CALLBACK_ARG_REF_FLAG_PTR
|
||||
} else if userdata.is::<CArrInfo>() {
|
||||
CALLBACK_ARG_REF_FLAG_ARR
|
||||
} else if userdata.is::<CStructInfo>() {
|
||||
CALLBACK_ARG_REF_FLAG_STRUCT
|
||||
} else if userdata.is::<CFnInfo>() {
|
||||
CALLBACK_ARG_REF_FLAG_CFN
|
||||
} else {
|
||||
return Err(LuaError::external("Unexpected argument type"));
|
||||
};
|
||||
Ok(FfiArg {
|
||||
size: helper::get_size(userdata)?,
|
||||
callback_ref_flag,
|
||||
})
|
||||
}
|
||||
|
||||
impl CFnInfo {
|
||||
pub fn new(
|
||||
args: Vec<Type>,
|
||||
ret: Type,
|
||||
arg_info_list: Vec<FfiArg>,
|
||||
result_info: FfiResult,
|
||||
) -> LuaResult<Self> {
|
||||
Ok(Self {
|
||||
cif: Cif::new(args.clone(), ret.clone()),
|
||||
arg_info_list,
|
||||
result_info,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_table<'lua>(
|
||||
lua: &'lua Lua,
|
||||
arg_table: LuaTable,
|
||||
ret: LuaAnyUserData,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
if helper::has_void(&arg_table)? {
|
||||
return Err(LuaError::external("Arguments can not include void type"));
|
||||
}
|
||||
|
||||
let args_types = helper::get_middle_type_list(&arg_table)?;
|
||||
let ret_type = helper::get_middle_type(&ret)?;
|
||||
|
||||
let arg_info_list = helper::create_list(&arg_table, create_arg_info)?;
|
||||
let result_info = FfiResult {
|
||||
size: helper::get_size(&ret)?,
|
||||
};
|
||||
|
||||
let cfn =
|
||||
lua.create_userdata(Self::new(args_types, ret_type, arg_info_list, result_info)?)?;
|
||||
|
||||
// Create association to hold argument and result type
|
||||
association::set(lua, CFN_ARGS, &cfn, arg_table)?;
|
||||
association::set(lua, CFN_RESULT, &cfn, ret)?;
|
||||
|
||||
Ok(cfn)
|
||||
}
|
||||
|
||||
// Stringify for pretty-print
|
||||
// ex: <CFn( (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))) = (
|
||||
association::get(lua, CFN_ARGS, userdata)?,
|
||||
association::get(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 = 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!(") -> {} ", helper::pretty_format(lua, &result_userdata)?,).as_str(),
|
||||
);
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(LuaError::external("Failed to retrieve inner type"))
|
||||
}
|
||||
}
|
||||
|
||||
// Create ClosureData with lua function
|
||||
pub fn create_closure<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
this: &LuaAnyUserData,
|
||||
lua_function: LuaFunction<'lua>,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let closure_data = ClosureData::alloc(
|
||||
lua,
|
||||
self.cif.as_raw_ptr(),
|
||||
self.arg_info_list.clone(),
|
||||
self.result_info.clone(),
|
||||
lua.create_registry_value(&lua_function)?,
|
||||
)?;
|
||||
|
||||
association::set(lua, CLOSURE_CFN, &closure_data, this)?;
|
||||
association::set(lua, CLOSURE_FUNC, &closure_data, lua_function)?;
|
||||
|
||||
Ok(closure_data)
|
||||
}
|
||||
|
||||
// Create CallableData from RefData
|
||||
pub fn create_callable<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
this: &LuaAnyUserData,
|
||||
target_ref: &LuaAnyUserData,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
if !target_ref.is::<RefData>() {
|
||||
return Err(LuaError::external("Argument 'functionRef' must be RefData"));
|
||||
}
|
||||
|
||||
let ffi_ref = target_ref.borrow::<RefData>()?;
|
||||
if u8_test_not(ffi_ref.flags, RefFlag::Function.value()) {
|
||||
return Err(LuaError::external(
|
||||
"Argument 'functionRef' is not a valid function reference",
|
||||
));
|
||||
}
|
||||
|
||||
let callable = lua.create_userdata(unsafe {
|
||||
CallableData::new(
|
||||
self.cif.as_raw_ptr(),
|
||||
self.arg_info_list.clone(),
|
||||
self.result_info.clone(),
|
||||
ffi_ref.get_inner_pointer(),
|
||||
)
|
||||
})?;
|
||||
|
||||
association::set(lua, CALLABLE_CFN, &callable, this)?;
|
||||
association::set(lua, CALLABLE_REF, &callable, target_ref)?;
|
||||
|
||||
Ok(callable)
|
||||
}
|
||||
|
||||
pub fn get_middle_type() -> Type {
|
||||
Type::pointer()
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CFnInfo {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("size", |_lua, _this| Ok(SIZE_OF_POINTER));
|
||||
}
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
// ToString
|
||||
method_provider::provide_to_string(methods);
|
||||
|
||||
// Realize
|
||||
methods.add_function(
|
||||
"closure",
|
||||
|lua, (cfn, func): (LuaAnyUserData, LuaFunction)| {
|
||||
let this = cfn.borrow::<CFnInfo>()?;
|
||||
this.create_closure(lua, cfn.as_ref(), func)
|
||||
},
|
||||
);
|
||||
methods.add_function(
|
||||
"callable",
|
||||
|lua, (cfn, target): (LuaAnyUserData, LuaAnyUserData)| {
|
||||
let this = cfn.borrow::<CFnInfo>()?;
|
||||
this.create_callable(lua, cfn.as_ref(), &target)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
318
crates/lune-std-ffi/src/c/helper.rs
Normal file
318
crates/lune-std-ffi/src/c/helper.rs
Normal file
|
@ -0,0 +1,318 @@
|
|||
use libffi::middle::Type;
|
||||
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{ctype_helper, void_info::CVoidInfo, CArrInfo, CFnInfo, CPtrInfo, CStructInfo};
|
||||
use crate::{
|
||||
data::{BoxData, GetFfiData},
|
||||
ffi::{FfiConvert, FfiSize},
|
||||
};
|
||||
|
||||
pub mod method_provider {
|
||||
use super::*;
|
||||
|
||||
// Implement tostring
|
||||
pub fn provide_to_string<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| {
|
||||
stringify(lua, &this)
|
||||
});
|
||||
}
|
||||
|
||||
// Implement ptr method
|
||||
pub fn provide_ptr<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
|
||||
CPtrInfo::from_userdata(lua, &this)
|
||||
});
|
||||
}
|
||||
|
||||
// Implement arr method
|
||||
pub fn provide_arr<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
|
||||
CArrInfo::from_userdata(lua, &this, length)
|
||||
});
|
||||
}
|
||||
|
||||
// Implement readData method
|
||||
pub fn provide_read_data<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiSize + FfiConvert + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_method(
|
||||
"readData",
|
||||
|lua, this, (target, offset): (LuaAnyUserData, Option<isize>)| {
|
||||
let offset = offset.unwrap_or(0);
|
||||
|
||||
let data_handle = &target.get_ffi_data()?;
|
||||
if !data_handle.check_inner_boundary(offset, this.get_size()) {
|
||||
return Err(LuaError::external("Out of bounds"));
|
||||
}
|
||||
if !data_handle.is_readable() {
|
||||
return Err(LuaError::external("Unreadable data"));
|
||||
}
|
||||
|
||||
unsafe { this.value_from_data(lua, offset, data_handle) }
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Implement writeData method
|
||||
pub fn provide_write_data<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiSize + FfiConvert + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_method(
|
||||
"writeData",
|
||||
|lua, this, (target, value, offset): (LuaAnyUserData, LuaValue, Option<isize>)| {
|
||||
let offset = offset.unwrap_or(0);
|
||||
|
||||
let data_handle = &target.get_ffi_data()?;
|
||||
// use or functions
|
||||
if !data_handle.check_inner_boundary(offset, this.get_size()) {
|
||||
return Err(LuaError::external("Out of bounds"));
|
||||
}
|
||||
if !data_handle.is_writable() {
|
||||
return Err(LuaError::external("Unwritable data"));
|
||||
}
|
||||
|
||||
unsafe { this.value_into_data(lua, offset, data_handle, value) }
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Implement copyData method
|
||||
pub fn provide_copy_data<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiSize + FfiConvert + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_method(
|
||||
"copyData",
|
||||
|lua,
|
||||
this,
|
||||
(dst, src, dst_offset, src_offset): (
|
||||
LuaAnyUserData,
|
||||
LuaAnyUserData,
|
||||
Option<isize>,
|
||||
Option<isize>,
|
||||
)| {
|
||||
let dst_offset = dst_offset.unwrap_or(0);
|
||||
let src_offset = src_offset.unwrap_or(0);
|
||||
|
||||
let dst = &dst.get_ffi_data()?;
|
||||
// use or functions
|
||||
if !dst.check_inner_boundary(dst_offset, this.get_size()) {
|
||||
return Err(LuaError::external("Destination out of bounds"));
|
||||
}
|
||||
if !dst.is_writable() {
|
||||
return Err(LuaError::external("Destination is unwritable"));
|
||||
}
|
||||
|
||||
let src = &src.get_ffi_data()?;
|
||||
if !src.check_inner_boundary(dst_offset, this.get_size()) {
|
||||
return Err(LuaError::external("Source out of bounds"));
|
||||
}
|
||||
if !src.is_readable() {
|
||||
return Err(LuaError::external("Source is unreadable"));
|
||||
}
|
||||
|
||||
unsafe { this.copy_data(lua, dst_offset, src_offset, dst, src) }
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Implement stringifyData method
|
||||
pub fn provide_stringify_data<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiSize + FfiConvert + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_method(
|
||||
"stringifyData",
|
||||
|lua, this, (target, offset): (LuaAnyUserData, Option<isize>)| unsafe {
|
||||
this.stringify_data(lua, offset.unwrap_or(0), &target.get_ffi_data()?)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Implement box method
|
||||
pub fn provide_box<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiSize + FfiConvert + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_method("box", |lua, this, value: LuaValue| {
|
||||
let result = lua.create_userdata(BoxData::new(this.get_size()))?;
|
||||
unsafe { this.value_into_data(lua, 0, &result.get_ffi_data()?, value)? };
|
||||
Ok(result)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn get_userdata(value: LuaValue) -> LuaResult<LuaAnyUserData> {
|
||||
if let LuaValue::UserData(field_type) = value {
|
||||
Ok(field_type)
|
||||
} else {
|
||||
Err(LuaError::external(format!(
|
||||
"CStruct, CType, CFn, CVoid or CArr is required but got {}",
|
||||
pretty_format_value(&value, &ValueFormatConfig::new())
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// Create vec<T> from table with (userdata)->T
|
||||
pub fn create_list<T>(
|
||||
table: &LuaTable,
|
||||
callback: fn(&LuaAnyUserData) -> LuaResult<T>,
|
||||
) -> LuaResult<Vec<T>> {
|
||||
let len: usize = table.raw_len();
|
||||
let mut list = Vec::<T>::with_capacity(len);
|
||||
|
||||
for i in 0..len {
|
||||
let value: LuaValue = table.raw_get(i + 1)?;
|
||||
list.push(callback(&get_userdata(value)?)?);
|
||||
}
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
// Get the dynamic FfiConvert handle from the userData
|
||||
// This is intended to avoid lookup userdata and lua table every time. (eg: struct)
|
||||
// The userdata must live longer than the FfiConvert handle
|
||||
pub unsafe fn get_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn FfiConvert> {
|
||||
if userdata.is::<CStructInfo>() {
|
||||
Ok(userdata.to_pointer().cast::<CStructInfo>() as *const dyn FfiConvert)
|
||||
} else if userdata.is::<CArrInfo>() {
|
||||
Ok(userdata.to_pointer().cast::<CArrInfo>() as *const dyn FfiConvert)
|
||||
} else if userdata.is::<CPtrInfo>() {
|
||||
Ok(userdata.to_pointer().cast::<CPtrInfo>() as *const dyn FfiConvert)
|
||||
} else {
|
||||
ctype_helper::get_conv(userdata)
|
||||
}
|
||||
}
|
||||
pub unsafe fn get_conv_list(table: &LuaTable) -> LuaResult<Vec<*const dyn FfiConvert>> {
|
||||
create_list(table, |userdata| get_conv(userdata))
|
||||
}
|
||||
|
||||
// Get libffi_type from ctype userdata
|
||||
pub fn get_middle_type(userdata: &LuaAnyUserData) -> LuaResult<Type> {
|
||||
if userdata.is::<CStructInfo>() {
|
||||
Ok(userdata.borrow::<CStructInfo>()?.get_middle_type())
|
||||
} else if let Some(middle_type) = ctype_helper::get_middle_type(userdata)? {
|
||||
Ok(middle_type)
|
||||
} else if userdata.is::<CArrInfo>() {
|
||||
Ok(userdata.borrow::<CArrInfo>()?.get_middle_type())
|
||||
} else if userdata.is::<CPtrInfo>() {
|
||||
Ok(CPtrInfo::get_middle_type())
|
||||
} else if userdata.is::<CVoidInfo>() {
|
||||
Ok(CVoidInfo::get_middle_type())
|
||||
} else if userdata.is::<CFnInfo>() {
|
||||
Ok(CFnInfo::get_middle_type())
|
||||
} else {
|
||||
Err(LuaError::external(format!(
|
||||
"CStruct, CType, CFn, CVoid or CArr is required but got {}",
|
||||
pretty_format_value(
|
||||
// Since the data is in the Lua location,
|
||||
// there is no problem with the clone.
|
||||
&LuaValue::UserData(userdata.to_owned()),
|
||||
&ValueFormatConfig::new()
|
||||
)
|
||||
)))
|
||||
}
|
||||
}
|
||||
pub fn get_middle_type_list(table: &LuaTable) -> LuaResult<Vec<Type>> {
|
||||
create_list(table, get_middle_type)
|
||||
}
|
||||
|
||||
// Get type size from ctype userdata
|
||||
pub fn get_size(userdata: &LuaAnyUserData) -> LuaResult<usize> {
|
||||
if userdata.is::<CStructInfo>() {
|
||||
Ok(userdata.borrow::<CStructInfo>()?.get_size())
|
||||
} else if userdata.is::<CArrInfo>() {
|
||||
Ok(userdata.borrow::<CArrInfo>()?.get_size())
|
||||
} else if userdata.is::<CPtrInfo>() {
|
||||
Ok(userdata.borrow::<CPtrInfo>()?.get_size())
|
||||
} else if userdata.is::<CVoidInfo>() {
|
||||
Ok(userdata.borrow::<CVoidInfo>()?.get_size())
|
||||
} else if userdata.is::<CFnInfo>() {
|
||||
Ok(userdata.borrow::<CFnInfo>()?.get_size())
|
||||
} else {
|
||||
ctype_helper::get_size(userdata)
|
||||
}
|
||||
}
|
||||
|
||||
// Check lua table has void ctype userdata
|
||||
pub fn has_void(table: &LuaTable) -> LuaResult<bool> {
|
||||
for i in 0..table.raw_len() {
|
||||
let value: LuaValue = table.raw_get(i + 1)?;
|
||||
if get_userdata(value)?.is::<CVoidInfo>() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// Stringify any c-type userdata recursively
|
||||
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
|
||||
if userdata.is::<CStructInfo>() {
|
||||
CStructInfo::stringify(lua, userdata)
|
||||
} else if userdata.is::<CArrInfo>() {
|
||||
CArrInfo::stringify(lua, userdata)
|
||||
} else if userdata.is::<CPtrInfo>() {
|
||||
CPtrInfo::stringify(lua, userdata)
|
||||
} else if userdata.is::<CFnInfo>() {
|
||||
CFnInfo::stringify(lua, userdata)
|
||||
} else if userdata.is::<CVoidInfo>() {
|
||||
CVoidInfo::stringify()
|
||||
} else if let Some(name) = ctype_helper::get_name(userdata)? {
|
||||
Ok(String::from(name))
|
||||
} else {
|
||||
Ok(String::from("unknown"))
|
||||
}
|
||||
}
|
||||
|
||||
// Get name tag from ctype userdata
|
||||
pub fn get_tag_name(userdata: &LuaAnyUserData) -> LuaResult<String> {
|
||||
Ok(if userdata.is::<CStructInfo>() {
|
||||
String::from("CStructInfo")
|
||||
} else if userdata.is::<CArrInfo>() {
|
||||
String::from("CArrInfo")
|
||||
} else if userdata.is::<CPtrInfo>() {
|
||||
String::from("CPtrInfo")
|
||||
} else if userdata.is::<CFnInfo>() {
|
||||
String::from("CFnInfo")
|
||||
} else if userdata.is::<CVoidInfo>() {
|
||||
String::from("CVoidInfo")
|
||||
} else if ctype_helper::is_ctype(userdata) {
|
||||
String::from("CTypeInfo")
|
||||
} else {
|
||||
String::from("Unknown")
|
||||
})
|
||||
}
|
||||
|
||||
// Emulate 'print' for ctype userdata, but simplified
|
||||
// Used for print struct field, cfn arguments, etc...
|
||||
pub fn pretty_format(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
|
||||
if ctype_helper::is_ctype(userdata) {
|
||||
stringify(lua, userdata)
|
||||
} else {
|
||||
Ok(format!(
|
||||
"<{}({})>",
|
||||
get_tag_name(userdata)?,
|
||||
stringify(lua, userdata)?
|
||||
))
|
||||
}
|
||||
}
|
52
crates/lune-std-ffi/src/c/mod.rs
Normal file
52
crates/lune-std-ffi/src/c/mod.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use lune_utils::TableBuilder;
|
||||
use mlua::prelude::*;
|
||||
|
||||
mod arr_info;
|
||||
mod fn_info;
|
||||
pub mod helper;
|
||||
mod ptr_info;
|
||||
mod string_info;
|
||||
mod struct_info;
|
||||
mod type_info;
|
||||
mod types;
|
||||
mod void_info;
|
||||
|
||||
pub use self::{
|
||||
arr_info::CArrInfo,
|
||||
fn_info::CFnInfo,
|
||||
helper::method_provider,
|
||||
ptr_info::CPtrInfo,
|
||||
string_info::CStringInfo,
|
||||
struct_info::CStructInfo,
|
||||
type_info::{CTypeCast, CTypeInfo},
|
||||
types::{ctype_helper, export_c_types, export_fixed_types},
|
||||
void_info::CVoidInfo,
|
||||
};
|
||||
|
||||
// Named registry keys
|
||||
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 CFN_RESULT: &str = "__cfn_result";
|
||||
pub const CFN_ARGS: &str = "__cfn_args";
|
||||
pub const CALLABLE_REF: &str = "__callable_ref";
|
||||
pub const CALLABLE_CFN: &str = "__callable_cfn";
|
||||
pub const CLOSURE_FUNC: &str = "__closure_func";
|
||||
pub const CLOSURE_CFN: &str = "__closure_cfn";
|
||||
}
|
||||
|
||||
// Export c namespace
|
||||
pub fn export_c(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
TableBuilder::new(lua)?
|
||||
.with_value("void", CVoidInfo::new())?
|
||||
.with_values(export_c_types(lua)?)?
|
||||
.with_function("struct", |lua, types: LuaTable| {
|
||||
CStructInfo::from_table(lua, types)
|
||||
})?
|
||||
.with_function("fn", |lua, (args, ret): (LuaTable, LuaAnyUserData)| {
|
||||
CFnInfo::from_table(lua, args, ret)
|
||||
})?
|
||||
.with_value("string", CStringInfo::new())?
|
||||
.build_readonly()
|
||||
}
|
192
crates/lune-std-ffi/src/c/ptr_info.rs
Normal file
192
crates/lune-std-ffi/src/c/ptr_info.rs
Normal file
|
@ -0,0 +1,192 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use libffi::middle::Type;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{association_names::CPTR_INNER, ctype_helper, helper, method_provider};
|
||||
use crate::{
|
||||
data::{GetFfiData, RefData, RefFlag, UNSIZED_BOUNDS},
|
||||
ffi::{
|
||||
association, libffi_helper::SIZE_OF_POINTER, FfiConvert, FfiData, FfiSignedness, FfiSize,
|
||||
},
|
||||
};
|
||||
|
||||
const READ_CPTR_REF_FLAGS: u8 = RefFlag::Dereferenceable.value() | RefFlag::Offsetable.value();
|
||||
const READ_REF_FLAGS: u8 =
|
||||
RefFlag::Offsetable.value() | RefFlag::Readable.value() | RefFlag::Writable.value();
|
||||
|
||||
pub struct CPtrInfo {
|
||||
inner_is_cptr: bool,
|
||||
}
|
||||
|
||||
impl FfiSignedness for CPtrInfo {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiSize for CPtrInfo {
|
||||
fn get_size(&self) -> usize {
|
||||
SIZE_OF_POINTER
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CPtrInfo {
|
||||
// Write address of RefData
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let LuaValue::UserData(value_userdata) = value else {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a RefData, BoxData or ClosureData, got {}",
|
||||
value.type_name()
|
||||
)));
|
||||
};
|
||||
*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<*mut ()>() = value_userdata.get_ffi_data()?.get_inner_pointer();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Read address, create RefData
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
Ok(LuaValue::UserData(
|
||||
lua.create_userdata(RefData::new(
|
||||
*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<*mut ()>(),
|
||||
if self.inner_is_cptr {
|
||||
READ_CPTR_REF_FLAGS
|
||||
} else {
|
||||
READ_REF_FLAGS
|
||||
},
|
||||
UNSIZED_BOUNDS,
|
||||
))?,
|
||||
))
|
||||
}
|
||||
|
||||
// Copy Address
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<*mut ()>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<*mut ()>();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CPtrInfo {
|
||||
// Create pointer type with '.inner' field
|
||||
// inner can be CArr, CType or CStruct
|
||||
pub fn from_userdata<'lua>(
|
||||
lua: &'lua Lua,
|
||||
inner: &LuaAnyUserData,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let value = lua.create_userdata(Self {
|
||||
inner_is_cptr: inner.is::<CPtrInfo>(),
|
||||
})?;
|
||||
|
||||
association::set(lua, CPTR_INNER, &value, inner)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// Stringify CPtr with inner ctype
|
||||
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
|
||||
if let LuaValue::UserData(inner_userdata) = userdata.get("inner")? {
|
||||
let pretty_formatted = helper::pretty_format(lua, &inner_userdata)?;
|
||||
Ok(if ctype_helper::is_ctype(&inner_userdata) {
|
||||
pretty_formatted
|
||||
} else {
|
||||
format!(" {pretty_formatted} ")
|
||||
})
|
||||
} else {
|
||||
Err(LuaError::external("Failed to retrieve inner type"))
|
||||
}
|
||||
}
|
||||
|
||||
// Return void*
|
||||
pub fn get_middle_type() -> Type {
|
||||
Type::pointer()
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CPtrInfo {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("size", |_lua, _this| Ok(SIZE_OF_POINTER));
|
||||
fields.add_field_function_get("inner", |lua, this| {
|
||||
association::get(lua, CPTR_INNER, this)?
|
||||
.ok_or_else(|| LuaError::external("Failed to retrieve inner type"))
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
methods.add_method(
|
||||
"readRef",
|
||||
|lua,
|
||||
this,
|
||||
(target, offset, ref_data): (
|
||||
LuaAnyUserData,
|
||||
Option<isize>,
|
||||
Option<LuaAnyUserData>,
|
||||
)| unsafe {
|
||||
if let Some(ref_userdata) = ref_data {
|
||||
if !ref_userdata.is::<RefData>() {
|
||||
return Err(LuaError::external(""));
|
||||
}
|
||||
RefData::update(
|
||||
lua,
|
||||
ref_userdata.clone(),
|
||||
*target
|
||||
.get_ffi_data()?
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset.unwrap_or(0))
|
||||
.cast::<*mut ()>(),
|
||||
if this.inner_is_cptr {
|
||||
READ_CPTR_REF_FLAGS
|
||||
} else {
|
||||
READ_REF_FLAGS
|
||||
},
|
||||
UNSIZED_BOUNDS,
|
||||
)?;
|
||||
Ok(LuaValue::UserData(ref_userdata))
|
||||
} else {
|
||||
this.value_from_data(lua, offset.unwrap_or(0), &target.get_ffi_data()?)
|
||||
}
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"writeRef",
|
||||
|lua, this, (target, value, offset): (LuaAnyUserData, LuaValue, Option<isize>)| unsafe {
|
||||
this.value_into_data(lua, offset.unwrap_or(0), &target.get_ffi_data()?, value)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
13
crates/lune-std-ffi/src/c/string_info.rs
Normal file
13
crates/lune-std-ffi/src/c/string_info.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// TODO:
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
pub struct CStringInfo();
|
||||
|
||||
impl CStringInfo {
|
||||
pub fn new() -> Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CStringInfo {}
|
215
crates/lune-std-ffi/src/c/struct_info.rs
Normal file
215
crates/lune-std-ffi/src/c/struct_info.rs
Normal file
|
@ -0,0 +1,215 @@
|
|||
use std::{cell::Ref, vec::Vec};
|
||||
|
||||
use libffi::{low, middle::Type, raw::ffi_get_struct_offsets};
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{association_names::CSTRUCT_INNER, helper, method_provider};
|
||||
use crate::ffi::{
|
||||
association, libffi_helper::ffi_status_assert, FfiConvert, FfiData, FfiSignedness, FfiSize,
|
||||
};
|
||||
|
||||
pub struct CStructInfo {
|
||||
middle_type: Type,
|
||||
size: usize,
|
||||
inner_offset_list: Vec<usize>,
|
||||
inner_conv_list: Vec<*const dyn FfiConvert>,
|
||||
}
|
||||
|
||||
fn get_field_table<'lua>(
|
||||
lua: &'lua Lua,
|
||||
userdata: &LuaAnyUserData<'lua>,
|
||||
) -> LuaResult<LuaTable<'lua>> {
|
||||
let value = association::get(lua, CSTRUCT_INNER, userdata)?
|
||||
.ok_or_else(|| LuaError::external("Failed to retrieve inner field table: not found"))?;
|
||||
if let LuaValue::Table(table) = value {
|
||||
Ok(table)
|
||||
} else {
|
||||
Err(LuaError::external(
|
||||
"Failed to retrieve inner field: not a table",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl CStructInfo {
|
||||
pub fn new(fields: Vec<Type>, inner_conv_list: Vec<*const dyn FfiConvert>) -> LuaResult<Self> {
|
||||
let len = fields.len();
|
||||
let mut inner_offset_list = Vec::<usize>::with_capacity(len);
|
||||
let middle_type = Type::structure(fields);
|
||||
|
||||
// Get field offsets with ffi_get_struct_offsets
|
||||
unsafe {
|
||||
ffi_status_assert(ffi_get_struct_offsets(
|
||||
low::ffi_abi_FFI_DEFAULT_ABI,
|
||||
middle_type.as_raw_ptr(),
|
||||
inner_offset_list.as_mut_ptr(),
|
||||
))?;
|
||||
inner_offset_list.set_len(len);
|
||||
}
|
||||
|
||||
// Get tailing padded size of struct (get_ensured_size not required)
|
||||
let size = unsafe { (*middle_type.as_raw_ptr()).size };
|
||||
|
||||
Ok(Self {
|
||||
middle_type,
|
||||
size,
|
||||
inner_offset_list,
|
||||
inner_conv_list,
|
||||
})
|
||||
}
|
||||
|
||||
// Create new CStruct from LuaTable.
|
||||
// Freeze and hold table
|
||||
pub fn from_table<'lua>(
|
||||
lua: &'lua Lua,
|
||||
table: LuaTable<'lua>,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
if helper::has_void(&table)? {
|
||||
return Err(LuaError::external("Void field in sturct is not allowed"));
|
||||
}
|
||||
|
||||
let cstruct = lua
|
||||
.create_userdata(Self::new(helper::get_middle_type_list(&table)?, unsafe {
|
||||
helper::get_conv_list(&table)?
|
||||
})?)?;
|
||||
|
||||
// Save field table
|
||||
table.set_readonly(true);
|
||||
association::set(lua, CSTRUCT_INNER, &cstruct, table)?;
|
||||
Ok(cstruct)
|
||||
}
|
||||
|
||||
// Stringify cstruct for pretty printing
|
||||
// ex: <CStruct( u8, i32, size = 8 )>
|
||||
pub fn stringify(lua: &Lua, userdata: &LuaAnyUserData) -> LuaResult<String> {
|
||||
let fields = get_field_table(lua, userdata)?;
|
||||
let mut stringified = String::from(" ");
|
||||
|
||||
// Children
|
||||
for i in 0..fields.raw_len() {
|
||||
let child: LuaAnyUserData = fields.raw_get(i + 1)?;
|
||||
let pretty_formatted = helper::pretty_format(lua, &child)?;
|
||||
stringified.push_str(format!("{pretty_formatted}, ").as_str());
|
||||
}
|
||||
|
||||
// Size
|
||||
stringified
|
||||
.push_str(format!("size = {} ", userdata.borrow::<CStructInfo>()?.get_size()).as_str());
|
||||
Ok(stringified)
|
||||
}
|
||||
|
||||
// Get byte offset of nth field
|
||||
pub fn offset(&self, index: usize) -> LuaResult<usize> {
|
||||
Ok(self
|
||||
.inner_offset_list
|
||||
.get(index)
|
||||
.ok_or_else(|| LuaError::external("Out of index"))?
|
||||
.to_owned())
|
||||
}
|
||||
|
||||
pub fn get_middle_type(&self) -> Type {
|
||||
self.middle_type.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiSize for CStructInfo {
|
||||
fn get_size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiSignedness for CStructInfo {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CStructInfo {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let LuaValue::Table(ref table) = value else {
|
||||
return Err(LuaError::external("Value is not a table"));
|
||||
};
|
||||
for (index, conv) in self.inner_conv_list.iter().enumerate() {
|
||||
let field_offset = self.offset(index)? as isize;
|
||||
let data: LuaValue = table.get(index + 1)?;
|
||||
conv.as_ref().unwrap().value_into_data(
|
||||
lua,
|
||||
field_offset + offset,
|
||||
data_handle,
|
||||
data,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
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,
|
||||
conv.as_ref()
|
||||
.unwrap()
|
||||
.value_from_data(lua, field_offset + offset, data_handle)?,
|
||||
)?;
|
||||
}
|
||||
Ok(LuaValue::Table(table))
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
dst.get_inner_pointer().byte_offset(dst_offset).copy_from(
|
||||
src.get_inner_pointer().byte_offset(src_offset),
|
||||
self.get_size(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CStructInfo {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("size", |_lua, 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_read_data(methods);
|
||||
method_provider::provide_write_data(methods);
|
||||
method_provider::provide_copy_data(methods);
|
||||
|
||||
// Get nth field offset
|
||||
methods.add_method("offset", |_lua, this, index: usize| this.offset(index));
|
||||
// Get nth field type
|
||||
methods.add_function(
|
||||
"field",
|
||||
|lua, (this, field_index): (LuaAnyUserData, usize)| {
|
||||
let field_table = get_field_table(lua, &this)?;
|
||||
field_table
|
||||
.raw_get::<_, Option<LuaAnyUserData>>(field_index + 1)?
|
||||
.ok_or_else(|| LuaError::external("Out of index"))
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
142
crates/lune-std-ffi/src/c/type_info.rs
Normal file
142
crates/lune-std-ffi/src/c/type_info.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
#![allow(clippy::inline_always)]
|
||||
|
||||
use std::{cell::Ref, marker::PhantomData};
|
||||
|
||||
use libffi::middle::Type;
|
||||
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::method_provider;
|
||||
use crate::{
|
||||
data::GetFfiData,
|
||||
ffi::{libffi_helper::get_ensured_size, FfiConvert, FfiData, FfiSignedness, FfiSize},
|
||||
};
|
||||
|
||||
// Provide type casting
|
||||
// This trait should be implemented for each types
|
||||
pub trait CTypeCast {
|
||||
#[inline(always)]
|
||||
fn cast(
|
||||
&self,
|
||||
from_ctype: &LuaAnyUserData,
|
||||
into_ctype: &LuaAnyUserData,
|
||||
_from: &Ref<dyn FfiData>,
|
||||
_into: &Ref<dyn FfiData>,
|
||||
_from_offset: isize,
|
||||
_into_offset: isize,
|
||||
) -> LuaResult<()> {
|
||||
// Error if have no cast implement
|
||||
Err(Self::cast_failed_with(self, from_ctype, into_ctype))
|
||||
}
|
||||
|
||||
fn cast_failed_with(
|
||||
&self,
|
||||
from_ctype: &LuaAnyUserData,
|
||||
into_ctype: &LuaAnyUserData,
|
||||
) -> LuaError {
|
||||
let config = ValueFormatConfig::new();
|
||||
LuaError::external(format!(
|
||||
"Failed to cast {} into {}",
|
||||
pretty_format_value(&LuaValue::UserData(from_ctype.to_owned()), &config),
|
||||
pretty_format_value(&LuaValue::UserData(into_ctype.to_owned()), &config),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CTypeInfo<T> {
|
||||
middle_type: Type,
|
||||
size: usize,
|
||||
name: &'static str,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> FfiSize for CTypeInfo<T> {
|
||||
fn get_size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CTypeInfo<T>
|
||||
where
|
||||
T: 'static,
|
||||
Self: CTypeCast + FfiSignedness + FfiConvert + FfiSize,
|
||||
{
|
||||
pub fn from_middle_type<'lua>(
|
||||
lua: &'lua Lua,
|
||||
libffi_type: Type,
|
||||
name: &'static str,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let size = get_ensured_size(libffi_type.as_raw_ptr())?;
|
||||
|
||||
let ctype = Self {
|
||||
middle_type: libffi_type,
|
||||
size,
|
||||
name,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
let userdata = lua.create_userdata(ctype)?;
|
||||
|
||||
Ok(userdata)
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
pub fn get_middle_type(&self) -> Type {
|
||||
self.middle_type.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LuaUserData for CTypeInfo<T>
|
||||
where
|
||||
T: 'static,
|
||||
Self: CTypeCast + FfiSignedness + FfiConvert + FfiSize,
|
||||
{
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field(LuaMetaMethod::Type, "CTypeInfo");
|
||||
fields.add_field_method_get("size", |_lua, this| Ok(this.get_size()));
|
||||
fields.add_field_method_get("signedness", |_lua, this| Ok(this.get_signedness()));
|
||||
}
|
||||
|
||||
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_read_data(methods);
|
||||
method_provider::provide_write_data(methods);
|
||||
method_provider::provide_copy_data(methods);
|
||||
method_provider::provide_stringify_data(methods);
|
||||
|
||||
// Math
|
||||
// TODO: Math support for numeric types
|
||||
|
||||
methods.add_function(
|
||||
"cast",
|
||||
|_lua,
|
||||
(from_type, into_type, from, into, from_offset, into_offset): (
|
||||
LuaAnyUserData,
|
||||
LuaAnyUserData,
|
||||
LuaAnyUserData,
|
||||
LuaAnyUserData,
|
||||
Option<isize>,
|
||||
Option<isize>,
|
||||
)| {
|
||||
from_type.borrow::<Self>()?.cast(
|
||||
&from_type,
|
||||
&into_type,
|
||||
&from.get_ffi_data()?,
|
||||
&into.get_ffi_data()?,
|
||||
from_offset.unwrap_or(0),
|
||||
into_offset.unwrap_or(0),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/f32.rs
Normal file
90
crates/lune-std-ffi/src/c/types/f32.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<f32> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<f32> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: f32 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<f32>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f32>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f32>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<f32>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<f32>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f32>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/f64.rs
Normal file
90
crates/lune-std-ffi/src/c/types/f64.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<f64> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<f64> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: f64 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<f64>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f64>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f64>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<f64>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<f64>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f64>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/i128.rs
Normal file
90
crates/lune-std-ffi/src/c/types/i128.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<i128> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<i128> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: i128 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<i128>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i128>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i128>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<i128>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<i128>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i128>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/i16.rs
Normal file
90
crates/lune-std-ffi/src/c/types/i16.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<i16> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<i16> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: i16 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<i16>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i16>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i16>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<i16>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<i16>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i16>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/i32.rs
Normal file
90
crates/lune-std-ffi/src/c/types/i32.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<i32> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<i32> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: i32 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<i32>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i32>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i32>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<i32>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<i32>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i32>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/i64.rs
Normal file
90
crates/lune-std-ffi/src/c/types/i64.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<i64> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<i64> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: i64 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<i64>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i64>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i64>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<i64>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<i64>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i64>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
82
crates/lune-std-ffi/src/c/types/i8.rs
Normal file
82
crates/lune-std-ffi/src/c/types/i8.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<i8> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<i8> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: i8 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::String(t) => t.as_bytes().first().map_or(0, u8::to_owned).as_(),
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i8>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i8>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer().byte_offset(dst_offset).cast::<i8>() =
|
||||
*src.get_inner_pointer().byte_offset(src_offset).cast::<i8>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<i8>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/isize.rs
Normal file
90
crates/lune-std-ffi/src/c/types/isize.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<isize> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<isize> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: isize = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<isize>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<isize>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<isize>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<isize>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<isize>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<isize>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
203
crates/lune-std-ffi/src/c/types/mod.rs
Normal file
203
crates/lune-std-ffi/src/c/types/mod.rs
Normal file
|
@ -0,0 +1,203 @@
|
|||
#![allow(clippy::inline_always)]
|
||||
|
||||
use core::ffi::*;
|
||||
use std::{any::TypeId, cell::Ref};
|
||||
|
||||
use libffi::middle::Type;
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use super::{CTypeCast, CTypeInfo};
|
||||
use crate::ffi::{num_cast, FfiConvert, FfiData, FfiSize};
|
||||
|
||||
mod f32;
|
||||
mod f64;
|
||||
mod i128;
|
||||
mod i16;
|
||||
mod i32;
|
||||
mod i64;
|
||||
mod i8;
|
||||
mod isize;
|
||||
mod u128;
|
||||
mod u16;
|
||||
mod u32;
|
||||
mod u64;
|
||||
mod u8;
|
||||
mod usize;
|
||||
|
||||
// CType userdata export
|
||||
macro_rules! create_ctypes {
|
||||
($lua:ident, $(( $name:expr, $rust_type:ty, $libffi_type:expr ),)* ) => {
|
||||
Ok(vec![$((
|
||||
$name,
|
||||
CTypeInfo::<$rust_type>::from_middle_type($lua, $libffi_type, $name)?,
|
||||
),)*])
|
||||
};
|
||||
}
|
||||
pub fn export_c_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaAnyUserData)>> {
|
||||
create_ctypes!(
|
||||
lua,
|
||||
// Export Compile-time known c-types
|
||||
("char", c_char, {
|
||||
if TypeId::of::<c_char>() == TypeId::of::<u8>() {
|
||||
Type::c_uchar()
|
||||
} else {
|
||||
Type::c_schar()
|
||||
}
|
||||
}),
|
||||
("uchar", c_uchar, Type::c_uchar()),
|
||||
("schar", c_schar, Type::c_schar()),
|
||||
("short", c_short, Type::c_short()),
|
||||
("ushort", c_ushort, Type::c_ushort()),
|
||||
("int", c_int, Type::c_int()),
|
||||
("uint", c_uint, Type::c_uint()),
|
||||
("long", c_long, Type::c_long()),
|
||||
("ulong", c_ulong, Type::c_ulong()),
|
||||
("longlong", c_longlong, Type::c_longlong()),
|
||||
("ulonglong", c_ulonglong, Type::c_ulonglong()),
|
||||
)
|
||||
}
|
||||
pub fn export_fixed_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaAnyUserData)>> {
|
||||
create_ctypes!(
|
||||
lua,
|
||||
// Export Source-time known c-types (fixed)
|
||||
("u8", u8, Type::u8()),
|
||||
("u16", u16, Type::u16()),
|
||||
("u32", u32, Type::u32()),
|
||||
("u64", u64, Type::u64()),
|
||||
("u128", u128, Type::c_longlong()),
|
||||
("i8", i8, Type::i8()),
|
||||
("i16", i16, Type::i16()),
|
||||
("i32", i32, Type::i32()),
|
||||
("i64", i64, Type::i64()),
|
||||
("i128", i128, Type::c_ulonglong()),
|
||||
("f64", f64, Type::f64()),
|
||||
("f32", f32, Type::f32()),
|
||||
("usize", usize, Type::usize()),
|
||||
("isize", isize, Type::isize()),
|
||||
("f32", f32, Type::f32()),
|
||||
("f64", f64, Type::f64()),
|
||||
)
|
||||
}
|
||||
|
||||
// Implement type-casting for numeric ctypes
|
||||
macro_rules! define_cast_num {
|
||||
($from_rust_type:ident, $self:ident, $from_ctype:ident, $into_ctype:ident, $from:ident, $into:ident, $fromOffset:ident, $intoOffset:ident, $($into_rust_type:ty)*) => {
|
||||
$( if $into_ctype.is::<CTypeInfo<$into_rust_type>>() {
|
||||
num_cast::<$from_rust_type, $into_rust_type>($from, $into, $fromOffset, $intoOffset)
|
||||
} else )* {
|
||||
Err($self.cast_failed_with($from_ctype, $into_ctype))
|
||||
}
|
||||
};
|
||||
}
|
||||
impl<From> CTypeCast for CTypeInfo<From>
|
||||
where
|
||||
From: AsPrimitive<f32>
|
||||
+ AsPrimitive<f64>
|
||||
+ AsPrimitive<i128>
|
||||
+ AsPrimitive<i16>
|
||||
+ AsPrimitive<i32>
|
||||
+ AsPrimitive<i64>
|
||||
+ AsPrimitive<i8>
|
||||
+ AsPrimitive<isize>
|
||||
+ AsPrimitive<u128>
|
||||
+ AsPrimitive<u16>
|
||||
+ AsPrimitive<u32>
|
||||
+ AsPrimitive<u64>
|
||||
+ AsPrimitive<u8>
|
||||
+ AsPrimitive<usize>,
|
||||
{
|
||||
fn cast(
|
||||
&self,
|
||||
from_info: &LuaAnyUserData,
|
||||
into_info: &LuaAnyUserData,
|
||||
from: &Ref<dyn FfiData>,
|
||||
into: &Ref<dyn FfiData>,
|
||||
from_offset: isize,
|
||||
into_offset: isize,
|
||||
) -> LuaResult<()> {
|
||||
define_cast_num!(
|
||||
From, self, from_info, into_info, from, into, from_offset, into_offset,
|
||||
f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ctype_helper {
|
||||
use super::*;
|
||||
|
||||
// To prevent droping NativeConvert, need to ensure userdata keep alive
|
||||
macro_rules! define_get_conv {
|
||||
($userdata:ident, $( $rust_type:ty )*) => {
|
||||
$( if $userdata.is::<CTypeInfo<$rust_type>>() {
|
||||
Ok($userdata.to_pointer().cast::<CTypeInfo<$rust_type>>() as *const dyn FfiConvert)
|
||||
} else )* {
|
||||
Err(LuaError::external("Unexpected type"))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_conv(userdata: &LuaAnyUserData) -> LuaResult<*const dyn FfiConvert> {
|
||||
define_get_conv!(userdata, f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize)
|
||||
}
|
||||
|
||||
// Get libffi_type of ctype
|
||||
macro_rules! define_get_middle_type {
|
||||
($userdata:ident, $( $rust_type:ty )*) => {
|
||||
$( if $userdata.is::<CTypeInfo<$rust_type>>() {
|
||||
Ok(Some($userdata.borrow::<CTypeInfo<$rust_type>>()?.get_middle_type()))
|
||||
} else )* {
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_middle_type(userdata: &LuaAnyUserData) -> LuaResult<Option<Type>> {
|
||||
define_get_middle_type!(userdata, f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize)
|
||||
}
|
||||
|
||||
// Get size of ctype (not including struct, arr, ... only CType<*>)
|
||||
macro_rules! define_get_size {
|
||||
($userdata:ident, $( $rust_type:ty )*) => {
|
||||
$( if $userdata.is::<CTypeInfo<$rust_type>>() {
|
||||
Ok($userdata.borrow::<CTypeInfo<$rust_type>>()?.get_size())
|
||||
} else )* {
|
||||
Err(LuaError::external("Unexpected type"))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_size(userdata: &LuaAnyUserData) -> LuaResult<usize> {
|
||||
define_get_size!(userdata, f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize)
|
||||
}
|
||||
|
||||
// Get name of ctype
|
||||
macro_rules! define_get_name {
|
||||
($userdata:ident, $( $rust_type:ty )*) => {
|
||||
$( if $userdata.is::<CTypeInfo<$rust_type>>() {
|
||||
Ok(Some($userdata.borrow::<CTypeInfo<$rust_type>>()?.get_name()))
|
||||
} else )* {
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_name(userdata: &LuaAnyUserData) -> LuaResult<Option<&'static str>> {
|
||||
define_get_name!(userdata, f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize)
|
||||
}
|
||||
|
||||
// Check whether userdata is ctype or not
|
||||
macro_rules! define_is_ctype {
|
||||
($userdata:ident, $( $rust_type:ty )*) => {
|
||||
$( if $userdata.is::<CTypeInfo<$rust_type>>() {
|
||||
true
|
||||
} else )* {
|
||||
false
|
||||
}
|
||||
};
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_ctype(userdata: &LuaAnyUserData) -> bool {
|
||||
define_is_ctype!(userdata, f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize)
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/u128.rs
Normal file
90
crates/lune-std-ffi/src/c/types/u128.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<u128> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<u128> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: u128 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<u128>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u128>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u128>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<u128>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<u128>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u128>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
91
crates/lune-std-ffi/src/c/types/u16.rs
Normal file
91
crates/lune-std-ffi/src/c/types/u16.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<u16> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<u16> {
|
||||
// Convert luavalue into data, then write into ptr
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: u16 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<u16>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u16>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u16>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<u16>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<u16>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u16>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/u32.rs
Normal file
90
crates/lune-std-ffi/src/c/types/u32.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<u32> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<u32> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: u32 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<u32>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u32>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u32>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<u32>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<u32>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<f32>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/u64.rs
Normal file
90
crates/lune-std-ffi/src/c/types/u64.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<u64> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<u64> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: u64 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<u64>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u64>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u64>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<u64>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<u64>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u64>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
85
crates/lune-std-ffi/src/c/types/u8.rs
Normal file
85
crates/lune-std-ffi/src/c/types/u8.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<u8> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<u8> {
|
||||
// Convert luavalue into data, then write into ptr
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: u8 = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::String(t) => t.as_bytes().first().map_or(0, u8::to_owned).as_(),
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u8>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Read data from ptr, then convert into luavalue
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u8>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer().byte_offset(dst_offset).cast::<u8>() =
|
||||
*src.get_inner_pointer().byte_offset(src_offset).cast::<u8>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<u8>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
90
crates/lune-std-ffi/src/c/types/usize.rs
Normal file
90
crates/lune-std-ffi/src/c/types/usize.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use crate::{
|
||||
c::type_info::CTypeInfo,
|
||||
ffi::{FfiConvert, FfiData, FfiSignedness},
|
||||
};
|
||||
|
||||
impl FfiSignedness for CTypeInfo<usize> {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiConvert for CTypeInfo<usize> {
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
_lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()> {
|
||||
let value: usize = match value {
|
||||
LuaValue::Integer(t) => t.as_(),
|
||||
LuaValue::Number(t) => t.as_(),
|
||||
LuaValue::String(t) => t
|
||||
.to_string_lossy()
|
||||
.parse::<usize>()
|
||||
.map_err(LuaError::external)?,
|
||||
_ => {
|
||||
return Err(LuaError::external(format!(
|
||||
"Value must be a Integer, Number or String, got {}",
|
||||
value.type_name()
|
||||
)))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*(data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<usize>()) = value;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
let value = unsafe {
|
||||
(*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<usize>())
|
||||
.into_lua(lua)?
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()> {
|
||||
*dst.get_inner_pointer()
|
||||
.byte_offset(dst_offset)
|
||||
.cast::<usize>() = *src
|
||||
.get_inner_pointer()
|
||||
.byte_offset(src_offset)
|
||||
.cast::<usize>();
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Ok((*data_handle
|
||||
.get_inner_pointer()
|
||||
.byte_offset(offset)
|
||||
.cast::<usize>())
|
||||
.to_string())
|
||||
}
|
||||
}
|
42
crates/lune-std-ffi/src/c/void_info.rs
Normal file
42
crates/lune-std-ffi/src/c/void_info.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use libffi::middle::Type;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use crate::ffi::{FfiSignedness, FfiSize};
|
||||
|
||||
use super::method_provider;
|
||||
|
||||
pub struct CVoidInfo();
|
||||
|
||||
impl FfiSignedness for CVoidInfo {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiSize for CVoidInfo {
|
||||
fn get_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl CVoidInfo {
|
||||
pub fn new() -> Self {
|
||||
Self()
|
||||
}
|
||||
pub fn get_middle_type() -> Type {
|
||||
Type::void()
|
||||
}
|
||||
pub fn stringify() -> LuaResult<String> {
|
||||
Ok(String::from("CVoid"))
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CVoidInfo {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("size", |_lua, _this| Ok(0));
|
||||
}
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
method_provider::provide_to_string(methods);
|
||||
method_provider::provide_ptr(methods);
|
||||
}
|
||||
}
|
13
crates/lune-std-ffi/src/data/box_data/flag.rs
Normal file
13
crates/lune-std-ffi/src/data/box_data/flag.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use crate::ffi::bit_field::*;
|
||||
|
||||
pub enum BoxFlag {
|
||||
Leaked,
|
||||
}
|
||||
|
||||
impl BoxFlag {
|
||||
pub const fn value(&self) -> u8 {
|
||||
match self {
|
||||
Self::Leaked => U8_MASK1,
|
||||
}
|
||||
}
|
||||
}
|
166
crates/lune-std-ffi/src/data/box_data/mod.rs
Normal file
166
crates/lune-std-ffi/src/data/box_data/mod.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
use std::{
|
||||
alloc::{self, Layout},
|
||||
boxed::Box,
|
||||
mem::ManuallyDrop,
|
||||
ptr,
|
||||
};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::helper::method_provider;
|
||||
use crate::{
|
||||
data::{association_names::REF_INNER, RefBounds, RefData, RefFlag},
|
||||
ffi::{association, bit_field::*, FfiData},
|
||||
};
|
||||
|
||||
mod flag;
|
||||
|
||||
pub use self::flag::BoxFlag;
|
||||
|
||||
const FFI_BOX_PRINT_MAX_LENGTH: usize = 1024;
|
||||
|
||||
// Reference which created by lua should not be dereferenceable
|
||||
const BOX_REF_FLAGS: u8 =
|
||||
RefFlag::Readable.value() | RefFlag::Writable.value() | RefFlag::Offsetable.value();
|
||||
|
||||
// Untyped runtime sized memory for luau.
|
||||
// This operations are safe, have boundaries check.
|
||||
pub struct BoxData {
|
||||
flags: u8,
|
||||
data: ManuallyDrop<Box<[u8]>>,
|
||||
}
|
||||
|
||||
impl BoxData {
|
||||
pub fn new(size: usize) -> Self {
|
||||
let slice = unsafe {
|
||||
Box::from_raw(ptr::slice_from_raw_parts_mut(
|
||||
alloc::alloc(Layout::array::<u8>(size).unwrap()),
|
||||
size,
|
||||
))
|
||||
};
|
||||
|
||||
Self {
|
||||
flags: 0,
|
||||
data: ManuallyDrop::new(slice),
|
||||
}
|
||||
}
|
||||
|
||||
// Stringify for pretty-print, with hex format content
|
||||
pub fn stringify(&self) -> String {
|
||||
if self.size() > FFI_BOX_PRINT_MAX_LENGTH * 2 {
|
||||
return String::from("length limit exceed");
|
||||
}
|
||||
let mut buff: String = String::with_capacity(self.size() * 2 + 2);
|
||||
buff.push_str("0x");
|
||||
for value in self.data.iter() {
|
||||
buff.push_str(format!("{:x}", value.to_be()).as_str());
|
||||
}
|
||||
buff
|
||||
}
|
||||
|
||||
pub fn leak(&mut self) {
|
||||
self.flags = u8_set(self.flags, BoxFlag::Leaked.value(), true);
|
||||
}
|
||||
|
||||
// Make FfiRef from box, with boundary check
|
||||
pub fn luaref<'lua>(
|
||||
lua: &'lua Lua,
|
||||
this: LuaAnyUserData<'lua>,
|
||||
offset: Option<isize>,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let target = this.borrow::<BoxData>()?;
|
||||
let mut bounds = RefBounds::new(0, target.size());
|
||||
let mut ptr = unsafe { target.get_inner_pointer() };
|
||||
|
||||
// Calculate offset
|
||||
if let Some(t) = offset {
|
||||
if !bounds.check_offset(t) {
|
||||
return Err(LuaError::external(format!(
|
||||
"Offset out of bounds (box.size: {}, got {})",
|
||||
target.size(),
|
||||
t
|
||||
)));
|
||||
}
|
||||
ptr = unsafe { ptr.byte_offset(t) };
|
||||
bounds = bounds.offset(t);
|
||||
}
|
||||
|
||||
let luaref = lua.create_userdata(RefData::new(ptr.cast(), BOX_REF_FLAGS, bounds))?;
|
||||
|
||||
// Make box live longer then ref
|
||||
association::set(lua, REF_INNER, &luaref, &this)?;
|
||||
|
||||
Ok(luaref)
|
||||
}
|
||||
|
||||
// Fill with zero
|
||||
pub fn zero(&mut self) {
|
||||
self.data.fill(0);
|
||||
}
|
||||
|
||||
// Get size of box
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BoxData {
|
||||
fn drop(&mut self) {
|
||||
if u8_test_not(self.flags, BoxFlag::Leaked.value()) {
|
||||
unsafe { ManuallyDrop::drop(&mut self.data) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiData for BoxData {
|
||||
fn check_inner_boundary(&self, offset: isize, size: usize) -> bool {
|
||||
if offset < 0 {
|
||||
return false;
|
||||
}
|
||||
self.size() - (offset as usize) >= size
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_inner_pointer(&self) -> *mut () {
|
||||
self.data.as_ptr().cast_mut().cast::<()>()
|
||||
}
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for BoxData {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("size", |_lua, this| Ok(this.size()));
|
||||
}
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
method_provider::provide_copy_from(methods);
|
||||
method_provider::provide_read_string(methods);
|
||||
method_provider::provide_write_string(methods);
|
||||
|
||||
// For convenience, :zero returns box itself.
|
||||
methods.add_function_mut("zero", |_lua, this: LuaAnyUserData| {
|
||||
this.borrow_mut::<BoxData>()?.zero();
|
||||
Ok(this)
|
||||
});
|
||||
methods.add_function_mut(
|
||||
"leak",
|
||||
|lua, (this, offset): (LuaAnyUserData, Option<isize>)| {
|
||||
this.borrow_mut::<BoxData>()?.leak();
|
||||
BoxData::luaref(lua, this, offset)
|
||||
},
|
||||
);
|
||||
methods.add_function(
|
||||
"ref",
|
||||
|lua, (this, offset): (LuaAnyUserData, Option<isize>)| {
|
||||
BoxData::luaref(lua, this, offset)
|
||||
},
|
||||
);
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_lua, this, ()| {
|
||||
Ok(this.stringify())
|
||||
});
|
||||
}
|
||||
}
|
191
crates/lune-std-ffi/src/data/callable_data.rs
Normal file
191
crates/lune-std-ffi/src/data/callable_data.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
use core::ffi::c_void;
|
||||
use std::{
|
||||
mem::{self, MaybeUninit},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use libffi::{
|
||||
low::{ffi_cif, CodePtr},
|
||||
raw::ffi_call,
|
||||
};
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{GetFfiData, RefData};
|
||||
use crate::ffi::{FfiArg, FfiData, FfiResult};
|
||||
|
||||
// A function pointer that luau can call. it stores libffi cif for calling convention.
|
||||
pub struct CallableData {
|
||||
cif: *mut ffi_cif,
|
||||
arg_info_list: Vec<FfiArg>,
|
||||
result_info: FfiResult,
|
||||
code: CodePtr,
|
||||
}
|
||||
|
||||
const VOID_RESULT_PTR: *mut () = ptr::null_mut();
|
||||
const ZERO_SIZE_ARG_PTR: *mut *mut c_void = ptr::null_mut();
|
||||
|
||||
// Optimization:
|
||||
// Use known size array in stack instead of creating new Vec to eliminate heap allocation
|
||||
macro_rules! create_caller {
|
||||
($len:expr) => {
|
||||
|callable: &CallableData, result: LuaValue, args: LuaMultiValue| unsafe {
|
||||
// Get `rvalue: *mut c_void` result pointer
|
||||
let result_pointer = if callable.result_info.size == 0 {
|
||||
VOID_RESULT_PTR
|
||||
} else {
|
||||
result.get_ffi_data()?.get_inner_pointer()
|
||||
}
|
||||
.cast::<c_void>();
|
||||
|
||||
// Create `avalue: *mut *mut c_void` argument list
|
||||
let mut arg_list: [MaybeUninit<*mut c_void>; $len] = [MaybeUninit::uninit(); $len];
|
||||
for (index, arg) in arg_list.iter_mut().enumerate() {
|
||||
let arg_value = args
|
||||
.get(index)
|
||||
.ok_or_else(|| LuaError::external(format!("Argument {index} required")))?
|
||||
.as_userdata()
|
||||
.ok_or_else(|| LuaError::external("Argument must be a RefData"))?;
|
||||
|
||||
if let Ok(arg_ref) = arg_value.borrow::<RefData>() {
|
||||
arg.write(arg_ref.get_inner_pointer().cast::<c_void>());
|
||||
} else {
|
||||
return Err(LuaError::external("Argument must be a RefData"));
|
||||
}
|
||||
}
|
||||
|
||||
ffi_call(
|
||||
callable.cif,
|
||||
Some(*callable.code.as_safe_fun()),
|
||||
result_pointer,
|
||||
// SAFETY: MaybeUninit<T> has the same layout as `T`, and initialized above
|
||||
mem::transmute::<[MaybeUninit<*mut c_void>; $len], [*mut c_void; $len]>(arg_list)
|
||||
.as_mut_ptr(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Optimization:
|
||||
// Call without arguments
|
||||
unsafe fn zero_size_caller(
|
||||
callable: &CallableData,
|
||||
result: LuaValue,
|
||||
_args: LuaMultiValue,
|
||||
) -> LuaResult<()> {
|
||||
let result_pointer = if callable.result_info.size == 0 {
|
||||
VOID_RESULT_PTR
|
||||
} else {
|
||||
result.get_ffi_data()?.get_inner_pointer()
|
||||
}
|
||||
.cast::<c_void>();
|
||||
|
||||
ffi_call(
|
||||
callable.cif,
|
||||
Some(*callable.code.as_safe_fun()),
|
||||
result_pointer,
|
||||
ZERO_SIZE_ARG_PTR,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Optimization: sized callers
|
||||
type Caller =
|
||||
unsafe fn(callable: &CallableData, result: LuaValue, args: LuaMultiValue) -> LuaResult<()>;
|
||||
const SIZED_CALLERS: [Caller; 13] = [
|
||||
zero_size_caller,
|
||||
create_caller!(1),
|
||||
create_caller!(2),
|
||||
create_caller!(3),
|
||||
create_caller!(4),
|
||||
create_caller!(5),
|
||||
create_caller!(6),
|
||||
create_caller!(7),
|
||||
create_caller!(8),
|
||||
create_caller!(9),
|
||||
create_caller!(10),
|
||||
create_caller!(11),
|
||||
create_caller!(12),
|
||||
];
|
||||
|
||||
impl CallableData {
|
||||
pub unsafe fn new(
|
||||
cif: *mut ffi_cif,
|
||||
arg_info_list: Vec<FfiArg>,
|
||||
result_info: FfiResult,
|
||||
function_pointer: *const (),
|
||||
) -> Self {
|
||||
Self {
|
||||
cif,
|
||||
arg_info_list,
|
||||
result_info,
|
||||
code: CodePtr::from_ptr(function_pointer.cast::<c_void>()),
|
||||
}
|
||||
}
|
||||
|
||||
// Stringify for pretty-print, with hex format address
|
||||
pub fn stringify(&self) -> String {
|
||||
format!("0x{:x}", self.code.as_ptr() as usize)
|
||||
}
|
||||
|
||||
pub unsafe fn call(&self, result: LuaValue, args: LuaMultiValue) -> LuaResult<()> {
|
||||
let arg_len = self.arg_info_list.len();
|
||||
// Optimization: use sized caller when possible
|
||||
if arg_len < SIZED_CALLERS.len() {
|
||||
return SIZED_CALLERS[arg_len](self, result, args);
|
||||
}
|
||||
|
||||
// Get `rvalue: *mut c_void` result pointer
|
||||
let result_pointer = if self.result_info.size == 0 {
|
||||
VOID_RESULT_PTR
|
||||
} else {
|
||||
result.get_ffi_data()?.get_inner_pointer()
|
||||
}
|
||||
.cast::<c_void>();
|
||||
|
||||
// Create `avalue: *mut *mut c_void` argument list
|
||||
let mut arg_list = Vec::<*mut c_void>::with_capacity(arg_len);
|
||||
for index in 0..arg_len {
|
||||
let arg_value = args
|
||||
.get(index)
|
||||
.ok_or_else(|| LuaError::external(format!("Argument {index} required")))?
|
||||
.as_userdata()
|
||||
.ok_or_else(|| LuaError::external("Argument must be a RefData"))?;
|
||||
|
||||
if let Ok(arg_ref) = arg_value.borrow::<RefData>() {
|
||||
arg_list.push(arg_ref.get_inner_pointer().cast::<c_void>());
|
||||
} else {
|
||||
return Err(LuaError::external("Argument must be a RefData"));
|
||||
}
|
||||
}
|
||||
|
||||
// Call libffi::raw::ffi_call
|
||||
ffi_call(
|
||||
self.cif,
|
||||
Some(*self.code.as_safe_fun()),
|
||||
result_pointer,
|
||||
arg_list.as_mut_ptr(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for CallableData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(
|
||||
LuaMetaMethod::Call,
|
||||
|_lua, this: &CallableData, mut args: LuaMultiValue| {
|
||||
let result = args.pop_front().ok_or_else(|| {
|
||||
LuaError::external("First argument 'result' must be a RefData, BoxData or nil")
|
||||
})?;
|
||||
unsafe { this.call(result, args) }
|
||||
},
|
||||
);
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_lua, this, ()| {
|
||||
Ok(this.stringify())
|
||||
});
|
||||
}
|
||||
}
|
161
crates/lune-std-ffi/src/data/closure_data.rs
Normal file
161
crates/lune-std-ffi/src/data/closure_data.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use core::ffi::c_void;
|
||||
use std::{borrow::Borrow, ptr};
|
||||
|
||||
use libffi::{
|
||||
low::{closure_alloc, closure_free, ffi_cif},
|
||||
raw::{ffi_closure, ffi_prep_closure_loc},
|
||||
};
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{
|
||||
association_names::REF_INNER,
|
||||
ref_data::{RefBounds, RefData, RefFlag, UNSIZED_BOUNDS},
|
||||
};
|
||||
use crate::ffi::{
|
||||
association,
|
||||
libffi_helper::{ffi_status_assert, SIZE_OF_POINTER},
|
||||
FfiArg, FfiData, FfiResult,
|
||||
};
|
||||
|
||||
// A closure that can be created with lua function.
|
||||
pub struct ClosureData {
|
||||
lua: *const Lua,
|
||||
closure: *mut ffi_closure,
|
||||
code: Box<*mut c_void>,
|
||||
arg_info_list: Vec<FfiArg>,
|
||||
result_info: FfiResult,
|
||||
func: LuaRegistryKey,
|
||||
}
|
||||
|
||||
impl Drop for ClosureData {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
closure_free(self.closure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RESULT_REF_FLAGS: u8 = RefFlag::Writable.value() | RefFlag::Offsetable.value();
|
||||
const CLOSURE_REF_FLAGS: u8 = RefFlag::Function.value();
|
||||
|
||||
// Process C -> Lua function call
|
||||
unsafe extern "C" fn callback(
|
||||
cif: *mut ffi_cif,
|
||||
result_pointer: *mut c_void,
|
||||
arg_pointers: *mut *mut c_void,
|
||||
closure_data: *mut c_void,
|
||||
) {
|
||||
let closure_data = closure_data.cast::<ClosureData>().as_ref().unwrap();
|
||||
let lua = closure_data.lua.as_ref().unwrap();
|
||||
let len = (*cif).nargs as usize;
|
||||
let mut args = Vec::<LuaValue>::with_capacity(len + 1);
|
||||
|
||||
// Push result pointer (ref)
|
||||
args.push(LuaValue::UserData(
|
||||
lua.create_userdata(RefData::new(
|
||||
result_pointer.cast::<()>(),
|
||||
RESULT_REF_FLAGS,
|
||||
RefBounds::new(0, closure_data.result_info.size),
|
||||
))
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
// Push arg pointer (ref)
|
||||
for i in 0..len {
|
||||
let arg_info = closure_data.arg_info_list.get(i).unwrap();
|
||||
args.push(LuaValue::UserData(
|
||||
lua.create_userdata(RefData::new(
|
||||
(*arg_pointers.add(i)).cast::<()>(),
|
||||
arg_info.callback_ref_flag,
|
||||
RefBounds::new(0, arg_info.size),
|
||||
))
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
closure_data
|
||||
.func
|
||||
.borrow()
|
||||
.into_lua(lua)
|
||||
.unwrap()
|
||||
.as_function()
|
||||
.unwrap()
|
||||
.call::<_, ()>(LuaMultiValue::from_vec(args))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
impl ClosureData {
|
||||
// Allocate new ffi closure with lua function
|
||||
pub fn alloc(
|
||||
lua: &Lua,
|
||||
cif: *mut ffi_cif,
|
||||
arg_info_list: Vec<FfiArg>,
|
||||
result_info: FfiResult,
|
||||
func: LuaRegistryKey,
|
||||
) -> LuaResult<LuaAnyUserData> {
|
||||
let (closure, code) = closure_alloc();
|
||||
let code = code.as_mut_ptr();
|
||||
|
||||
let closure_data = lua.create_userdata(ClosureData {
|
||||
lua: ptr::from_ref(lua),
|
||||
closure,
|
||||
code: Box::new(code),
|
||||
arg_info_list,
|
||||
result_info,
|
||||
func,
|
||||
})?;
|
||||
|
||||
let closure_data_ptr = ptr::from_ref(&*closure_data.borrow::<ClosureData>()?);
|
||||
|
||||
ffi_status_assert(unsafe {
|
||||
ffi_prep_closure_loc(
|
||||
closure,
|
||||
cif,
|
||||
Some(callback),
|
||||
closure_data_ptr.cast::<c_void>().cast_mut(),
|
||||
code,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(closure_data)
|
||||
}
|
||||
|
||||
// Stringify for pretty-print, with hex format address
|
||||
pub fn stringify(&self) -> String {
|
||||
format!("0x{:x}", unsafe { self.get_inner_pointer() } as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiData for ClosureData {
|
||||
unsafe fn get_inner_pointer(&self) -> *mut () {
|
||||
ptr::from_ref::<*mut c_void>(&*self.code)
|
||||
.cast::<()>()
|
||||
.cast_mut()
|
||||
}
|
||||
fn check_inner_boundary(&self, offset: isize, size: usize) -> bool {
|
||||
(offset as usize) + size <= SIZE_OF_POINTER
|
||||
}
|
||||
fn is_readable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn is_writable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for ClosureData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("ref", |lua, this: LuaAnyUserData| {
|
||||
let ref_data = lua.create_userdata(RefData::new(
|
||||
unsafe { this.borrow::<ClosureData>()?.get_inner_pointer() },
|
||||
CLOSURE_REF_FLAGS,
|
||||
UNSIZED_BOUNDS,
|
||||
))?;
|
||||
association::set(lua, REF_INNER, &ref_data, &this)?;
|
||||
Ok(ref_data)
|
||||
});
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_lua, this, ()| {
|
||||
Ok(this.stringify())
|
||||
});
|
||||
}
|
||||
}
|
103
crates/lune-std-ffi/src/data/helper.rs
Normal file
103
crates/lune-std-ffi/src/data/helper.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::{FfiData, GetFfiData};
|
||||
|
||||
pub mod method_provider {
|
||||
use super::*;
|
||||
|
||||
// Implement copyFrom method
|
||||
pub fn provide_copy_from<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiData + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_function(
|
||||
"copyFrom",
|
||||
|_lua,
|
||||
(this_userdata, src, length, dst_offset, src_offset): (
|
||||
LuaAnyUserData,
|
||||
LuaAnyUserData,
|
||||
usize,
|
||||
Option<isize>,
|
||||
Option<isize>,
|
||||
)| unsafe {
|
||||
let this = this_userdata.borrow::<Target>()?;
|
||||
let dst_offset = dst_offset.unwrap_or(0);
|
||||
let src_offset = src_offset.unwrap_or(0);
|
||||
let src = src.get_ffi_data()?;
|
||||
|
||||
if !src.check_inner_boundary(src_offset, length) {
|
||||
return Err(LuaError::external("Source out of bounds"));
|
||||
}
|
||||
if !this.check_inner_boundary(dst_offset, length) {
|
||||
return Err(LuaError::external("Self out of bounds"));
|
||||
}
|
||||
|
||||
this.copy_from(&src, length, dst_offset, src_offset);
|
||||
|
||||
Ok(this_userdata.clone())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Implement readString method
|
||||
pub fn provide_read_string<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiData + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_method(
|
||||
"readString",
|
||||
|lua, this, (length, offset): (usize, Option<isize>)| unsafe {
|
||||
let offset = offset.unwrap_or(0);
|
||||
|
||||
if !this.check_inner_boundary(offset, length) {
|
||||
return Err(LuaError::external("Source out of bounds"));
|
||||
}
|
||||
|
||||
lua.create_string(this.read_string(length, offset))
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Implement writeString method
|
||||
pub fn provide_write_string<'lua, Target, M>(methods: &mut M)
|
||||
where
|
||||
Target: FfiData + 'static,
|
||||
M: LuaUserDataMethods<'lua, Target>,
|
||||
{
|
||||
methods.add_function(
|
||||
"writeString",
|
||||
|_lua,
|
||||
(this_userdata, string, length, dst_offset, src_offset): (
|
||||
LuaAnyUserData,
|
||||
LuaString,
|
||||
Option<usize>,
|
||||
Option<isize>,
|
||||
Option<usize>,
|
||||
)| unsafe {
|
||||
let string_len = string.as_bytes().len();
|
||||
let dst_offset = dst_offset.unwrap_or(0);
|
||||
let src_offset = src_offset.unwrap_or(0);
|
||||
let length = length.unwrap_or_else(|| string_len - src_offset);
|
||||
let this = this_userdata.borrow::<Target>()?;
|
||||
|
||||
// Source string boundary check
|
||||
if string_len < src_offset + length {
|
||||
return Err(LuaError::external("Source out of bounds"));
|
||||
}
|
||||
|
||||
// Self boundary check
|
||||
if !this.check_inner_boundary(dst_offset, length) {
|
||||
return Err(LuaError::external("Self out of bounds"));
|
||||
}
|
||||
|
||||
this.write_string(string, length, dst_offset, src_offset);
|
||||
Ok(this_userdata.clone())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Bit operation support
|
||||
// TODO: writeBase64 and readBase64 methods
|
||||
}
|
68
crates/lune-std-ffi/src/data/lib_data.rs
Normal file
68
crates/lune-std-ffi/src/data/lib_data.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use dlopen2::raw::Library;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::{
|
||||
association_names::SYM_INNER,
|
||||
ref_data::{RefData, RefFlag, UNSIZED_BOUNDS},
|
||||
};
|
||||
use crate::ffi::association;
|
||||
|
||||
const LIB_REF_FLAGS: u8 = RefFlag::Offsetable.value()
|
||||
| RefFlag::Readable.value()
|
||||
| RefFlag::Dereferenceable.value()
|
||||
| RefFlag::Function.value();
|
||||
|
||||
// Runtime dynamic loaded libraries
|
||||
pub struct LibData {
|
||||
name: String,
|
||||
lib: Library,
|
||||
}
|
||||
|
||||
impl LibData {
|
||||
// Open library then return library handle
|
||||
pub fn new(libname: String) -> LuaResult<Self> {
|
||||
match Library::open(&libname) {
|
||||
Ok(t) => Ok(Self {
|
||||
lib: t,
|
||||
name: libname.clone(),
|
||||
}),
|
||||
Err(err) => Err(err.into_lua_err()),
|
||||
}
|
||||
}
|
||||
|
||||
// Get named symbol from library
|
||||
pub fn find_symbol<'lua>(
|
||||
lua: &'lua Lua,
|
||||
this: LuaAnyUserData<'lua>,
|
||||
name: String,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let lib = this.borrow::<LibData>()?;
|
||||
let sym = unsafe {
|
||||
lib.lib
|
||||
.symbol::<*const ()>(name.as_str())
|
||||
.map_err(LuaError::external)?
|
||||
};
|
||||
let ffi_ref =
|
||||
lua.create_userdata(RefData::new(sym.cast_mut(), LIB_REF_FLAGS, UNSIZED_BOUNDS))?;
|
||||
|
||||
// Library handle should live longer than retrieved symbol
|
||||
association::set(lua, SYM_INNER, &ffi_ref, &this)?;
|
||||
|
||||
Ok(ffi_ref)
|
||||
}
|
||||
|
||||
pub fn stringify(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for LibData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("find", |lua, (this, name): (LuaAnyUserData, String)| {
|
||||
LibData::find_symbol(lua, this, name)
|
||||
});
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_lua, this, ()| {
|
||||
Ok(this.stringify())
|
||||
});
|
||||
}
|
||||
}
|
68
crates/lune-std-ffi/src/data/mod.rs
Normal file
68
crates/lune-std-ffi/src/data/mod.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
|
||||
use mlua::prelude::*;
|
||||
|
||||
mod box_data;
|
||||
mod callable_data;
|
||||
mod closure_data;
|
||||
mod helper;
|
||||
mod lib_data;
|
||||
mod ref_data;
|
||||
|
||||
pub use self::{
|
||||
box_data::BoxData,
|
||||
callable_data::CallableData,
|
||||
closure_data::ClosureData,
|
||||
lib_data::LibData,
|
||||
ref_data::{create_nullref, RefBounds, RefData, RefFlag, UNSIZED_BOUNDS},
|
||||
};
|
||||
use crate::ffi::FfiData;
|
||||
|
||||
// Named registry keys
|
||||
mod association_names {
|
||||
pub const REF_INNER: &str = "__ref_inner";
|
||||
pub const SYM_INNER: &str = "__syn_inner";
|
||||
}
|
||||
|
||||
// Get dynamic FfiData handle from LuaValue and LuaAnyUserData
|
||||
pub trait GetFfiData {
|
||||
fn get_ffi_data(&self) -> LuaResult<Ref<dyn FfiData>>;
|
||||
fn is_ffi_data(&self) -> bool;
|
||||
}
|
||||
impl GetFfiData for LuaAnyUserData<'_> {
|
||||
fn get_ffi_data(&self) -> LuaResult<Ref<dyn FfiData>> {
|
||||
if self.is::<BoxData>() {
|
||||
Ok(self.borrow::<BoxData>()? as Ref<dyn FfiData>)
|
||||
} else if self.is::<RefData>() {
|
||||
Ok(self.borrow::<RefData>()? as Ref<dyn FfiData>)
|
||||
} else if self.is::<ClosureData>() {
|
||||
Ok(self.borrow::<ClosureData>()? as Ref<dyn FfiData>)
|
||||
} else {
|
||||
let config = ValueFormatConfig::new();
|
||||
Err(LuaError::external(format!(
|
||||
"Expected FfiBox, FfiRef or ClosureData. got {}",
|
||||
pretty_format_value(&LuaValue::UserData(self.to_owned()), &config)
|
||||
)))
|
||||
}
|
||||
}
|
||||
fn is_ffi_data(&self) -> bool {
|
||||
self.is::<BoxData>() | self.is::<RefData>() | self.is::<ClosureData>()
|
||||
}
|
||||
}
|
||||
impl GetFfiData for LuaValue<'_> {
|
||||
fn get_ffi_data(&self) -> LuaResult<Ref<dyn FfiData>> {
|
||||
self.as_userdata()
|
||||
.ok_or_else(|| {
|
||||
let config = ValueFormatConfig::new();
|
||||
LuaError::external(format!(
|
||||
"Expected FfiBox, FfiRef or ClosureData. got {}",
|
||||
pretty_format_value(self, &config)
|
||||
))
|
||||
})?
|
||||
.get_ffi_data()
|
||||
}
|
||||
fn is_ffi_data(&self) -> bool {
|
||||
self.as_userdata().map_or(false, GetFfiData::is_ffi_data)
|
||||
}
|
||||
}
|
99
crates/lune-std-ffi/src/data/ref_data/bounds.rs
Normal file
99
crates/lune-std-ffi/src/data/ref_data/bounds.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Memory boundaries
|
||||
pub struct RefBounds {
|
||||
// How much bytes available above
|
||||
pub(crate) above: usize,
|
||||
// How much bytes available below
|
||||
pub(crate) below: usize,
|
||||
}
|
||||
|
||||
pub const UNSIZED_BOUNDS: RefBounds = RefBounds {
|
||||
above: usize::MAX,
|
||||
below: usize::MAX,
|
||||
};
|
||||
|
||||
impl RefBounds {
|
||||
pub fn new(above: usize, below: usize) -> Self {
|
||||
Self { above, below }
|
||||
}
|
||||
|
||||
// Check offset is in boundary
|
||||
#[inline]
|
||||
pub fn check_offset(&self, offset: isize) -> bool {
|
||||
let offset_abs = offset.unsigned_abs();
|
||||
match offset.signum() {
|
||||
-1 => self.above >= offset_abs,
|
||||
1 => self.below >= offset_abs,
|
||||
0 => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Check boundary with specific size
|
||||
//
|
||||
// -4 ∧ ────── Above = 4
|
||||
// -3 │ (Case1)
|
||||
// -2 │ ┌──── Offset = -2 : offset >= 0 || abs(offset) <= above
|
||||
// -1 │ │
|
||||
// 0 │ │ Size = 4
|
||||
// 1 │ │ (Case2)
|
||||
// 2 │ ∨ ─── End = 2 : end = offset + size;
|
||||
// 3 │ end <= 0 || end <= below
|
||||
// 4 ∨ ────── Below = 4
|
||||
//
|
||||
#[inline]
|
||||
pub fn check_sized(&self, offset: isize, size: usize) -> bool {
|
||||
// (Case1) offset over above
|
||||
if offset < 0 && self.above < offset.unsigned_abs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// (Case2) end over below
|
||||
let end = offset + (size as isize);
|
||||
end <= 0 || self.below >= end.unsigned_abs()
|
||||
}
|
||||
|
||||
// Calculate new boundaries with offset
|
||||
// No boundary checking in here
|
||||
//
|
||||
// Above = 3
|
||||
// ∧ ───∧───
|
||||
// -3│ │ New above = 2
|
||||
// -2│ │
|
||||
// -1│ <────── Offset = -1
|
||||
// 0│ │
|
||||
// 1│ │
|
||||
// 2│ │
|
||||
// 3│ │ New below = 4
|
||||
// ∨ ───∨───
|
||||
// Below = 3
|
||||
//
|
||||
#[inline]
|
||||
pub fn offset(&self, offset: isize) -> Self {
|
||||
let sign = offset.signum();
|
||||
let offset_abs = offset.unsigned_abs();
|
||||
|
||||
Self {
|
||||
above: match sign {
|
||||
-1 => self.above - offset_abs,
|
||||
1 => self.above + offset_abs,
|
||||
0 => self.above,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
below: match sign {
|
||||
-1 => self.below + offset_abs,
|
||||
1 => self.below - offset_abs,
|
||||
0 => self.below,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RefBounds {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
above: self.above,
|
||||
below: self.below,
|
||||
}
|
||||
}
|
||||
}
|
23
crates/lune-std-ffi/src/data/ref_data/flag.rs
Normal file
23
crates/lune-std-ffi/src/data/ref_data/flag.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use crate::ffi::bit_field::*;
|
||||
|
||||
pub enum RefFlag {
|
||||
Leaked,
|
||||
Dereferenceable,
|
||||
Readable,
|
||||
Writable,
|
||||
Offsetable,
|
||||
Function,
|
||||
}
|
||||
|
||||
impl RefFlag {
|
||||
pub const fn value(&self) -> u8 {
|
||||
match self {
|
||||
Self::Leaked => U8_MASK1,
|
||||
Self::Dereferenceable => U8_MASK2,
|
||||
Self::Writable => U8_MASK3,
|
||||
Self::Readable => U8_MASK4,
|
||||
Self::Offsetable => U8_MASK5,
|
||||
Self::Function => U8_MASK6,
|
||||
}
|
||||
}
|
||||
}
|
204
crates/lune-std-ffi/src/data/ref_data/mod.rs
Normal file
204
crates/lune-std-ffi/src/data/ref_data/mod.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
use std::{mem::ManuallyDrop, ptr};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::helper::method_provider;
|
||||
use crate::{
|
||||
data::association_names::REF_INNER,
|
||||
ffi::{association, bit_field::*, FfiData},
|
||||
};
|
||||
|
||||
mod bounds;
|
||||
mod flag;
|
||||
|
||||
pub use self::{
|
||||
bounds::{RefBounds, UNSIZED_BOUNDS},
|
||||
flag::RefFlag,
|
||||
};
|
||||
|
||||
// Box:ref():ref() should not be able to modify, Only for external
|
||||
const REF_REF_FLAGS: u8 = 0;
|
||||
|
||||
const DEREF_REF_FLAG: u8 = RefFlag::Dereferenceable.value()
|
||||
| RefFlag::Function.value()
|
||||
| RefFlag::Offsetable.value()
|
||||
| RefFlag::Readable.value()
|
||||
| RefFlag::Writable.value();
|
||||
|
||||
// A referenced memory address box. Possible to read and write through types.
|
||||
pub struct RefData {
|
||||
ptr: ManuallyDrop<Box<*mut ()>>,
|
||||
pub(crate) flags: u8,
|
||||
boundary: RefBounds,
|
||||
}
|
||||
|
||||
impl RefData {
|
||||
pub fn new(ptr: *mut (), flags: u8, boundary: RefBounds) -> Self {
|
||||
Self {
|
||||
ptr: ManuallyDrop::new(Box::new(ptr)),
|
||||
flags,
|
||||
boundary,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update<'lua>(
|
||||
lua: &'lua Lua,
|
||||
this: LuaAnyUserData<'lua>,
|
||||
ptr: *mut (),
|
||||
flags: u8,
|
||||
boundary: RefBounds,
|
||||
) -> LuaResult<()> {
|
||||
let mut target = this.borrow_mut::<RefData>()?;
|
||||
association::set(lua, REF_INNER, &this, LuaNil)?;
|
||||
|
||||
**target.ptr = ptr;
|
||||
target.flags = flags;
|
||||
target.boundary = boundary;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Create reference of this reference
|
||||
pub fn luaref<'lua>(
|
||||
lua: &'lua Lua,
|
||||
this: LuaAnyUserData<'lua>,
|
||||
) -> LuaResult<LuaAnyUserData<'lua>> {
|
||||
let target = this.borrow::<RefData>()?;
|
||||
|
||||
let luaref = lua.create_userdata(RefData::new(
|
||||
ptr::from_ref(&**target.ptr) as *mut (),
|
||||
REF_REF_FLAGS,
|
||||
RefBounds {
|
||||
below: 0,
|
||||
above: size_of::<usize>(),
|
||||
},
|
||||
))?;
|
||||
|
||||
// Make sure new reference live longer then this reference
|
||||
association::set(lua, REF_INNER, &luaref, &this)?;
|
||||
|
||||
Ok(luaref)
|
||||
}
|
||||
|
||||
// Dereference this reference
|
||||
pub unsafe fn dereference(&self) -> LuaResult<Self> {
|
||||
// Check dereferenceable
|
||||
if !u8_test(self.flags, RefFlag::Dereferenceable.value()) {
|
||||
return Err(LuaError::external("Reference is not dereferenceable"));
|
||||
}
|
||||
|
||||
// Check boundary
|
||||
if !self.boundary.check_sized(0, size_of::<usize>()) {
|
||||
return Err(LuaError::external("Out of bounds"));
|
||||
}
|
||||
|
||||
Ok(Self::new(
|
||||
*self.ptr.cast::<*mut ()>(),
|
||||
DEREF_REF_FLAG,
|
||||
UNSIZED_BOUNDS,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
// * ManuallyDrop wrapper
|
||||
// * Box wrapper
|
||||
(**self.ptr) as usize == 0
|
||||
}
|
||||
|
||||
pub fn leak(&mut self) {
|
||||
self.flags = u8_set(self.flags, RefFlag::Leaked.value(), true);
|
||||
}
|
||||
|
||||
// Create new reference with specific offset from this reference
|
||||
pub unsafe fn offset(&self, offset: isize) -> LuaResult<Self> {
|
||||
// Check offsetable
|
||||
if u8_test_not(self.flags, RefFlag::Offsetable.value()) {
|
||||
return Err(LuaError::external("Reference is not offsetable"));
|
||||
}
|
||||
|
||||
// Check boundary
|
||||
if !self.boundary.check_offset(offset) {
|
||||
return Err(LuaError::external(format!(
|
||||
"Offset out of bounds (high: {}, low: {}, got {})",
|
||||
self.boundary.above, self.boundary.below, offset
|
||||
)));
|
||||
}
|
||||
|
||||
let boundary = self.boundary.offset(offset);
|
||||
Ok(Self::new(
|
||||
self.ptr.byte_offset(offset),
|
||||
u8_set(self.flags, RefFlag::Leaked.value(), false),
|
||||
boundary,
|
||||
))
|
||||
}
|
||||
|
||||
// Stringify for pretty-print, with hex format address
|
||||
pub fn stringify(&self) -> String {
|
||||
format!("0x{:x}", **self.ptr as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RefData {
|
||||
fn drop(&mut self) {
|
||||
if u8_test_not(self.flags, RefFlag::Leaked.value()) {
|
||||
unsafe { ManuallyDrop::drop(&mut self.ptr) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiData for RefData {
|
||||
fn check_inner_boundary(&self, offset: isize, size: usize) -> bool {
|
||||
self.boundary.check_sized(offset, size)
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn get_inner_pointer(&self) -> *mut () {
|
||||
**self.ptr
|
||||
}
|
||||
fn is_readable(&self) -> bool {
|
||||
u8_test(self.flags, RefFlag::Readable.value())
|
||||
}
|
||||
fn is_writable(&self) -> bool {
|
||||
u8_test(self.flags, RefFlag::Writable.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for RefData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
method_provider::provide_copy_from(methods);
|
||||
method_provider::provide_read_string(methods);
|
||||
method_provider::provide_write_string(methods);
|
||||
|
||||
methods.add_method("deref", |_lua, this, ()| unsafe { this.dereference() });
|
||||
methods.add_function("offset", |lua, (this, offset): (LuaAnyUserData, isize)| {
|
||||
let ffiref = unsafe { this.borrow::<RefData>()?.offset(offset)? };
|
||||
let userdata = lua.create_userdata(ffiref)?;
|
||||
|
||||
// If the ref holds a box or reference, make sure the new ref also holds it
|
||||
if let Some(t) = association::get(lua, REF_INNER, &this)? {
|
||||
association::set(lua, REF_INNER, &userdata, t)?;
|
||||
}
|
||||
|
||||
Ok(userdata)
|
||||
});
|
||||
methods.add_function_mut("leak", |lua, this: LuaAnyUserData| {
|
||||
this.borrow_mut::<RefData>()?.leak();
|
||||
RefData::luaref(lua, this)
|
||||
});
|
||||
methods.add_function("ref", |lua, this: LuaAnyUserData| {
|
||||
RefData::luaref(lua, this)
|
||||
});
|
||||
methods.add_method("isNull", |_lua, this, ()| Ok(this.is_null()));
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_lua, this, ()| {
|
||||
Ok(this.stringify())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_nullref(lua: &Lua) -> LuaResult<LuaAnyUserData> {
|
||||
lua.create_userdata(RefData::new(
|
||||
ptr::null_mut::<()>().cast(),
|
||||
0,
|
||||
// usize::MAX means that nullptr is can be 'any' pointer type
|
||||
UNSIZED_BOUNDS,
|
||||
))
|
||||
}
|
52
crates/lune-std-ffi/src/ffi/association.rs
Normal file
52
crates/lune-std-ffi/src/ffi/association.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
// This is a small library that helps you set the dependencies of data in Lua.
|
||||
// In FFI, there is often data that is dependent on other data.
|
||||
// However, if you use user_value to inform Lua of the dependency,
|
||||
// a table will be created for each userdata.
|
||||
// To prevent this, we place a weak reference table in the named registry
|
||||
// and simulate what mlua does.
|
||||
|
||||
// If the dependency is deep, the value may be completely destroyed when
|
||||
// gc is performed multiple times. To prevent this situation, FFI should copy
|
||||
// dependency if possible.
|
||||
|
||||
// You can delete the relationship by changing 'associated' to nil
|
||||
#[inline]
|
||||
pub fn set<'lua, T, U>(lua: &'lua Lua, regname: &str, value: T, associated: U) -> LuaResult<()>
|
||||
where
|
||||
T: IntoLua<'lua>,
|
||||
U: IntoLua<'lua>,
|
||||
{
|
||||
let table = match lua.named_registry_value::<LuaValue>(regname)? {
|
||||
LuaValue::Nil => {
|
||||
let table = lua.create_table()?;
|
||||
lua.set_named_registry_value(regname, table.clone())?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.set("__mode", "k")?;
|
||||
table.set_metatable(Some(meta));
|
||||
table
|
||||
}
|
||||
LuaValue::Table(t) => t,
|
||||
_ => panic!(""),
|
||||
};
|
||||
|
||||
table.set(value, associated)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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]
|
||||
pub fn get<'lua, T>(lua: &'lua Lua, regname: &str, value: T) -> LuaResult<Option<LuaValue<'lua>>>
|
||||
where
|
||||
T: IntoLua<'lua>,
|
||||
{
|
||||
match lua.named_registry_value::<LuaValue>(regname)? {
|
||||
LuaValue::Nil => Ok(None),
|
||||
LuaValue::Table(t) => Ok(Some(t.get(value)?)),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
31
crates/lune-std-ffi/src/ffi/bit_field.rs
Normal file
31
crates/lune-std-ffi/src/ffi/bit_field.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
#![allow(unused)]
|
||||
|
||||
// Simple bit field library for handling data flags
|
||||
|
||||
pub const U8_MASK1: u8 = 1;
|
||||
pub const U8_MASK2: u8 = 2;
|
||||
pub const U8_MASK3: u8 = 4;
|
||||
pub const U8_MASK4: u8 = 8;
|
||||
pub const U8_MASK5: u8 = 16;
|
||||
pub const U8_MASK6: u8 = 32;
|
||||
pub const U8_MASK7: u8 = 64;
|
||||
pub const U8_MASK8: u8 = 128;
|
||||
|
||||
#[inline]
|
||||
pub fn u8_test(bits: u8, mask: u8) -> bool {
|
||||
bits & mask != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn u8_test_not(bits: u8, mask: u8) -> bool {
|
||||
bits & mask == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn u8_set(bits: u8, mask: u8, val: bool) -> u8 {
|
||||
if val {
|
||||
bits | mask
|
||||
} else {
|
||||
bits & !mask
|
||||
}
|
||||
}
|
36
crates/lune-std-ffi/src/ffi/cast.rs
Normal file
36
crates/lune-std-ffi/src/ffi/cast.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use num::cast::AsPrimitive;
|
||||
|
||||
use super::FfiData;
|
||||
|
||||
// Cast number type to another number type, with num::cast library
|
||||
#[inline]
|
||||
pub fn num_cast<From, Into>(
|
||||
from: &Ref<dyn FfiData>,
|
||||
into: &Ref<dyn FfiData>,
|
||||
from_offset: isize,
|
||||
into_offset: isize,
|
||||
) -> LuaResult<()>
|
||||
where
|
||||
From: AsPrimitive<Into>,
|
||||
Into: 'static + Copy,
|
||||
{
|
||||
let from_ptr = unsafe {
|
||||
from.get_inner_pointer()
|
||||
.byte_offset(from_offset)
|
||||
.cast::<From>()
|
||||
};
|
||||
let into_ptr = unsafe {
|
||||
into.get_inner_pointer()
|
||||
.byte_offset(into_offset)
|
||||
.cast::<Into>()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
*into_ptr = (*from_ptr).as_();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
43
crates/lune-std-ffi/src/ffi/libffi_helper.rs
Normal file
43
crates/lune-std-ffi/src/ffi/libffi_helper.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use std::ptr::{self, null_mut};
|
||||
|
||||
use libffi::{low, raw};
|
||||
use mlua::prelude::*;
|
||||
|
||||
pub const SIZE_OF_POINTER: usize = size_of::<*mut ()>();
|
||||
|
||||
// Get ensured size of ctype (raw::libffi_type)
|
||||
pub fn get_ensured_size(ffi_type: *mut raw::ffi_type) -> LuaResult<usize> {
|
||||
let mut cif = low::ffi_cif::default();
|
||||
let result = unsafe {
|
||||
raw::ffi_prep_cif(
|
||||
ptr::from_mut(&mut cif),
|
||||
raw::ffi_abi_FFI_DEFAULT_ABI,
|
||||
0,
|
||||
ffi_type,
|
||||
null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
ffi_status_assert(result)?;
|
||||
unsafe { Ok((*ffi_type).size) }
|
||||
}
|
||||
|
||||
// Converts ffi status into &str for formatting
|
||||
const FFI_STATUS_NAMES: [&str; 4] = [
|
||||
"ffi_status_FFI_OK",
|
||||
"ffi_status_FFI_BAD_TYPEDEF",
|
||||
"ffi_status_FFI_BAD_ABI",
|
||||
"ffi_status_FFI_BAD_ARGTYPE",
|
||||
];
|
||||
|
||||
// Check ffi_result is OK
|
||||
pub fn ffi_status_assert(result: raw::ffi_status) -> LuaResult<()> {
|
||||
if result == raw::ffi_status_FFI_OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(LuaError::external(format!(
|
||||
"ffi_status assertion failed. expected result {}, got {}",
|
||||
FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[result as usize]
|
||||
)))
|
||||
}
|
||||
}
|
129
crates/lune-std-ffi/src/ffi/mod.rs
Normal file
129
crates/lune-std-ffi/src/ffi/mod.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use std::cell::Ref;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
pub mod association;
|
||||
pub mod bit_field;
|
||||
mod cast;
|
||||
pub mod libffi_helper;
|
||||
|
||||
pub use self::cast::num_cast;
|
||||
|
||||
// Common type information
|
||||
pub trait FfiSize {
|
||||
fn get_size(&self) -> usize;
|
||||
}
|
||||
pub trait FfiSignedness {
|
||||
fn get_signedness(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Provide conversion between luau value and ffi types
|
||||
pub trait FfiConvert {
|
||||
// Write LuaValue into FfiData
|
||||
unsafe fn value_into_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
value: LuaValue<'lua>,
|
||||
) -> LuaResult<()>;
|
||||
|
||||
// Read LuaValue from FfiData
|
||||
unsafe fn value_from_data<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
offset: isize,
|
||||
data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<LuaValue<'lua>>;
|
||||
|
||||
unsafe fn copy_data(
|
||||
&self,
|
||||
lua: &Lua,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
dst: &Ref<dyn FfiData>,
|
||||
src: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<()>;
|
||||
|
||||
unsafe fn stringify_data(
|
||||
&self,
|
||||
_lua: &Lua,
|
||||
_offset: isize,
|
||||
_data_handle: &Ref<dyn FfiData>,
|
||||
) -> LuaResult<String> {
|
||||
Err(LuaError::external("Stringify method not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
// Provide read, write, boundary check methods for datas
|
||||
pub trait FfiData {
|
||||
fn check_inner_boundary(&self, offset: isize, size: usize) -> bool;
|
||||
unsafe fn get_inner_pointer(&self) -> *mut ();
|
||||
fn is_writable(&self) -> bool;
|
||||
fn is_readable(&self) -> bool;
|
||||
unsafe fn copy_from(
|
||||
&self,
|
||||
src: &Ref<dyn FfiData>,
|
||||
length: usize,
|
||||
dst_offset: isize,
|
||||
src_offset: isize,
|
||||
) {
|
||||
self.get_inner_pointer()
|
||||
.cast::<u8>()
|
||||
.byte_offset(dst_offset)
|
||||
.copy_from(
|
||||
src.get_inner_pointer().cast::<u8>().byte_offset(src_offset),
|
||||
length,
|
||||
);
|
||||
}
|
||||
unsafe fn read_string(&self, length: usize, offset: isize) -> Vec<u8> {
|
||||
let mut string = Vec::<u8>::with_capacity(length);
|
||||
string.as_mut_ptr().copy_from(
|
||||
self.get_inner_pointer().cast::<u8>().byte_offset(offset),
|
||||
length,
|
||||
);
|
||||
string.set_len(length);
|
||||
string
|
||||
}
|
||||
unsafe fn write_string(
|
||||
&self,
|
||||
src: LuaString,
|
||||
length: usize,
|
||||
dst_offset: isize,
|
||||
src_offset: usize,
|
||||
) {
|
||||
self.get_inner_pointer()
|
||||
.cast::<u8>()
|
||||
.byte_offset(dst_offset)
|
||||
.copy_from(
|
||||
src.as_bytes().as_ptr().cast::<u8>().byte_add(src_offset),
|
||||
length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Function argument informations
|
||||
pub struct FfiArg {
|
||||
pub size: usize,
|
||||
pub callback_ref_flag: u8,
|
||||
}
|
||||
impl Clone for FfiArg {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
size: self.size,
|
||||
callback_ref_flag: self.callback_ref_flag,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function result information
|
||||
pub struct FfiResult {
|
||||
pub size: usize,
|
||||
}
|
||||
impl Clone for FfiResult {
|
||||
fn clone(&self) -> Self {
|
||||
Self { size: self.size }
|
||||
}
|
||||
}
|
39
crates/lune-std-ffi/src/lib.rs
Normal file
39
crates/lune-std-ffi/src/lib.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
#![allow(clippy::cargo_common_metadata)]
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use libc::free;
|
||||
use lune_utils::TableBuilder;
|
||||
use mlua::prelude::*;
|
||||
|
||||
mod c;
|
||||
mod data;
|
||||
mod ffi;
|
||||
|
||||
use crate::{
|
||||
c::{export_c, export_fixed_types},
|
||||
data::{create_nullref, BoxData, GetFfiData, LibData},
|
||||
};
|
||||
|
||||
/**
|
||||
Creates the `ffi` standard library module.
|
||||
|
||||
# Errors
|
||||
|
||||
Errors when out of memory.
|
||||
*/
|
||||
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let result = TableBuilder::new(lua)?
|
||||
.with_function("nullRef", |lua, ()| create_nullref(lua))?
|
||||
.with_function("box", |_lua, size: usize| Ok(BoxData::new(size)))?
|
||||
.with_function("open", |_lua, name: String| LibData::new(name))?
|
||||
.with_function("isInteger", |_lua, num: LuaValue| Ok(num.is_integer()))?
|
||||
.with_function("free", |_lua, data: LuaAnyUserData| {
|
||||
unsafe { free(data.get_ffi_data()?.get_inner_pointer().cast::<c_void>()) };
|
||||
Ok(())
|
||||
})?
|
||||
.with_values(export_fixed_types(lua)?)?
|
||||
.with_value("c", export_c(lua)?)?;
|
||||
|
||||
result.build_readonly()
|
||||
}
|
|
@ -24,6 +24,7 @@ default = [
|
|||
"serde",
|
||||
"stdio",
|
||||
"task",
|
||||
"ffi",
|
||||
]
|
||||
|
||||
datetime = ["dep:lune-std-datetime"]
|
||||
|
@ -36,6 +37,7 @@ roblox = ["dep:lune-std-roblox"]
|
|||
serde = ["dep:lune-std-serde"]
|
||||
stdio = ["dep:lune-std-stdio"]
|
||||
task = ["dep:lune-std-task"]
|
||||
ffi = ["dep:lune-std-ffi"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.9", features = ["luau"] }
|
||||
|
@ -57,3 +59,4 @@ lune-std-roblox = { optional = true, version = "0.1.4", path = "../lune-std-robl
|
|||
lune-std-serde = { optional = true, version = "0.1.2", path = "../lune-std-serde" }
|
||||
lune-std-stdio = { optional = true, version = "0.1.2", path = "../lune-std-stdio" }
|
||||
lune-std-task = { optional = true, version = "0.1.2", path = "../lune-std-task" }
|
||||
lune-std-ffi = { optional = true, version = "0.1.1", path = "../lune-std-ffi" }
|
||||
|
|
|
@ -6,10 +6,12 @@ mod global;
|
|||
mod globals;
|
||||
mod library;
|
||||
mod luaurc;
|
||||
mod unsafe_library;
|
||||
|
||||
pub use self::global::LuneStandardGlobal;
|
||||
pub use self::globals::version::set_global_version;
|
||||
pub use self::library::LuneStandardLibrary;
|
||||
pub use self::unsafe_library::{get_unsafe_library_enabled, set_unsafe_library_enabled};
|
||||
|
||||
/**
|
||||
Injects all standard globals into the given Lua state / VM.
|
||||
|
|
|
@ -2,8 +2,10 @@ use std::str::FromStr;
|
|||
|
||||
use mlua::prelude::*;
|
||||
|
||||
use crate::get_unsafe_library_enabled;
|
||||
|
||||
/**
|
||||
A standard library provided by Lune.
|
||||
A standard library probloxrovided by Lune.
|
||||
*/
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[rustfmt::skip]
|
||||
|
@ -18,6 +20,7 @@ pub enum LuneStandardLibrary {
|
|||
#[cfg(feature = "serde")] Serde,
|
||||
#[cfg(feature = "stdio")] Stdio,
|
||||
#[cfg(feature = "roblox")] Roblox,
|
||||
#[cfg(feature = "ffi")] Ffi,
|
||||
}
|
||||
|
||||
impl LuneStandardLibrary {
|
||||
|
@ -36,6 +39,7 @@ impl LuneStandardLibrary {
|
|||
#[cfg(feature = "serde")] Self::Serde,
|
||||
#[cfg(feature = "stdio")] Self::Stdio,
|
||||
#[cfg(feature = "roblox")] Self::Roblox,
|
||||
#[cfg(feature = "ffi")] Self::Ffi,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -56,6 +60,31 @@ impl LuneStandardLibrary {
|
|||
#[cfg(feature = "serde")] Self::Serde => "serde",
|
||||
#[cfg(feature = "stdio")] Self::Stdio => "stdio",
|
||||
#[cfg(feature = "roblox")] Self::Roblox => "roblox",
|
||||
#[cfg(feature = "ffi")] Self::Ffi => "ffi",
|
||||
|
||||
_ => unreachable!("no standard library enabled"),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Gets whether the library is unsafe.
|
||||
*/
|
||||
#[must_use]
|
||||
#[rustfmt::skip]
|
||||
#[allow(unreachable_patterns)]
|
||||
pub fn is_unsafe(&self) -> bool {
|
||||
match self {
|
||||
#[cfg(feature = "datetime")] Self::DateTime => false,
|
||||
#[cfg(feature = "fs")] Self::Fs => false,
|
||||
#[cfg(feature = "luau")] Self::Luau => false,
|
||||
#[cfg(feature = "net")] Self::Net => false,
|
||||
#[cfg(feature = "task")] Self::Task => false,
|
||||
#[cfg(feature = "process")] Self::Process => false,
|
||||
#[cfg(feature = "regex")] Self::Regex => false,
|
||||
#[cfg(feature = "serde")] Self::Serde => false,
|
||||
#[cfg(feature = "stdio")] Self::Stdio => false,
|
||||
#[cfg(feature = "roblox")] Self::Roblox => false,
|
||||
#[cfg(feature = "ffi")] Self::Ffi => true,
|
||||
|
||||
_ => unreachable!("no standard library enabled"),
|
||||
}
|
||||
|
@ -66,11 +95,15 @@ impl LuneStandardLibrary {
|
|||
|
||||
# Errors
|
||||
|
||||
If the library could not be created.
|
||||
If the library could not be created, or if requiring an unsafe library without enabling the unsafe library.
|
||||
*/
|
||||
#[rustfmt::skip]
|
||||
#[allow(unreachable_patterns)]
|
||||
pub fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult<LuaMultiValue<'lua>> {
|
||||
if self.is_unsafe() && !get_unsafe_library_enabled(lua) {
|
||||
return Err(LuaError::external(format!("Standard library '{}' requires unsafe library enabled", self.name())));
|
||||
}
|
||||
|
||||
let res: LuaResult<LuaTable> = match self {
|
||||
#[cfg(feature = "datetime")] Self::DateTime => lune_std_datetime::module(lua),
|
||||
#[cfg(feature = "fs")] Self::Fs => lune_std_fs::module(lua),
|
||||
|
@ -82,6 +115,7 @@ impl LuneStandardLibrary {
|
|||
#[cfg(feature = "serde")] Self::Serde => lune_std_serde::module(lua),
|
||||
#[cfg(feature = "stdio")] Self::Stdio => lune_std_stdio::module(lua),
|
||||
#[cfg(feature = "roblox")] Self::Roblox => lune_std_roblox::module(lua),
|
||||
#[cfg(feature = "ffi")] Self::Ffi => lune_std_ffi::module(lua),
|
||||
|
||||
_ => unreachable!("no standard library enabled"),
|
||||
};
|
||||
|
@ -111,6 +145,7 @@ impl FromStr for LuneStandardLibrary {
|
|||
#[cfg(feature = "serde")] "serde" => Self::Serde,
|
||||
#[cfg(feature = "stdio")] "stdio" => Self::Stdio,
|
||||
#[cfg(feature = "roblox")] "roblox" => Self::Roblox,
|
||||
#[cfg(feature = "ffi")] "ffi" => Self::Ffi,
|
||||
|
||||
_ => {
|
||||
return Err(format!(
|
||||
|
|
26
crates/lune-std/src/unsafe_library.rs
Normal file
26
crates/lune-std/src/unsafe_library.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
struct UnsafeLibrary(bool);
|
||||
|
||||
/**
|
||||
Override unsafe library allowance
|
||||
*/
|
||||
pub fn set_unsafe_library_enabled(lua: &Lua, enabled: bool) {
|
||||
lua.set_app_data(UnsafeLibrary(enabled));
|
||||
}
|
||||
|
||||
/**
|
||||
Returns whether unsafe libraries are allowed
|
||||
|
||||
# Panics
|
||||
|
||||
Panic if `UnsafeLib` app data doesn't exist.
|
||||
*/
|
||||
#[must_use]
|
||||
pub fn get_unsafe_library_enabled(lua: &Lua) -> bool {
|
||||
if let Some(app_data) = lua.app_data_ref::<UnsafeLibrary>() {
|
||||
app_data.0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ std-roblox = ["dep:lune-std", "lune-std/roblox", "dep:lune-roblox"]
|
|||
std-serde = ["dep:lune-std", "lune-std/serde"]
|
||||
std-stdio = ["dep:lune-std", "lune-std/stdio"]
|
||||
std-task = ["dep:lune-std", "lune-std/task"]
|
||||
std-ffi = ["dep:lune-std", "lune-std/ffi"]
|
||||
|
||||
std = [
|
||||
"std-datetime",
|
||||
|
@ -42,6 +43,7 @@ std = [
|
|||
"std-serde",
|
||||
"std-stdio",
|
||||
"std-task",
|
||||
"std-ffi",
|
||||
]
|
||||
|
||||
cli = ["dep:clap", "dep:include_dir", "dep:rustyline", "dep:zip_next"]
|
||||
|
|
|
@ -17,7 +17,11 @@ enum PromptState {
|
|||
|
||||
/// Launch an interactive REPL (default)
|
||||
#[derive(Debug, Clone, Default, Parser)]
|
||||
pub struct ReplCommand {}
|
||||
pub struct ReplCommand {
|
||||
/// Allow unsafe libraries
|
||||
#[clap(long, action)]
|
||||
r#unsafe: bool,
|
||||
}
|
||||
|
||||
impl ReplCommand {
|
||||
pub async fn run(self) -> Result<ExitCode> {
|
||||
|
@ -38,7 +42,7 @@ impl ReplCommand {
|
|||
let mut prompt_state = PromptState::Regular;
|
||||
let mut source_code = String::new();
|
||||
|
||||
let mut lune_instance = Runtime::new();
|
||||
let mut lune_instance = Runtime::new().set_unsafe_library_enabled(self.r#unsafe);
|
||||
|
||||
loop {
|
||||
let prompt = match prompt_state {
|
||||
|
|
|
@ -14,6 +14,9 @@ use super::utils::files::{discover_script_path_including_lune_dirs, strip_sheban
|
|||
/// Run a script
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
pub struct RunCommand {
|
||||
/// Allow unsafe libraries
|
||||
#[clap(long, action)]
|
||||
r#unsafe: bool,
|
||||
/// Script name or full path to the file to run
|
||||
script_path: String,
|
||||
/// Arguments to pass to the script, stored in process.args
|
||||
|
@ -41,7 +44,9 @@ impl RunCommand {
|
|||
};
|
||||
|
||||
// Create a new lune runtime with all globals & run the script
|
||||
let mut rt = Runtime::new().with_args(self.script_args);
|
||||
let mut rt = Runtime::new()
|
||||
.with_args(self.script_args)
|
||||
.set_unsafe_library_enabled(self.r#unsafe);
|
||||
|
||||
let result = rt
|
||||
.run(&script_display_name, strip_shebang(script_contents))
|
||||
|
|
|
@ -54,6 +54,7 @@ impl RuntimeInner {
|
|||
feature = "std-serde",
|
||||
feature = "std-stdio",
|
||||
feature = "std-task",
|
||||
feature = "std-ffi",
|
||||
))]
|
||||
{
|
||||
lune_std::set_global_version(lua, env!("CARGO_PKG_VERSION"));
|
||||
|
@ -76,6 +77,7 @@ impl RuntimeInner {
|
|||
feature = "std-serde",
|
||||
feature = "std-stdio",
|
||||
feature = "std-task",
|
||||
feature = "std-ffi",
|
||||
))]
|
||||
{
|
||||
let g_table = lune_std::LuneStandardGlobal::GTable;
|
||||
|
@ -130,6 +132,15 @@ impl Runtime {
|
|||
self
|
||||
}
|
||||
|
||||
/**
|
||||
Sets arguments to give in `process.args` for Lune scripts.
|
||||
*/
|
||||
#[must_use]
|
||||
pub fn set_unsafe_library_enabled(self, enabled: bool) -> Self {
|
||||
lune_std::set_unsafe_library_enabled(self.inner.lua(), enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/**
|
||||
Runs a Lune script inside of the current runtime.
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ macro_rules! create_tests {
|
|||
// The rest of the test logic can continue as normal
|
||||
let full_name = format!("{}/tests/{}.luau", workspace_dir.display(), $value);
|
||||
let script = read_to_string(&full_name).await?;
|
||||
let mut lune = Runtime::new().with_args(
|
||||
let mut lune = Runtime::new().set_unsafe_library_enabled(true).with_args(
|
||||
ARGS
|
||||
.clone()
|
||||
.iter()
|
||||
|
@ -99,6 +99,34 @@ create_tests! {
|
|||
datetime_to_universal_time: "datetime/toUniversalTime",
|
||||
}
|
||||
|
||||
#[cfg(feature = "std-ffi")]
|
||||
create_tests! {
|
||||
ffi_external_closure_call_closure: "ffi/external_closure/callClosure",
|
||||
ffi_external_closure_call_closure_with_pointer: "ffi/external_closure/callClosureWithPointer",
|
||||
ffi_external_closure_call_hello_world: "ffi/external_closure/callHelloWorld",
|
||||
ffi_external_math_add_int: "ffi/external_math/addInt",
|
||||
ffi_external_math_mul_int: "ffi/external_math/mulInt",
|
||||
ffi_external_pointer_pointer_read: "ffi/external_pointer/pointerRead",
|
||||
ffi_external_pointer_pointer_write: "ffi/external_pointer/pointerWrite",
|
||||
ffi_external_print_hello_world: "ffi/external_print/helloWorld",
|
||||
ffi_external_struct_ab: "ffi/external_struct/ab",
|
||||
ffi_pretty_print_arr: "ffi/pretty_print/arr",
|
||||
ffi_pretty_print_box: "ffi/pretty_print/box",
|
||||
ffi_pretty_print_fn: "ffi/pretty_print/fn",
|
||||
ffi_pretty_print_ptr: "ffi/pretty_print/ptr",
|
||||
ffi_pretty_print_struct: "ffi/pretty_print/struct",
|
||||
ffi_pretty_print_type: "ffi/pretty_print/type",
|
||||
ffi_types_arr: "ffi/types/arr",
|
||||
ffi_types_ptr: "ffi/types/ptr",
|
||||
ffi_types_struct: "ffi/types/struct",
|
||||
ffi_cast: "ffi/cast",
|
||||
ffi_free: "ffi/free",
|
||||
ffi_is_integer: "ffi/isInteger",
|
||||
ffi_read_boundary: "ffi/readBoundary",
|
||||
ffi_read_write_string: "ffi/stringReadWrite",
|
||||
ffi_write_boundary: "ffi/writeBoundary",
|
||||
}
|
||||
|
||||
#[cfg(feature = "std-fs")]
|
||||
create_tests! {
|
||||
fs_files: "fs/files",
|
||||
|
|
113
tests/ffi/README.md
Normal file
113
tests/ffi/README.md
Normal file
|
@ -0,0 +1,113 @@
|
|||
<!-- markdownlint-disable MD036 -->
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
|
||||
# `tests/ffi`
|
||||
|
||||
## Requirements
|
||||
|
||||
gcc for library compiling (for external-\*)
|
||||
|
||||
## Test Results
|
||||
|
||||
**External tests**
|
||||
|
||||
- [x] [external_closure](./external_closure/init.luau)
|
||||
- [x] [external_math](./external_math/init.luau)
|
||||
- [x] [external_pointer](./external_pointer/init.luau)
|
||||
- [x] [external_print](./external_print/init.luau)
|
||||
- [x] [external_struct](./external_struct/init.luau)
|
||||
|
||||
**Luau-side**
|
||||
|
||||
- [x] [cast](./cast.luau)
|
||||
- [x] [free](./free.luau)
|
||||
- [x] [isInteger](./isInteger.luau)
|
||||
- [x] [read_boundary](./read_boundary.luau)
|
||||
- [x] [write_boundary](./write_boundary.luau)
|
||||
|
||||
**Types**
|
||||
|
||||
- [x] [arr](./types/arr.luau)
|
||||
- [x] [ptr](./types/ptr.luau)
|
||||
- [x] [struct](./types/struct.luau)
|
||||
|
||||
**Pretty Print**
|
||||
|
||||
- [x] [arr](./pretty_print/arr.luau)
|
||||
- [ ] [box](./pretty_print/box.luau) Need assertion
|
||||
- [ ] [ref](./pretty_print/ref.luau) Need assertion
|
||||
- [ ] [lib](./pretty_print/lib.luau) Need assertion
|
||||
- [x] [fn](./pretty_print/fn.luau)
|
||||
- [x] [ptr](./pretty_print/ptr.luau)
|
||||
- [x] [struct](./pretty_print/struct.luau)
|
||||
- [x] [type](./pretty_print/type.luau)
|
||||
|
||||
## Benchmark Results
|
||||
|
||||
> Note: LuaJit's os.clock function returns process CPU time (used) which much smaller then Luau's os.clock output. In this benchmark, luau uses 'time.h' instead of os.clock. See [utility/proc_clock](./utility/proc_clock/init.luau)
|
||||
|
||||
<details><summary><h3><a href="./benchmark/external_call/init.luau">benchmark/external_call</a></h3></summary>
|
||||
|
||||
**Target external c function**
|
||||
|
||||
```c
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
bench_scale = 1000000
|
||||
|
||||
**Lune ffi**
|
||||
|
||||
Command: `cargo run run tests/ffi/benchmark/external_call`
|
||||
Command: `cargo run --profile=release run tests/ffi/benchmark/external_call`
|
||||
|
||||
- Device1-Linux-PVE
|
||||
Lune release target: 0.205127 (sec)
|
||||
Lune dev target: 1.556489 (sec)
|
||||
|
||||
> Commit: ddf0c4c
|
||||
|
||||
- Device2-Windows-11
|
||||
Lune release target: 0.1875 (sec)
|
||||
Lune dev target: ? SEGFUALT (sec)
|
||||
|
||||
> Commit: ddf0c4c
|
||||
|
||||
**C**
|
||||
|
||||
- Device1-Linux-PVE: 0.001949 (sec)
|
||||
> gcc (GCC) 14.2.1 20240910
|
||||
|
||||
**LuaJit ffi**
|
||||
|
||||
Command: `luajit tests/ffi/benchmark/external_call/luajit.lua`
|
||||
|
||||
- Device1-Linux-PVE: 0.001682 (sec)
|
||||
> LuaJIT 2.1.1727870382
|
||||
> (flags = JIT ON SSE3 SSE4.1 BMI2 fold cse dce fwd dse narrow loop abc sink fuse)
|
||||
|
||||
**Deno ffi**
|
||||
|
||||
Command: `deno run --unstable-ffi --allow-ffi ./tests/ffi/benchmark/external_call/deno.ts`
|
||||
|
||||
- Device1-Linux-PVE: 0.006384 (sec)
|
||||
> Deno 1.46.3 (v8 = 12.9.202.5-rusty)
|
||||
|
||||
**Sysinformation**
|
||||
|
||||
- Device1-Linux-PVE
|
||||
|
||||
> CPU: AMD Ryzen 5 7600 (12) @ 5.1
|
||||
> MEM: 61898MiB 5600 MT/s
|
||||
> KERNEL: 6.8.12-2-pve (Proxmox VE 8.2.7 x86_64)
|
||||
|
||||
- Device2-Windows-11
|
||||
|
||||
> CPU: AMD Ryzen 5 7600 (4) @ 3.800GHz
|
||||
> MEM: 12250MiB 5600 MT/s
|
||||
> KERNEL: 10.0.22631 (Windows 11 x86_64)
|
||||
> HOST: QEMU Standard PC (Q35 + ICH9, 2009)
|
||||
|
||||
</details>
|
24
tests/ffi/benchmark/external_call/deno.ts
vendored
Normal file
24
tests/ffi/benchmark/external_call/deno.ts
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { libSuffix } from "../../utils/libSuffix.ts";
|
||||
import { get_clock, get_offset } from "../../utils/proc_clock/deno.ts";
|
||||
|
||||
const library_file = "./tests/ffi/benchmark/external_call/lib."+libSuffix;
|
||||
// @ts-ignore
|
||||
let library = Deno.dlopen(library_file, {
|
||||
add: {
|
||||
parameters: ["i32", "i32"],
|
||||
result: "i32",
|
||||
},
|
||||
});
|
||||
|
||||
function bench_add(bench_size: number) {
|
||||
let add = library.symbols.add;
|
||||
let value = 0;
|
||||
const before = get_clock();
|
||||
for (let i=0; i<bench_size; i++) {
|
||||
value = add(value,1);
|
||||
}
|
||||
const after = get_clock();
|
||||
console.log(get_offset(before,after))
|
||||
}
|
||||
|
||||
bench_add(1000000);
|
31
tests/ffi/benchmark/external_call/init.luau
Normal file
31
tests/ffi/benchmark/external_call/init.luau
Normal file
|
@ -0,0 +1,31 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../../utils/compile")("./tests/ffi/benchmark/external_call/lib.c")
|
||||
local process = require("@lune/process")
|
||||
local c = ffi.c
|
||||
local BENCH_SCALE: number = tonumber(process.env.BENCH_SCALE) or 1000000
|
||||
|
||||
-- Get clock provider
|
||||
local procClock = require("../../utils/proc_clock")
|
||||
local before, after = procClock.newBox()
|
||||
local getClock = procClock.getClock
|
||||
|
||||
local add = c.fn({ c.int, c.int }, c.int):callable(lib:find("add"))
|
||||
|
||||
local a = c.int:box(0)
|
||||
local delta = c.int:box(1)
|
||||
local a_ref = a:ref()
|
||||
local delta_ref = delta:ref()
|
||||
|
||||
getClock(before)
|
||||
for i = 1, BENCH_SCALE do
|
||||
add(a, a_ref, delta_ref)
|
||||
end
|
||||
getClock(after)
|
||||
|
||||
print("lune-std-ffi: " .. procClock.getOffset(before, after))
|
||||
local result = c.int:readData(a)
|
||||
assert(result == BENCH_SCALE, `bench_add failed. result expected {BENCH_SCALE}, got {result}`)
|
||||
|
||||
local cSideTime = ffi.box(ffi.f64.size)
|
||||
c.fn({}, ffi.f64):callable(lib:find("c_test"))(cSideTime)
|
||||
print("C level: " .. ffi.f64:readData(cSideTime))
|
18
tests/ffi/benchmark/external_call/lib.c
vendored
Normal file
18
tests/ffi/benchmark/external_call/lib.c
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <time.h>
|
||||
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
double c_test() {
|
||||
clock_t before = clock();
|
||||
|
||||
int a = 0;
|
||||
for (int i=0; i<1000000; i++) {
|
||||
a = add(a, 1);
|
||||
}
|
||||
|
||||
clock_t after = clock();
|
||||
|
||||
return (double)(after - before) / CLOCKS_PER_SEC;
|
||||
}
|
24
tests/ffi/benchmark/external_call/luajit.lua
Normal file
24
tests/ffi/benchmark/external_call/luajit.lua
Normal file
|
@ -0,0 +1,24 @@
|
|||
--!nolint
|
||||
--!nocheck
|
||||
|
||||
local ffi = require("ffi")
|
||||
local BENCH_SCALE = 1000000
|
||||
|
||||
ffi.cdef([[
|
||||
int add(int a, int b);
|
||||
]])
|
||||
local lib = ffi.load("./tests/ffi/benchmark/external_call/lib.so")
|
||||
local add = lib.add
|
||||
local a = 0
|
||||
|
||||
local before = os.clock()
|
||||
for i = 1, BENCH_SCALE do
|
||||
a = add(a, 1)
|
||||
end
|
||||
local after = os.clock()
|
||||
|
||||
print(after - before)
|
||||
assert(
|
||||
a == BENCH_SCALE,
|
||||
string.format("bench_add failed. result expected %d, got %d", BENCH_SCALE, a)
|
||||
)
|
13
tests/ffi/cast.luau
Normal file
13
tests/ffi/cast.luau
Normal file
|
@ -0,0 +1,13 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
|
||||
local floatBox = ffi.f32:box(1.2)
|
||||
local intBox = ffi.box(ffi.i32.size)
|
||||
|
||||
ffi.f32:cast(ffi.i32, floatBox, intBox)
|
||||
|
||||
local castedInt = ffi.i32:readData(intBox)
|
||||
|
||||
assert(
|
||||
castedInt == 1 and ffi.isInteger(castedInt),
|
||||
"castedInt == 1 and ffi.isInteger(castedInt) assersion failed"
|
||||
)
|
13
tests/ffi/external_closure/callClosure.luau
Normal file
13
tests/ffi/external_closure/callClosure.luau
Normal file
|
@ -0,0 +1,13 @@
|
|||
local callableWrapper = require("../utils/callableWrapper")
|
||||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_closure/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
-- Create closure
|
||||
local closure = c.fn({ c.int, c.int }, c.int):closure(function(ret, a, b)
|
||||
c.int:writeData(ret, c.int:readData(a) + c.int:readData(b))
|
||||
end)
|
||||
|
||||
local callClosure = callableWrapper(lib:find("call_closure"), { c.void:ptr() }, c.int)
|
||||
local result = callClosure(closure:ref())
|
||||
assert(result == 72, `callClosure failed. result expected 20000, got {result}`)
|
15
tests/ffi/external_closure/callClosureWithPointer.luau
Normal file
15
tests/ffi/external_closure/callClosureWithPointer.luau
Normal file
|
@ -0,0 +1,15 @@
|
|||
local callableWrapper = require("../utils/callableWrapper")
|
||||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_closure/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
-- Create closure
|
||||
local closureWithPointer = c.fn({ c.int, c.int:ptr() }, c.int)
|
||||
:closure(function(returnRef, aRef, bRef)
|
||||
c.int:writeData(returnRef, c.int:readData(aRef) + c.int:readData(bRef:deref()))
|
||||
end)
|
||||
|
||||
local callClosureWithPointer =
|
||||
callableWrapper(lib:find("call_closure_with_pointer"), { c.void:ptr() }, c.int)
|
||||
local result = callClosureWithPointer(closureWithPointer:ref())
|
||||
assert(result == 72, `closureWithPointer failed. result expected 20000, got {result}`)
|
11
tests/ffi/external_closure/callHelloWorld.luau
Normal file
11
tests/ffi/external_closure/callHelloWorld.luau
Normal file
|
@ -0,0 +1,11 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_closure/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
-- Create closure
|
||||
local helloWorld = c.fn({}, c.void):closure(function()
|
||||
print("Hello world in lua closure!")
|
||||
end)
|
||||
|
||||
local callHelloWorld = c.fn({ c.void:ptr() }, c.void):callable(lib:find("call_hello_world"))
|
||||
callHelloWorld(nil, helloWorld:ref())
|
17
tests/ffi/external_closure/lib.c
vendored
Normal file
17
tests/ffi/external_closure/lib.c
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include<stdio.h>
|
||||
|
||||
typedef int (*lua_closure_t)(int, int);
|
||||
int call_closure(lua_closure_t lua_closure) {
|
||||
return lua_closure(12, 24) * 2;
|
||||
}
|
||||
|
||||
typedef void (*lua_hello_world_t)();
|
||||
void call_hello_world(lua_hello_world_t lua_closure) {
|
||||
lua_closure();
|
||||
}
|
||||
|
||||
typedef int (*lua_closure_with_pointer_t)(int, int*);
|
||||
int call_closure_with_pointer(lua_closure_with_pointer_t lua_closure) {
|
||||
int b = 24;
|
||||
return lua_closure(12, &b) * 2;
|
||||
}
|
9
tests/ffi/external_math/addInt.luau
Normal file
9
tests/ffi/external_math/addInt.luau
Normal file
|
@ -0,0 +1,9 @@
|
|||
local callableWrapper = require("../utils/callableWrapper")
|
||||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_math/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
local addInt = callableWrapper(lib:find("add_int"), { c.int, c.int }, c.int)
|
||||
local result = addInt(100, 200)
|
||||
|
||||
assert(result == 300, `test_addInt failed. result expected 300, got {result}`)
|
7
tests/ffi/external_math/lib.c
vendored
Normal file
7
tests/ffi/external_math/lib.c
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
int add_int(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int mul_int(int a, int b) {
|
||||
return a * b;
|
||||
}
|
8
tests/ffi/external_math/mulInt.luau
Normal file
8
tests/ffi/external_math/mulInt.luau
Normal file
|
@ -0,0 +1,8 @@
|
|||
local callableWrapper = require("../utils/callableWrapper")
|
||||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_math/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
local mulInt = callableWrapper(lib:find("mul_int"), { c.int, c.int }, c.int)
|
||||
local result = mulInt(100, 200)
|
||||
assert(result == 20000, `test_mulInt failed. result expected 20000, got {result}`)
|
7
tests/ffi/external_pointer/lib.c
vendored
Normal file
7
tests/ffi/external_pointer/lib.c
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
void pointer_write(int *a) {
|
||||
*a = 123;
|
||||
}
|
||||
|
||||
int pointer_read(int *a) {
|
||||
return *a;
|
||||
}
|
8
tests/ffi/external_pointer/pointerRead.luau
Normal file
8
tests/ffi/external_pointer/pointerRead.luau
Normal file
|
@ -0,0 +1,8 @@
|
|||
local callableWrapper = require("../utils/callableWrapper")
|
||||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_pointer/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
local pointerRead = callableWrapper(lib:find("pointer_read"), { c.int:ptr() }, c.int)
|
||||
local result = pointerRead(c.int:box(123):ref():ref())
|
||||
assert(result == 123, `pointerRead failed. result expected 123, got {result}`)
|
9
tests/ffi/external_pointer/pointerWrite.luau
Normal file
9
tests/ffi/external_pointer/pointerWrite.luau
Normal file
|
@ -0,0 +1,9 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_pointer/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
local pointerWrite = c.fn({ c.int:ptr() }, c.void):callable(lib:find("pointer_write"))
|
||||
local aBox = ffi.box(c.int.size)
|
||||
pointerWrite(nil, aBox:ref():ref())
|
||||
local result = c.int:readData(aBox)
|
||||
assert(result == 123, `pointerWrite failed. result expected 123, got {result}`)
|
5
tests/ffi/external_print/helloWorld.luau
Normal file
5
tests/ffi/external_print/helloWorld.luau
Normal file
|
@ -0,0 +1,5 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_print/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
c.fn({}, c.void):callable(lib:find("hello_world"))(nil)
|
5
tests/ffi/external_print/lib.c
vendored
Normal file
5
tests/ffi/external_print/lib.c
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include<stdio.h>
|
||||
|
||||
void hello_world() {
|
||||
printf("Hello world from external function!");
|
||||
}
|
12
tests/ffi/external_struct/ab.luau
Normal file
12
tests/ffi/external_struct/ab.luau
Normal file
|
@ -0,0 +1,12 @@
|
|||
local callableWrapper = require("../utils/callableWrapper")
|
||||
local ffi = require("@lune/ffi")
|
||||
local lib = require("../utils/compile")("./tests/ffi/external_struct/lib.c")
|
||||
local c = ffi.c
|
||||
|
||||
local argStructInfo = c.struct({ c.int, c.int:ptr() })
|
||||
local resultStructInfo = c.struct({ c.int, c.int })
|
||||
|
||||
local ab = callableWrapper(lib:find("ab"), { argStructInfo }, resultStructInfo)
|
||||
local result = ab({ 100, c.int:box(200):ref() } :: { any })
|
||||
assert(result[1] == 300, `ab failed. result expected 300, got {result[1]}`)
|
||||
assert(result[2] == 20000, `ab failed. result expected 300, got {result[2]}`)
|
14
tests/ffi/external_struct/lib.c
vendored
Normal file
14
tests/ffi/external_struct/lib.c
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
typedef struct {
|
||||
int a;
|
||||
int* b;
|
||||
} ArgStruct;
|
||||
|
||||
typedef struct {
|
||||
int sum;
|
||||
int mul;
|
||||
} ResultStruct;
|
||||
|
||||
ResultStruct ab(ArgStruct t) {
|
||||
ResultStruct result = { t.a+ * t.b, t.a * (*t.b) };
|
||||
return result;
|
||||
}
|
18
tests/ffi/free.luau
Normal file
18
tests/ffi/free.luau
Normal file
|
@ -0,0 +1,18 @@
|
|||
--!nocheck
|
||||
--!nolint
|
||||
local ffi = require("@lune/ffi")
|
||||
|
||||
local box = ffi.box(ffi.i32.size)
|
||||
local ref = box:leak()
|
||||
|
||||
box = nil
|
||||
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
|
||||
ffi.free(ref)
|
||||
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
||||
collectgarbage("collect")
|
7
tests/ffi/isInteger.luau
Normal file
7
tests/ffi/isInteger.luau
Normal file
|
@ -0,0 +1,7 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
|
||||
local int = 0b1
|
||||
local float = 0.5
|
||||
|
||||
assert(ffi.isInteger(int) == true, "ffi.isInteger(int) == true assersion failed")
|
||||
assert(ffi.isInteger(float) == false, "ffi.isInteger(float) == false assersion failed")
|
6
tests/ffi/pretty_print/arr.luau
Normal file
6
tests/ffi/pretty_print/arr.luau
Normal file
|
@ -0,0 +1,6 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local c = ffi.c
|
||||
|
||||
assert(typeof(c.int:arr(5)) :: string == "CArrInfo")
|
||||
assert(tostring(c.int:arr(5)) == " int, length = 5 ")
|
||||
assert(tostring(c.int:ptr():arr(5)) == " <CPtrInfo(int)>, length = 5 ")
|
4
tests/ffi/pretty_print/box.luau
Normal file
4
tests/ffi/pretty_print/box.luau
Normal file
|
@ -0,0 +1,4 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local half_back = 0b_0000_0000_0000_0000_0000_0000_1111_1111
|
||||
|
||||
print(ffi.i32:box(half_back))
|
13
tests/ffi/pretty_print/fn.luau
Normal file
13
tests/ffi/pretty_print/fn.luau
Normal file
|
@ -0,0 +1,13 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local c = ffi.c
|
||||
|
||||
assert(typeof(c.fn({ c.int }, c.int)) :: string == "CFnInfo")
|
||||
assert(tostring(c.fn({ c.int }, c.int)) == " (int) -> int ")
|
||||
assert(tostring(c.fn({ c.int, ffi.f32 }, c.int)) == " (int, f32) -> int ")
|
||||
assert(tostring(c.fn({ c.int:ptr() }, c.int)) == " (<CPtrInfo(int)>) -> int ")
|
||||
assert(tostring(c.fn({ c.int }, c.int:ptr())) == " (int) -> <CPtrInfo(int)> ")
|
||||
assert(tostring(c.fn({ c.int:ptr() }, c.int:ptr())) == " (<CPtrInfo(int)>) -> <CPtrInfo(int)> ")
|
||||
assert(
|
||||
tostring(c.fn({ c.int:ptr(), c.int:ptr() }, c.int:ptr()))
|
||||
== " (<CPtrInfo(int)>, <CPtrInfo(int)>) -> <CPtrInfo(int)> "
|
||||
)
|
6
tests/ffi/pretty_print/ptr.luau
Normal file
6
tests/ffi/pretty_print/ptr.luau
Normal file
|
@ -0,0 +1,6 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local c = ffi.c
|
||||
|
||||
assert(typeof(c.int:ptr()) :: string == "CPtrInfo")
|
||||
assert(tostring(c.int:ptr()) == "int")
|
||||
assert(tostring(c.int:arr(5):ptr()) == " <CArrInfo( int, length = 5 )> ")
|
8
tests/ffi/pretty_print/struct.luau
Normal file
8
tests/ffi/pretty_print/struct.luau
Normal file
|
@ -0,0 +1,8 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local c = ffi.c
|
||||
|
||||
assert(typeof(c.struct({ c.int, c.char })) :: string == "CStructInfo")
|
||||
assert(
|
||||
tostring(c.struct({ c.int, c.char:ptr() }))
|
||||
== ` int, <CPtrInfo(char)>, size = {c.struct({ c.int, c.char:ptr() }).size} `
|
||||
)
|
6
tests/ffi/pretty_print/type.luau
Normal file
6
tests/ffi/pretty_print/type.luau
Normal file
|
@ -0,0 +1,6 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local c = ffi.c
|
||||
|
||||
assert(typeof(c.int) :: string == "CTypeInfo")
|
||||
assert(tostring(c.int) == "int")
|
||||
assert(tostring(ffi.i32) == "i32")
|
51
tests/ffi/readBoundary.luau
Normal file
51
tests/ffi/readBoundary.luau
Normal file
|
@ -0,0 +1,51 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local ok
|
||||
|
||||
-- Case1: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.u8:box(1)
|
||||
ffi.u8:readData(box)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case1 should success")
|
||||
|
||||
-- Case2: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.u8:box(1)
|
||||
ffi.u16:readData(box)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case2 should fail")
|
||||
|
||||
-- Case3: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(ffi.u8.size * 2)
|
||||
ffi.u16:readData(box)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case3 should success")
|
||||
|
||||
-- Case4: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(ffi.u8.size * 2)
|
||||
ffi.u8:readData(box, ffi.u8.size)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case4 should success")
|
||||
|
||||
-- Case5: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.u8:box(1):ref()
|
||||
ffi.u16:readData(box)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case5 should fail")
|
||||
|
||||
-- Case6: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(ffi.u8.size * 2):ref()
|
||||
ffi.u16:readData(box)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case6 should success")
|
||||
|
||||
-- Case7: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(ffi.u8.size * 2):ref(ffi.u16.size)
|
||||
ffi.u16:readData(box)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case7 should fail")
|
34
tests/ffi/stringReadWrite.luau
Normal file
34
tests/ffi/stringReadWrite.luau
Normal file
|
@ -0,0 +1,34 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local ok
|
||||
|
||||
local str = "hello world"
|
||||
local strbox = ffi.box(#str):writeString(str)
|
||||
assert(strbox:readString(#str) == str, "String read write assersion failed")
|
||||
|
||||
-- Case1: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(2)
|
||||
box:readString(10)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case1 should fail")
|
||||
|
||||
-- Case2: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(2)
|
||||
box:writeString("hello world")
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case2 should fail")
|
||||
|
||||
-- Case3: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(2):ref()
|
||||
box:readString(10)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case3 should fail")
|
||||
|
||||
-- Case4: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(2):ref()
|
||||
box:writeString("hello world")
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case4 should fail")
|
19
tests/ffi/types/arr.luau
Normal file
19
tests/ffi/types/arr.luau
Normal file
|
@ -0,0 +1,19 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local ok
|
||||
|
||||
local arr = ffi.i32:arr(4)
|
||||
|
||||
assert(rawequal(arr.length, 4), "length assersion failed, arr.length should be 4")
|
||||
assert(rawequal(arr.inner, ffi.i32), "inner assersion failed, arr.inner should be ffi.i32")
|
||||
|
||||
-- offset(2) success
|
||||
ok = pcall(function()
|
||||
arr:offset(2)
|
||||
end)
|
||||
assert(ok, "assersion failed, arr:offset(2) should success")
|
||||
|
||||
-- offset(4) success
|
||||
ok = pcall(function()
|
||||
arr:offset(4)
|
||||
end)
|
||||
assert(not ok, "assersion failed, arr:offset(4) should fail")
|
31
tests/ffi/types/ptr.luau
Normal file
31
tests/ffi/types/ptr.luau
Normal file
|
@ -0,0 +1,31 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
|
||||
-- ptr size test
|
||||
assert(
|
||||
ffi.i32:ptr().size == ffi.i64:ptr().size,
|
||||
"size assersion failed, size of pointer should be same with each other (ffi.i32:ptr().size == ffi.i64:ptr().size)"
|
||||
)
|
||||
|
||||
-- inner test
|
||||
local i32ptr = ffi.i32:ptr()
|
||||
assert(
|
||||
rawequal(ffi.i32, i32ptr.inner),
|
||||
"inner assersion failed, inner field must be same with their parent"
|
||||
.. " (ffi.i32 == ffi.i32:ptr().inner)"
|
||||
)
|
||||
assert(
|
||||
rawequal(i32ptr, i32ptr:ptr().inner),
|
||||
"inner assersion failed, inner field must be same with their parent"
|
||||
.. " (i32ptr == i32ptr:ptr().inner)"
|
||||
)
|
||||
assert(
|
||||
rawequal(i32ptr, i32ptr:ptr().inner:ptr().inner:ptr().inner),
|
||||
"inner assersion failed, inner field must be same with their parent"
|
||||
.. " (i32ptr == i32ptr:ptr().inner:ptr().inner:ptr().inner)"
|
||||
)
|
||||
|
||||
-- deep ptr test
|
||||
local ok, err = pcall(function()
|
||||
i32ptr:ptr():ptr():ptr():ptr():ptr():ptr():ptr()
|
||||
end)
|
||||
assert(ok, "deep ptr assersion failed\n" .. (err or ""))
|
20
tests/ffi/types/struct.luau
Normal file
20
tests/ffi/types/struct.luau
Normal file
|
@ -0,0 +1,20 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local ok
|
||||
|
||||
local i32ptr = ffi.i32:ptr()
|
||||
local struct = ffi.c.struct({ i32ptr, ffi.i32 })
|
||||
|
||||
assert(rawequal(struct:field(0), i32ptr), "Struct get field failed")
|
||||
assert(rawequal(struct:field(1), ffi.i32), "Struct get field failed")
|
||||
|
||||
-- offset(2) should fail
|
||||
ok = pcall(function()
|
||||
struct:offset(2)
|
||||
end)
|
||||
assert(not ok, "assersion failed, struct:offset(2) should fail")
|
||||
|
||||
-- field(2) should fail
|
||||
ok = pcall(function()
|
||||
struct:field(2)
|
||||
end)
|
||||
assert(not ok, "assersion failed, struct:field(2) should fail")
|
38
tests/ffi/utils/callableWrapper.luau
Normal file
38
tests/ffi/utils/callableWrapper.luau
Normal file
|
@ -0,0 +1,38 @@
|
|||
--!nocheck
|
||||
local ffi = require("@lune/ffi")
|
||||
|
||||
local function callableWrapper(
|
||||
functionRef: ffi.RefData,
|
||||
argTypeList: { ffi.CTypes },
|
||||
retType: ffi.CTypes
|
||||
): (...any) -> any
|
||||
local callable = ffi.c.fn(argTypeList, retType):callable(functionRef)
|
||||
|
||||
return function(...)
|
||||
local argValues = table.create(#argTypeList + 1)
|
||||
|
||||
local resultBox
|
||||
if retType ~= ffi.c.void then
|
||||
resultBox = ffi.box(retType.size)
|
||||
end
|
||||
argValues[1] = resultBox
|
||||
|
||||
for index, argType in argTypeList do
|
||||
local arg = select(index, ...)
|
||||
if type(arg) == "userdata" then
|
||||
argValues[index + 1] = arg
|
||||
else
|
||||
argValues[index + 1] = argType:box(arg):ref()
|
||||
end
|
||||
end
|
||||
|
||||
callable(table.unpack(argValues, 1, #argTypeList + 1))
|
||||
|
||||
if retType == ffi.c.void then
|
||||
return nil
|
||||
end
|
||||
return retType:readData(resultBox)
|
||||
end
|
||||
end
|
||||
|
||||
return callableWrapper
|
25
tests/ffi/utils/compile.luau
Normal file
25
tests/ffi/utils/compile.luau
Normal file
|
@ -0,0 +1,25 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local process = require("@lune/process")
|
||||
|
||||
local function getLibSuffix(): string
|
||||
if process.os == "linux" then
|
||||
return "so"
|
||||
elseif process.os == "windows" then
|
||||
return "dll"
|
||||
elseif process.os == "macos" then
|
||||
return "dylib"
|
||||
end
|
||||
error("Unknown OS")
|
||||
end
|
||||
|
||||
local function compile(file: string): ffi.LibData
|
||||
local out = file:gsub("%.c$", "." .. getLibSuffix())
|
||||
local gcc = process.exec("gcc", { "-shared", "-o", out, "-fPIC", file })
|
||||
if not gcc.ok then
|
||||
error("Failed to execute gcc command\n" .. gcc.stdout .. gcc.stderr)
|
||||
end
|
||||
|
||||
return ffi.open(out)
|
||||
end
|
||||
|
||||
return compile
|
13
tests/ffi/utils/libSuffix.ts
vendored
Normal file
13
tests/ffi/utils/libSuffix.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
export let libSuffix = "";
|
||||
// @ts-ignore
|
||||
switch (Deno.build.os) {
|
||||
case "windows":
|
||||
libSuffix = "dll";
|
||||
break;
|
||||
case "darwin":
|
||||
libSuffix = "dylib";
|
||||
break;
|
||||
case "linux":
|
||||
libSuffix = "so";
|
||||
break;
|
||||
}
|
27
tests/ffi/utils/proc_clock/deno.ts
vendored
Normal file
27
tests/ffi/utils/proc_clock/deno.ts
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { libSuffix } from "../libSuffix.ts";
|
||||
|
||||
const library_file = "./tests/ffi/utils/proc_clock/lib."+libSuffix;
|
||||
// @ts-ignore
|
||||
let library = Deno.dlopen(library_file, {
|
||||
sizeof_clock: {
|
||||
parameters: [],
|
||||
result: "i32",
|
||||
},
|
||||
});
|
||||
const sizeof_clock = library.symbols.sizeof_clock();
|
||||
const type_clock_t = "u" + (sizeof_clock * 8);
|
||||
library.close();
|
||||
// @ts-ignore
|
||||
library = Deno.dlopen(library_file, {
|
||||
get_clock: {
|
||||
parameters: [],
|
||||
result: type_clock_t,
|
||||
},
|
||||
get_offset: {
|
||||
parameters: [type_clock_t, type_clock_t],
|
||||
result: "f64",
|
||||
},
|
||||
});
|
||||
|
||||
export const get_clock = library.symbols.get_clock;
|
||||
export const get_offset = library.symbols.get_offset;
|
46
tests/ffi/utils/proc_clock/init.luau
Normal file
46
tests/ffi/utils/proc_clock/init.luau
Normal file
|
@ -0,0 +1,46 @@
|
|||
-- FIXME: in windows, we need another library to get process cpu time
|
||||
|
||||
local ffi = require("@lune/ffi")
|
||||
local process = require("@lune/process")
|
||||
local isWindows = process.os == "windows"
|
||||
local c = ffi.c
|
||||
|
||||
local procClock = {}
|
||||
|
||||
local lib = require("../compile")("./tests/ffi/utils/proc_clock/lib.c")
|
||||
|
||||
-- sizeof_clock
|
||||
local sizeofClock = c.fn({}, c.int):callable(lib:find("sizeof_clock"))
|
||||
function procClock.sizeofClock(): number
|
||||
local result = ffi.box(c.int.size)
|
||||
sizeofClock(result)
|
||||
return c.int:readData(result)
|
||||
end
|
||||
-- get_clock
|
||||
local clock_t = if isWindows then ffi.f32 else ffi["u" .. (procClock.sizeofClock() * 8)]
|
||||
assert(clock_t, "clock_t is unknown type")
|
||||
procClock.getClock = (
|
||||
if isWindows
|
||||
then function(clock: ffi.BoxData | ffi.RefData)
|
||||
ffi.f32:writeData(clock, os.clock())
|
||||
end
|
||||
else c.fn({}, clock_t):callable(lib:find("get_clock"))
|
||||
) :: (ffi.BoxData | ffi.RefData) -> ()
|
||||
|
||||
-- get_offset
|
||||
local getOffset: (ffi.BoxData, ffi.RefData, ffi.RefData) -> () = if isWindows
|
||||
then function(result: ffi.BoxData, before: ffi.RefData, after: ffi.RefData)
|
||||
ffi.f64:writeData(result, (ffi.f32:readData(after) - ffi.f32:readData(before)))
|
||||
end
|
||||
else c.fn({ clock_t, clock_t }, ffi.f64):callable(lib:find("get_offset"))
|
||||
function procClock.getOffset(before: ffi.BoxData, after: ffi.BoxData): number
|
||||
local result = ffi.box(ffi.f64.size)
|
||||
getOffset(result, before:ref(), after:ref())
|
||||
return ffi.f64:readData(result)
|
||||
end
|
||||
|
||||
function procClock.newBox(): (ffi.BoxData, ffi.BoxData)
|
||||
return ffi.box(clock_t.size), ffi.box(clock_t.size)
|
||||
end
|
||||
|
||||
return procClock
|
12
tests/ffi/utils/proc_clock/lib.c
vendored
Normal file
12
tests/ffi/utils/proc_clock/lib.c
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <time.h>
|
||||
clock_t get_clock() {
|
||||
return clock();
|
||||
}
|
||||
|
||||
int sizeof_clock() {
|
||||
return sizeof(clock_t);
|
||||
}
|
||||
|
||||
double get_offset(clock_t before, clock_t after) {
|
||||
return (double)(after - before) / CLOCKS_PER_SEC;
|
||||
}
|
45
tests/ffi/writeBoundary.luau
Normal file
45
tests/ffi/writeBoundary.luau
Normal file
|
@ -0,0 +1,45 @@
|
|||
local ffi = require("@lune/ffi")
|
||||
local c = ffi.c
|
||||
local ok
|
||||
|
||||
-- Case1: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(c.int.size - 1)
|
||||
c.int:writeData(box, 10)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case1 should fail")
|
||||
|
||||
-- Case2: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(c.int.size)
|
||||
c.int:writeData(box, 10)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case2 should success")
|
||||
|
||||
-- Case3: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(c.int.size * 2)
|
||||
c.int:writeData(box, 10, c.int.size)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case3 should success")
|
||||
|
||||
-- Case4: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(c.int.size * 2)
|
||||
c.int:writeData(box, 10, c.int.size * 2)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case4 should fail")
|
||||
|
||||
-- Case5: Success
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(c.int.size * 2):ref()
|
||||
c.int:writeData(box, 10, c.int.size)
|
||||
end)
|
||||
assert(ok, "assersion failed, Case5 should success")
|
||||
|
||||
-- Case6: Fail
|
||||
ok = pcall(function()
|
||||
local box = ffi.box(c.int.size * 2):ref()
|
||||
c.int:writeData(box, 10, c.int.size * 2)
|
||||
end)
|
||||
assert(not ok, "assersion failed, Case6 should fail")
|
1224
types/ffi.luau
Normal file
1224
types/ffi.luau
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue