Skip to content
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

OPTION_INVOKING_METHOD should be a public field #1394

Open
dkocher opened this issue Nov 4, 2021 · 7 comments
Open

OPTION_INVOKING_METHOD should be a public field #1394

dkocher opened this issue Nov 4, 2021 · 7 comments

Comments

@dkocher
Copy link
Contributor

dkocher commented Nov 4, 2021

The field in Function#OPTION_INVOKING_METHOD should be public as it is required when using the public method invoke in the same class with the options argument.

@matthiasblaesing
Copy link
Member

What do you try to do? The method is only used to determine the number of fixed arguments, to setup the right native call frame. I'm interest in the use-case.

@dkocher
Copy link
Contributor Author

dkocher commented Nov 4, 2021

This is used for the mapping of objc_msgSend on macOS in Rococoa.

@matthiasblaesing
Copy link
Member

Thank you - a quick first thought: I think it would be better to rethink this on the JNA side. The core problem is, that the foreign function interface needs the information how many fixed functions parameters are present. This information is currently deduced from the java method signature. However it is perfectly possible to call native functions without a java method present and that should be enough.

@dkocher
Copy link
Contributor Author

dkocher commented Nov 7, 2021

The particular use case I am just looking at is very related to your comment. The objc_msgSend beast takes a vararg parameter and when letting JNA know about this method signature will lead to unexpected crashes on aarch64. This is documented in Don’t Redeclare a Function to Have Variable Parameters at Addressing Architectural Differences in Your macOS Code and was previously discussed in this thread.

Thus I can make it work for invocations with no variadic arguments by omitting the OPTION_INVOKING_METHOD option.

Now, invoking objc_msgSend as vararg with passing the Method parameter accordingly will crash with

C  [libobjc.A.dylib+0x6310]  objc_retain+0x10
C  [CoreFoundation+0x689fc]  +[NSArray arrayWithObjects:]+0x19c
C  [jna1860582620152237549.tmp+0x10044]  ffi_prep_closure_loc+0x198c

with version 5.9.0, but succeed with the latest master branch with #1383 included when objc_MsgSend is defined as a method with parameters "objc_msgSend", ID.class, Selector.class, Object.class, Object[].class and passed to invoke accordingly. It doesn't work when omitting the first fixed argument as in "objc_msgSend", ID.class, Selector.class, Object[].class with tests mapping the variadic constructor of NSArray.

Not sure if this is the appropriate place to discuss this but I thought I should write it down here as this what caused me to report this issue in the first place.

@matthiasblaesing
Copy link
Member

Ok - it all comes down to the question: What is the real definition of objc_msgSend. And a second question: Are there really multiple definitions of that method, or is ist defined somewhere in the core libraries? From the description objc_msgSend is on the same level as the COM libraries on windows and these are bound exactly once. If so, the whole magic with getting function pointers and calling them through the invocation interface are superfluous and can be replaced with a single Library interface (and instance).

@dkocher
Copy link
Contributor Author

dkocher commented Nov 9, 2021

Because objc_msgSend must support calls to any method, it accepts a variable list of parameters instead of fixed parameters.

The same code fails on arm64 because the caller of the function and the function itself marshal the parameters differently. The function expects all of the parameters to be in registers. However, the caller passes only the first parameter in a register; it passes all remaining parameters on the stack.

Because of this I assume my workaround works by defining objc_MsgSend like this.

@fkistner
Copy link
Contributor

fkistner commented Nov 9, 2021

Ok - it all comes down to the question: What is the real definition of objc_msgSend.

See https://www.mikeash.com/pyblog/objc_msgsends-new-prototype.html for a detailed explanation.
The proper way to use objc_msgSend looks something like this:

    @interface Foo : NSObject @end
    @implementation Foo
    - (void)log:(float)x {
        printf("%f\n", x);
    }
    @end

    int main(int argc, char **argv) {
        ((void (*)(id, SEL, float))objc_msgSend)(obj, @selector(log:), M_PI);
    }

The rule of thumb seems to be: You take the method prototype, prepend an id argument for the object/class instance as well as an SEL argument for the selector to get the objc_msgSend prototype.

Therefore, my conclusion has been such far that objc_msgSend cannot properly be bound using JNA to a Java method, since its prototype changes depending on the prototype of the target method. Happy to learn, if there's a better way of doing things.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants