You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[lldb] Fix stepping into Objective-C interop ctors (#10697)
* [lldb][nfc] Create helper functions in SwiftLanguageRuntimeNames
These will be useful to reuse code in upcoming commits.
* [lldb] Fix stepping into ObjcC ctor from Swift
When constructing an Objective C object of type `Foo` from Swift, this
sequence of function calls is used:
```
* frame #0: 0x000000010000147c test.out`-[Foo initWithString:](self=0x00006000023ec000, _cmd="initWithString:", value=@"Bar") -[Foo initWithString:] at Foo.m:9:21
frame #1: 0x00000001000012bc test.out`@nonobjc Foo.init(string:) $sSo3FooC6stringABSS_tcfcTO at <compiler-generated>:0
frame #2: 0x0000000100001170 test.out`Foo.__allocating_init(string:) $sSo3FooC6stringABSS_tcfC at Foo.h:0
frame #3: 0x0000000100000ed8 test.out`work() $s4test4workyyF at main.swift:5:18
```
Frames 1 and 2 are common with pure Swift classes, and LLDB has a Thread
Plan to go from `Foo.allocating_init` -> `Foo.init`.
In the case of Objcetive C interop, `Foo.init` has no user code, and is
annotated with `@nonobjc`. The debugger needs a plan to go from that
code to the Objective C implementation. This is what this patch attempts
to fix by creating a plan that runs to any symbol matching `Foo init`
(this will match all the :withBlah suffixes).
This seems to be the only possible fix for this. While Objective C
constructors are not necessarily called init, the interop layer seems to
assume this.
The only other alternative has some obstacles that could not be easily
overcome. Here's the main idea for that. The assembly for `@nonobjc
Foo.init` looks like (deleted all non branches):
```
test.out`@nonobjc Foo.init(string:):
...
0x1000012a0 <+20>: bl 0x100001618 ; symbol stub for: Swift.String._bridgeToObjectiveC() -> __C.NSString
...
0x1000012b8 <+44>: bl 0x100001630 ; symbol stub for: objc_msgSend
...
0x1000012e8 <+92>: ret
```
If we had more String arguments, there would be more calls to
`_bridgeToObjectiveC`. The call to `objc_msgSend` is the important one,
and LLDB knows how to go from that to the target of the message, LLDB
has ThreadPlans for that. However, setting a breakpoint on
`objc_msgSend` would fail: the calls to `_bridgeToObjectiveC` may also
call `objc_msgSend`, so LLDB would end up in the wrong `objc_msgSend`.
This is not entirely bad, LLDB would step back to `Foo.init`.
Here's the catch: the language runtime refuses to create other plans if
PC is not at the start of the function, which makes sense, as it would
not be able to distinguish if its job was already done previously or
not, unless it had a stateful plan (which it doesn't today).
0 commit comments