Skip to content

Commit d6a4251

Browse files
Marcos-catOmnikar
andauthored
Rehaul FFI (#783)
* redo pointer reading pointers dont deref themselves memcpy accepts all ffi types, even other pointers pointers know their type * setting mostly works, minor bugs * get arrays to work * repr structs * get return types working properly * get length indices working * check for duplicate indices * get out parameters working * format array structs better * add more type aliases * optimize pointer reading * chnage type display * update ffi documentation * make ffi tests pass * add to changelog * Fix woring in the changlog Co-authored-by: Omnikar <[email protected]> --------- Co-authored-by: Omnikar <[email protected]>
1 parent 9139ae8 commit d6a4251

File tree

10 files changed

+891
-1392
lines changed

10 files changed

+891
-1392
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This version is not yet released. If you are reading this on the website, then t
1414
- **Breaking Change** - [`under ⍜`](https://uiua.org/docs/under)[`shape △`](https://uiua.org/docs/shape) now tiles the original array to match the new shape instead of [`reshape ↯`](https://uiua.org/docs/reshape)ing
1515
- **Breaking Change** - [`dip ⊙`](https://uiua.org/docs/dip) and [`on ⟜`](https://uiua.org/docs/on) function packs no longer apply their modifier to the leftmost function
1616
- This change makes most usecases of these function packs cleaner, as `A⊙(B|C)` changes to `⊙(A|B|C)` and likewise for [`on ⟜`](https://uiua.org/docs/on)
17+
- **Breaking Change** - The FFI function API has been changed considerably, read more in the documentation for [`&ffi`](https://uiua.org/docs/&ffi), [`&memcpy`](https://uiua.org/docs/&memcpy), and [`&memset`](https://uiua.org/docs/&memset)
1718
- Allow for private [modules](https://uiua.org/tutorial/modules#private-scoped-modules), [imports](https://uiua.org/tutorial/modules#private-imports), and [data definitions](https://uiua.org/tutorial/datadefs#visibility)
1819
- Add array pack syntactic sugar. This lets you write code like `[⊃(+|×)]` as `⊃[+|×]`.
1920
- Subscripts can now be typed with `,` instead of `__`s

parser/src/defs.rs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3998,8 +3998,10 @@ sys_op! {
39983998
/// - `unsigned int`
39993999
/// - `unsigned long`
40004000
/// - `unsigned long long`
4001+
/// Any unsigned type can be written with just a `u` such as `uint` instead of `unsigned int`.
40014002
/// Suffixing any of these with `*` makes them a pointer type.
40024003
/// Struct types are defined as a list of types between `{}`s separated by `;`s, i.e. `{int; float}`. A trailing `;` is optional.
4004+
/// A struct with all fields of the same type can be written like `type[number]`. For example a pair of `int`s would be written `int[2]`.
40034005
///
40044006
/// Arguments are passed as a list of boxed values.
40054007
/// If we have a C function `int add(int a, int b)` in a shared library `example.dll`, we can call it like this:
@@ -4011,32 +4013,25 @@ sys_op! {
40114013
/// Uiua arrays can be passed to foreign functions as pointer-length pairs.
40124014
/// To do this, specify the type of the list items followed by `:n`, where `n` is the index of the parameter that corresponds to the length.
40134015
/// The interpreter will automatically pass the number of elements in the array to this parameter.
4014-
/// Arrays passed in this way will be implicitely [deshape]ed, unless the item type is a struct.
40154016
/// If we wave a C function `int sum(const int* arr, int len)` in a shared library `example.dll`, we can call it like this:
40164017
/// ex! # Experimental!
40174018
/// : Lib ← &ffi ⊂□"example.dll"
4018-
/// : Sum ← Lib {"int" "sum" "const int:1" "int"}
4019+
/// : Sum ← Lib {"int" "sum" "int:1" "int"}
40194020
/// : Sum {[1 2 3 4 5]} # 15
40204021
///
40214022
/// [&ffi] calls can return multiple values.
4022-
/// In addition to the return value, any non-`const` pointer parameters will be interpreted as out-parameters.
4023+
/// In addition to the return value, any parameters prefixed with `out` will be sent and returned as out parameters.
4024+
/// If the out parameter is not marked as a pointer, it will be interpreted as a single value, and will be read back into an array when returned.
4025+
/// If the out parameter is marked as a pointer, it will be interpreted as an array and will be returned as a pointer value. This is allows you to keep memory that is allocated by passing an array valid.
40234026
/// If there is more than one output value (including the return value), [&ffi] will return a list of the boxed output values.
4024-
/// If we have a C function `int split_head(int* list, int* len)` in a shared library `example.dll`, we can call it like this:
4025-
/// ex! # Experimental!
4026-
/// : Lib ← &ffi ⊂□"example.dll"
4027-
/// : SplitHead ← Lib {"int" "split_head" "int:1" "int*"}
4028-
/// : SplitHead {[1 2 3 4 5]} # {1 [2 3 4 5]}
4029-
/// Note that the length parameter is a non-`const` pointer. This is because the function will modify it.
4030-
///
4031-
/// `const char*` parameters and return types are interpreted as null-terminated strings, without an associated length parameter.
40324027
///
40334028
/// Structs can be passed either as lists of boxed values or, if all fields are of the same type, as a normal array.
40344029
/// If all fields of a struct returned by a foreign function are of the same type, the interpreter will automatically interpret it as an array rather than a list of boxed values.
40354030
/// If we have a C struct `struct Vec2 { float x; float y; }` and a function `Vec2 vec2_add(Vec2 a, Vec2 b)` in a shared library `example.dll`, we can call it like this:
40364031
/// ex! # Experimental!
40374032
/// : Lib ← &ffi ⊂□"example.dll"
4038-
/// : VecII ← "{float; float}"
4039-
/// : Add ← Lib {VecII "vec2_add" VecII VecII}
4033+
/// : Vec₂ ← "float[2]"
4034+
/// : Add ← Lib {Vec₂ "vec2_add" Vec₂ Vec₂}
40404035
/// : Add {[1 2] [3 4]} # [4 6]
40414036
///
40424037
/// If a foreign function returns or has an out-parameter that is a pointer type, a special array is returned representing the pointer. This array is not useful as a normal array, but it can be passed back as an [&ffi] argument, read from with [&memcpy], or freed with [&memfree].
@@ -4052,31 +4047,33 @@ sys_op! {
40524047
/// Expects a string indicating the type, a pointer, and a length.
40534048
///
40544049
/// The returned array will always be rank-`1`.
4055-
/// The type of the array depends on the given type.
4056-
/// Types are specified in the same way as in [&ffi].
4057-
/// `"char"` will create a character array.
4058-
/// `"unsigned char"` will create a number array with efficient byte storage.
4059-
/// All other number types will create a normal number array.
4050+
/// The type of the array depends on the pointer's type.
40604051
///
40614052
/// For example, if we have a C function `int* get_ints(int len)` in a shared library `example.dll`, we can call it and copy the result like this:
40624053
/// ex! # Experimental!
40634054
/// : Lib ← &ffi ⊂□"example.dll"
40644055
/// : GetInts ← Lib {"int*" "get_ints" "int"}
4065-
/// : &memcpy "int":3 GetInts 3
4056+
/// : &memcpy GetInts 3
40664057
///
40674058
/// Importantly, [&memcpy] does *not* free the memory allocated by the foreign function.
40684059
/// Use [&memfree] to free the memory.
40694060
/// ex! # Experimental!
40704061
/// : Lib ← &ffi ⊂□"example.dll"
40714062
/// : GetInts ← Lib {"int*" "get_ints" "int"}
4072-
/// : ⊃&memfree(&memcpy "int":3) GetInts 3
4073-
(3, MemCopy, Ffi, "&memcpy", "foreign function interface - copy", Mutating, { experimental: true }),
4063+
/// : ⊃&memfree&memcpy ⊸GetInts 3
4064+
(2, MemCopy, Ffi, "&memcpy", "foreign function interface - copy", Mutating, { experimental: true }),
4065+
/// Write data from an array into a pointer
4066+
///
4067+
/// Expects a pointer, an index, and a value.
4068+
///
4069+
/// This function is equivalent to the C syntax `pointer[index] = value`
4070+
(3(0), MemSet, Ffi, "&memset", "write to memory", Mutating, { experimental: true }),
40744071
/// Free a pointer
40754072
///
40764073
/// *Warning ⚠️: [&memfree] can lead to undefined behavior if used incorrectly.*
40774074
///
4078-
/// This is useful for freeing memory allocated by a foreign function.
4079-
/// Expects a pointer.
4075+
/// This is useful for freeing memory allocated by a foreign function, or by an out-pointer.
4076+
/// Expects a pointer. If the pointer is `NULL` [&memfree] will do nothing.
40804077
/// See [&memcpy] for an example.
40814078
(1(0), MemFree, Ffi, "&memfree", "free memory", Mutating, { experimental: true }),
40824079
}

src/array.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use crate::{
2020
cowslice::{cowslice, CowSlice},
2121
fill::{Fill, FillValue},
2222
grid_fmt::GridFmt,
23-
Boxed, Complex, ExactDoubleIterator, HandleKind, Shape, Value, WILDCARD_CHAR, WILDCARD_NAN,
23+
Boxed, Complex, ExactDoubleIterator, FfiType, HandleKind, Shape, Value, WILDCARD_CHAR,
24+
WILDCARD_NAN,
2425
};
2526

2627
/// Uiua's array type
@@ -230,33 +231,33 @@ impl ArrayMeta {
230231
}
231232

232233
/// Array pointer metadata
233-
#[derive(Debug, Clone, Copy)]
234+
#[derive(Debug, Clone)]
234235
pub struct MetaPtr {
235236
/// The pointer value
236237
pub ptr: usize,
237-
/// Whether the pointer should prevent the array's value from being shown
238-
pub raw: bool,
238+
/// The type of value stored at the pointer when read
239+
pub ty: FfiType,
239240
}
240241

241242
impl MetaPtr {
242243
/// Get a null metadata pointer
243244
pub const fn null() -> Self {
244-
Self { ptr: 0, raw: true }
245-
}
246-
/// Create a new metadata pointer
247-
pub fn new<T: ?Sized>(ptr: *const T, raw: bool) -> Self {
248245
Self {
249-
ptr: ptr as *const () as usize,
250-
raw,
246+
ptr: 0,
247+
ty: FfiType::Void,
251248
}
252249
}
250+
/// Create a new metadata pointer
251+
pub fn new(ptr: usize, ffi_type: FfiType) -> Self {
252+
Self { ptr, ty: ffi_type }
253+
}
253254
/// Get the pointer as a raw pointer
254-
pub fn get<T>(&self) -> *const T {
255-
self.ptr as *const T
255+
pub fn get(&self) -> *const u8 {
256+
self.ptr as *const _
256257
}
257258
/// Get the pointer as a raw pointer
258-
pub fn get_mut<T>(&self) -> *mut T {
259-
self.ptr as *mut T
259+
pub fn get_mut(&self) -> *mut u8 {
260+
self.ptr as *mut _
260261
}
261262
}
262263

0 commit comments

Comments
 (0)