-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
feat(ext/ffi): structs by value #15060
Conversation
@littledivy PTAL |
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.
LGTM! Really nice to have the last piece of the puzzle, so to say :)
Can tests be added for passing structs by value? |
I do think we should actually be checking the buffer for size validity. Trying to pass a 0 size buffer as an eg. |
@littledivy please take a look. @DjDeveloperr could you please rebase? |
5b0c6e0
to
cc84460
Compare
@littledivy @aapoalas PTAL |
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.
Left some comments. Looks mostly good to me, but I'd change a few things before landing.
Er, we need nested struct tests. Furthermore, recursive structs should be rejected. |
Seems to panic when deserializing in Rust land. Should this be handled in JS side? I think it's very edge case, we don't really need a check for this. |
Yes; panicking represents a bug - panics shouldn't actually be reached. |
Shouldn't this be actually handled in serde_v8? Its overflowing stack when deserializing this object. |
Yup, that's the issue. |
So, do I need to implement a workaround for this bug of serde_v8 right now? |
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.
How does ownership of the buffer work? Are we copying the buffer?
When passing buffer as struct argument, we directly pass pointer from backing store. Libffi doesn't seem to take any ownership of it. See: #15060 (comment) When returning struct we pre allocate buffer in JS side and pass its pointer to libffi so return value can be written to it. |
Ah ok that makes sense. libffi pushes the struct onto the stack. What happens when structs are too big? On aarch64, pass-by-value structs large enough are actually passed by reference. From https://devblogs.microsoft.com/oldnewthing/20220823-00/?p=107041 "The AArch64 processor (aka arm64), part 20: The classic calling convention"
|
I'm not sure how libffi handles this internally, but it would be pretty weird (and problematic) if it is taking ownership of arguments only in certain cases. |
#ifdef CTYPES_PASS_BY_REF_HACK
size_t size = atypes[i]->size;
if (IS_PASS_BY_REF(size)) {
void *tmp = alloca(size);
if (atypes[i]->type == FFI_TYPE_STRUCT)
memcpy(tmp, args[i].value.p, size);
else
memcpy(tmp, (void*)&args[i].value, size);
avalues[i] = tmp;
}
else
#endif We need to do this workaround for large structs. Code from cpython above. libffi issue: libffi/libffi#694 |
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.
Oh i misunderstood the PR, the calls seems to accept the raw buffer from the user instead of a JS object. We could make this inbuilt and have JIT unwrapping for object -> buffer conversion?
call({ x: 1, y : 2 });
// call(...) codegen:
const u8 = new Uint8Array(size);
function call(obj) {
const { x, y } = obj;
u8[0] = x;
u8[1] = y;
ffi_call(u8)
}
Also how is struct padding defined? Right now it is only "packed" structs IIUC |
It's only C repr structs, non-packed. libffi does not give any options for structs, they're C repr always. Doing a packed struct needs to be done manually with u8s. |
I think this would be unnecessary / can be done in user-land fairly easily. From my |
I don't think we should do this, at least only for passing structs. As AapoAlas stated like it is easily doable in user land. If we do however add struct serialization, it should be complete and also support deserialization (e.g for returning). It's also useful for APIs that expect structs by pointer too. But I think that's for another PR. |
0c17ff5
to
d0d1c68
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.
I want to land this, let's get the CI green. @DjDeveloperr Do you need help with something?
Passing structs in nonblocking calls doesn't seem to work, I couldn't find the reason though. In normal sync calls, struct is passed correctly by value but in nonblocking calls the struct value is actually a pointer to another pointer of struct's memory, which is weird because both type of calls use same function for parsing struct arguments. After some debugging, found that when we pass the same struct buffer as argument to both sync & async calls, literally same libffi Also, the windows fastci is failing for unknown reason... EDIT: Commented out the test case for it. CI is green now but it's still a bug. Seems windows fastci had seg fault because of this (unlike on Linux where I tested, it had no segmentation fault). |
d0d1c68
to
c31b829
Compare
7a20d99
to
a85103b
Compare
LGTM. |
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.
LGTM! Thank you, this is a nice feature to have.
Adds support for passing and returning structs as buffers to FFI. This does not implement fastapi support for structs. Needed for certain system APIs such as AppKit on macOS.
Adds support for passing and returning structs as buffers to FFI. This does not implement fastapi support for structs. Needed for certain system APIs such as AppKit on macOS.
Adds support for passing and returning structs as buffers to FFI.
Does not implement fastapi support for structs.
Needed for certain system APIs such as AppKit on macOS