Skip to content

Commit 8c46ae5

Browse files
authored
[lldb] Handle @objc classes in Swift runtime (#8033)
@objc classes can be used from ObjC. In those cases, the Swift runtime is used. This change implements the handling of @objc classes in the Swift language runtime. (cherry picked from commit b4e30b7) (cherry-picked from commit 6e5a4b5)
1 parent a995aa0 commit 8c46ae5

File tree

5 files changed

+168
-4
lines changed

5 files changed

+168
-4
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "lldb/Symbol/Variable.h"
2525
#include "lldb/Symbol/VariableList.h"
2626
#include "lldb/Target/ProcessStructReader.h"
27+
#include "lldb/Target/SectionLoadList.h"
2728
#include "lldb/Target/Target.h"
2829
#include "lldb/Utility/LLDBLog.h"
2930
#include "lldb/Utility/Log.h"
@@ -2233,6 +2234,80 @@ SwiftLanguageRuntimeImpl::GetValueType(ValueObject &in_value,
22332234
return Value::ValueType::Scalar;
22342235
}
22352236

2237+
namespace {
2238+
struct SwiftNominalType {
2239+
std::string module;
2240+
std::string identifier;
2241+
};
2242+
2243+
// Find the Swift class that backs an ObjC type.
2244+
//
2245+
// A Swift class that uses the @objc(<ClassName>) attribute will emit ObjC
2246+
// metadata into the binary. Typically, ObjC classes have a symbol in the form
2247+
// of OBJC_CLASS_$_<ClassName>, however for Swift classes, there are two symbols
2248+
// that both point to the ObjC class metadata, where the second symbol is a
2249+
// Swift mangled name.
2250+
std::optional<SwiftNominalType> GetSwiftClass(ValueObject &valobj,
2251+
AppleObjCRuntime &objc_runtime) {
2252+
// To find the Swift symbol, the following preparation steps are taken:
2253+
// 1. Get the value's ISA pointer
2254+
// 2. Resolve the ISA load address into an Address instance
2255+
// 3. Get the Module that contains the Address
2256+
auto descriptor_sp = objc_runtime.GetClassDescriptor(valobj);
2257+
if (!descriptor_sp)
2258+
return {};
2259+
2260+
auto isa_load_addr = descriptor_sp->GetISA();
2261+
Address isa;
2262+
const auto &sections = objc_runtime.GetTargetRef().GetSectionLoadList();
2263+
if (!sections.ResolveLoadAddress(isa_load_addr, isa))
2264+
return {};
2265+
2266+
// Next, iterate over the Module's symbol table, looking for a symbol with
2267+
// following criteria:
2268+
// 1. The symbol address is the ISA address
2269+
// 2. The symbol name is a Swift mangled name
2270+
std::optional<StringRef> swift_symbol;
2271+
auto find_swift_symbol_for_isa = [&](Symbol *symbol) {
2272+
if (symbol->GetAddress() == isa) {
2273+
StringRef symbol_name =
2274+
symbol->GetMangled().GetMangledName().GetStringRef();
2275+
if (SwiftLanguageRuntime::IsSwiftMangledName(symbol_name)) {
2276+
swift_symbol = symbol_name;
2277+
return false;
2278+
}
2279+
}
2280+
return true;
2281+
};
2282+
2283+
isa.GetModule()->GetSymtab()->ForEachSymbolContainingFileAddress(
2284+
isa.GetFileAddress(), find_swift_symbol_for_isa);
2285+
if (!swift_symbol)
2286+
return {};
2287+
2288+
// Once the Swift symbol is found, demangle it into a node tree. The node tree
2289+
// provides the final data, the name of the class and the name of its module.
2290+
swift::Demangle::Context ctx;
2291+
auto *global = ctx.demangleSymbolAsNode(*swift_symbol);
2292+
using Kind = Node::Kind;
2293+
auto *class_node = swift_demangle::nodeAtPath(
2294+
global, {Kind::TypeMetadata, Kind::Type, Kind::Class});
2295+
if (class_node && class_node->getNumChildren() == 2) {
2296+
auto module_node = class_node->getFirstChild();
2297+
auto ident_node = class_node->getLastChild();
2298+
if (module_node->getKind() == Kind::Module && module_node->hasText() &&
2299+
ident_node->getKind() == Kind::Identifier && ident_node->hasText()) {
2300+
auto module_name = module_node->getText();
2301+
auto class_name = ident_node->getText();
2302+
return SwiftNominalType{module_name.str(), class_name.str()};
2303+
}
2304+
}
2305+
2306+
return {};
2307+
}
2308+
2309+
} // namespace
2310+
22362311
bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType(
22372312
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
22382313
TypeAndOrName &class_type_or_name, Address &address,
@@ -2259,9 +2334,22 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType(
22592334
dyn_name.startswith("__NS"))
22602335
return false;
22612336

2337+
SwiftNominalType swift_class;
2338+
2339+
if (auto maybe_swift_class = GetSwiftClass(in_value, *objc_runtime)) {
2340+
swift_class = *maybe_swift_class;
2341+
std::string type_name =
2342+
(llvm::Twine(swift_class.module) + "." + swift_class.identifier).str();
2343+
dyn_class_type_or_name.SetName(type_name.data());
2344+
address.SetRawAddress(in_value.GetPointerValue());
2345+
} else {
2346+
swift_class.module = swift::MANGLING_MODULE_OBJC;
2347+
swift_class.identifier = dyn_name;
2348+
}
2349+
22622350
std::string remangled;
22632351
{
2264-
// Create a mangle tree for __C.dyn_name?.
2352+
// Create a mangle tree for Swift.Optional<$module.$class>
22652353
using namespace swift::Demangle;
22662354
NodeFactory factory;
22672355
NodePointer global = factory.createNode(Node::Kind::Global);
@@ -2272,7 +2360,8 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType(
22722360
NodePointer ety = factory.createNode(Node::Kind::Type);
22732361
bge->addChild(ety, factory);
22742362
NodePointer e = factory.createNode(Node::Kind::Enum);
2275-
e->addChild(factory.createNode(Node::Kind::Module, "Swift"), factory);
2363+
e->addChild(factory.createNode(Node::Kind::Module, swift::STDLIB_NAME),
2364+
factory);
22762365
e->addChild(factory.createNode(Node::Kind::Identifier, "Optional"),
22772366
factory);
22782367
ety->addChild(e, factory);
@@ -2281,10 +2370,11 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType(
22812370
NodePointer cty = factory.createNode(Node::Kind::Type);
22822371
list->addChild(cty, factory);
22832372
NodePointer c = factory.createNode(Node::Kind::Class);
2373+
c->addChild(factory.createNode(Node::Kind::Module, swift_class.module),
2374+
factory);
22842375
c->addChild(
2285-
factory.createNode(Node::Kind::Module, swift::MANGLING_MODULE_OBJC),
2376+
factory.createNode(Node::Kind::Identifier, swift_class.identifier),
22862377
factory);
2287-
c->addChild(factory.createNode(Node::Kind::Identifier, dyn_name), factory);
22882378
cty->addChild(c, factory);
22892379

22902380
auto mangling = mangleNode(global);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Foundation
2+
3+
struct Document {
4+
var kind: Kind
5+
var path: String
6+
7+
enum Kind {
8+
case binary
9+
case text
10+
}
11+
}
12+
13+
@objc(App)
14+
class App : NSObject {
15+
var name: String = "Debugger"
16+
var version: (Int, Int) = (1, 0)
17+
var recentDocuments: [Document]? = [
18+
Document(kind: .binary, path: "/path/to/something"),
19+
]
20+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
OBJC_SOURCES := main.m
2+
SWIFT_SOURCES := App.swift
3+
SWIFTFLAGS_EXTRAS := -parse-as-library
4+
5+
include Makefile.rules
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import lldb
2+
from lldbsuite.test.lldbtest import *
3+
from lldbsuite.test.decorators import *
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
class TestCase(TestBase):
8+
@skipUnlessFoundation
9+
@swiftTest
10+
def test(self):
11+
"""Verify printing of Swift implemented ObjC objects."""
12+
self.build()
13+
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m"))
14+
15+
# For Swift implemented objects, it's assumed the ObjC runtime prints
16+
# only a pointer, and cannot generate any child values.
17+
self.expect("v -d no app", startstr="(App *) app = 0x")
18+
self.expect(
19+
"v -d no -P1 app",
20+
matching=False,
21+
substrs=["name", "version", "recentDocuments"],
22+
)
23+
24+
# With dynamic typing, the Swift runtime produces Swift child values.
25+
self.expect("v app", substrs=["App?) app = 0x"])
26+
self.expect("v app.name", startstr='(String) app.name = "Debugger"')
27+
self.expect(
28+
"v app.version", startstr="((Int, Int)) app.version = (0 = 1, 1 = 0)"
29+
)
30+
31+
documents = """\
32+
([a.Document]?) app.recentDocuments = 1 value {
33+
[0] = {
34+
kind = binary
35+
path = "/path/to/something"
36+
}
37+
}
38+
"""
39+
self.expect("v app.recentDocuments", startstr=documents)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface App : NSObject
4+
@end
5+
6+
int main() {
7+
App *app = [App new];
8+
printf("break here\n");
9+
return 0;
10+
}

0 commit comments

Comments
 (0)