-
Notifications
You must be signed in to change notification settings - Fork 232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added ffi-buffer scaffolding functions #2039
Conversation
9d6063a
to
53832f6
Compare
That's crazy! You could refactor the bindings to use only this capability :) Not that you would, but it's just an extra level of ffi indirection over the same ffi. |
I've thought about the same thing and at points have had the urge to say we should just move all languages to using this. But then I think about the converse of your point: it's the essentially the same FFI so there's not much benefit to switching over the existing bindings. That said, it could help us avoid some limitations, like the fact that ctypes doesn't let you return structures or the JNA bugs we've dealt with. I don't think that makes it worth it, but it's interesting to think about. |
I do kinda like the idea that today, a function that only uses "ffi-native" types as args or return types ends up with the "correct and expected" signature - ie, |
0582bde
to
43f2890
Compare
The more I think about it, the more I wonder if this could be a good system for bindings in general.
This is true for Swift, but for Python and Kotlin it's not so simple because the libffi API uses a similar buffer to pass function arguments. If I compare this system to the current system, I think there will still be some overhead, since now we're using an extra layer of argument buffers, but it's probably small. A simpler FFI layer for Python and Kotlin could unblock some performance wins, like using switching from using RustBuffers for dictionaries/enums and using structs/tagged unions instead. I think that the system should still be additive in the sense that we should still export the current scaffolding functions for languages like Swift that can call them directly and can handle the fiddly C bits. |
c31a83a
to
2dcd78a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you already have some example code in gecko-js how this would be used?
uniffi_core/src/ffi/ffiserialize.rs
Outdated
} | ||
|
||
/// Write a value to a u64 buffer ref and advance it | ||
fn write(buf: &mut &mut [u64], value: Self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&mut &mut [u64]
-- that's a type you don't see often.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got that from the bytes
crate, that implements a bunch of &mut
methods on &mut u8
.
uniffi_core/src/ffi/ffiserialize.rs
Outdated
u32, | ||
i64, | ||
u64, | ||
*const std::ffi::c_void, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
followers of pointer provenance will not like us for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not that comfortable with it myself. That's one of the reasons I want to move away from pointers and towards handles. Someday I'll get back to working on #1823.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking that maybe instead of making the elements all u64
it should be a C union of all the "primitive" FFI types (u8
- i64
, float
, double
and void *
). That way we don't need to cast things so much. In addition to messing with pointer provenance, it's actually not so trivial to do the reverse casts on the C++ side for the int types. The floating point casts also get complicated, since C++ doesn't technically have a f32
or f64
type before C++ 23.
const SIZE: usize = 1; | ||
|
||
fn get(buf: &[u64]) -> Self { | ||
// Safety: this relies on the foreign code passing us valid pointers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uff. Dangerous.
But I guess not the only time we assume stuff like this in UniFFI.
On top of unsafety this also makes up a lifetime, doesn't it? We better prove all usage of this correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I think we maybe can get rid of this one. I think the only reason we needed to support references was the &mut RustCallStatus
out param. But my last force-push got rid of that. If we're going with the ffi-buffer system then it's simpler to write a tuple into the return buffer vs. inputing an out-param.
Coming soon... |
09397a5
to
e142976
Compare
I put some more work on this over the last week or so and I think it's ready to start talking about this for real:
|
I'm really confused as to why these tests are failing. |
No! The command you specify does what you said it does, but a plain
So still with the same bad advice about how to re-run it, but the error does appear to be real? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems great to me! I've a question re features that I don't think we need to block on, and you still have that odd test issue, but 👍
uniffi_core/src/ffi/rustbuffer.rs
Outdated
@@ -2,7 +2,10 @@ | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |||
|
|||
use crate::ffi::{rust_call, ForeignBytes, RustCallStatus}; | |||
use crate::{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should uniffi_core also grow a feature which enables this?
Still not sure what's causing the doctest failure, the crates compile file when you build them and when you run the normal tests. I'm just going to change the |
These are alternate versions of the current scaffolding functions that use u64 buffers to handle their input/output. The main use case is the gecko-js bindings used in Firefox. This allows us to replace the generated C++ code with static code, since the scaffolding functions now always have the exact same signature. This commit generates FFI buffer versions for scaffolding functions defined for user functions/methods. It does not generate them for functions with known/static signatures like the rust buffer FFI functions, or the object clone/delete functions. If the signature is always the same, then there isn't a problem calling the normal scaffolding functions.
These are alternate versions of the current scaffolding functions that use u64 buffers to handle their input/output.
The main use case is the gecko-js bindings used in Firefox. This allows us to replace the generated C++ code with static code, since the scaffolding functions now always have the exact same signature.
I added ffi buffer versions of all generated scaffolding functions. I didn't make them for "static" functions, like the rust_buffer FFI functions. I don't think we need them there since the signature is always the same.
Added
do not merge
because I want to test that this will actually work first, but the code is ready for review.