NSInvocation calls wrong objc_msgSend dispatcher when calling method with Objective-C++ return value
| Originator: | ben | ||
| Number: | rdar://33655497 | Date Originated: | 8/1/2017 |
| Status: | Open | Resolved: | |
| Product: | iOS SDK | Product Version: | Foundation |
| Classification: | Serious Bug | Reproducible: | Always |
Summary: Suppose we have an Objective-C method in ARC code with an Objective C++ return value: - (Foo::Bar)doSomething; And suppose Foo::Bar is just a C++ class that wraps a single Objective-C object in an ivar. When calling this method with NSInvocation, NSInvocation will read the method signature and decide too call -doSomething via objc_msgSend. However, if you call this method directly, you will see that the compiler actually calls the method via objc_msgSend_stret. This means that -doSomething ends up getting called with its arguments in the wrong order, since objc_msgSend and objc_msgSend_stret have differing ordering of parameters. The problem here might be in the method signatures emitted by the compiler, because the method signatures of both the ARC and non-ARC implementations of the method is exactly the same even though the dispatcher used is different. Steps to Reproduce: # Test 1 This test shows that NSInvocation always calls this particular method type with objc_msgSend. Code: https://gist.github.com/bnham/eb1b67fc4eb79ab4ef1aa7a34c7a2a65 When compiling the C++ class *without* ARC, the arguments to the method are received in the correct order: $ ./test1-no-arc Called directly: +[Foo foo]: receiver is CORRECT Called via NSInvocation: +[Foo foo]: receiver is CORRECT When compiling the C++ class *with* ARC, the arguments to the method are received in the incorrect order: $ ./test1-arc Called directly: +[Foo foo]: receiver is CORRECT Called via NSInvocation: +[Foo foo]: receiver is INCORRECT # Test 2 This test shows that the problem is that MRR code calls this particular method with objc_msgSend, while ARC code calls this particular method with objc_msgSend_stret. Code: https://gist.github.com/bnham/f9a0cf6c1cf232a1fbb1ea8bc8be7c9f When the caller and callee are both compiled with ARC, everything works: $ ./test2-arc-caller +[Foo foo]: receiver is CORRECT When the caller is compiled with MRR and the callee is compiled with ARC, args are received in the wrong order: $ ./test2-mrr-caller +[Foo foo]: receiver is INCORRECT Expected Results: Both MRR and ARC code should call the same dispatcher for this method. Calls to this method via NSInvocation should always pass arguments in the correct order. Observed Results: MRR and ARC code call different dispatchers for this method. Calls to this method via NSInvocation do not always pass arguments in the correct order. iOS Version / Build: iOS 10 Simulator, Mac OS X 10.12 Additional Notes: Possible workaround is to pad the returned C++ class with enough bytes to convince the compiler to always use objc_msgSend_stret.
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!