-
Notifications
You must be signed in to change notification settings - Fork 342
[lldb] Add Foundation._NSSwiftTimeZone support #7074
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
#include "lldb/Symbol/Variable.h" | ||
#include "lldb/Symbol/VariableList.h" | ||
#include "lldb/Target/ProcessStructReader.h" | ||
#include "lldb/Target/SectionLoadList.h" | ||
#include "lldb/Target/Target.h" | ||
#include "lldb/Utility/LLDBLog.h" | ||
#include "lldb/Utility/Log.h" | ||
|
@@ -39,6 +40,7 @@ | |
#include "swift/Remote/MemoryReader.h" | ||
#include "swift/RemoteAST/RemoteAST.h" | ||
#include "swift/Runtime/Metadata.h" | ||
#include "swift/Strings.h" | ||
|
||
#include <sstream> | ||
|
||
|
@@ -2941,6 +2943,80 @@ SwiftLanguageRuntimeImpl::GetValueType(ValueObject &in_value, | |
return Value::ValueType::Scalar; | ||
} | ||
|
||
namespace { | ||
struct SwiftNominalType { | ||
std::string module; | ||
std::string identifier; | ||
}; | ||
|
||
// Find the Swift class that backs an ObjC type. | ||
// | ||
// A Swift class that uses the @objc(<ClassName>) attribute will emit ObjC | ||
// metadata into the binary. Typically, ObjC classes have a symbol in the form | ||
// of OBJC_CLASS_$_<ClassName>, however for Swift classes, there are two symbols | ||
// that both point to the ObjC class metadata, where the second symbol is a | ||
// Swift mangled name. | ||
std::optional<SwiftNominalType> GetSwiftClass(ValueObject &valobj, | ||
AppleObjCRuntime &objc_runtime) { | ||
// To find the Swift symbol, the following preparation steps are taken: | ||
// 1. Get the value's ISA pointer | ||
// 2. Resolve the ISA load address into an Address instance | ||
// 3. Get the Module that contains the Address | ||
auto descriptor_sp = objc_runtime.GetClassDescriptor(valobj); | ||
if (!descriptor_sp) | ||
return {}; | ||
|
||
auto isa_load_addr = descriptor_sp->GetISA(); | ||
Address isa; | ||
const auto §ions = objc_runtime.GetTargetRef().GetSectionLoadList(); | ||
if (!sections.ResolveLoadAddress(isa_load_addr, isa)) | ||
return {}; | ||
|
||
// Next, iterate over the Module's symbol table, looking for a symbol with | ||
// following criteria: | ||
// 1. The symbol address is the ISA address | ||
// 2. The symbol name is a Swift mangled name | ||
std::optional<StringRef> swift_symbol; | ||
auto find_swift_symbol_for_isa = [&](Symbol *symbol) { | ||
if (symbol->GetAddress() == isa) { | ||
StringRef symbol_name = | ||
symbol->GetMangled().GetMangledName().GetStringRef(); | ||
if (SwiftLanguageRuntime::IsSwiftMangledName(symbol_name)) { | ||
swift_symbol = symbol_name; | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
|
||
isa.GetModule()->GetSymtab()->ForEachSymbolContainingFileAddress( | ||
isa.GetFileAddress(), find_swift_symbol_for_isa); | ||
if (!swift_symbol) | ||
return {}; | ||
|
||
// Once the Swift symbol is found, demangle it into a node tree. The node tree | ||
// provides the final data, the name of the class and the name of its module. | ||
swift::Demangle::Context ctx; | ||
auto *global = ctx.demangleSymbolAsNode(*swift_symbol); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to do this right away — but for readability this could be a helper in SwiftDemangle.h? |
||
using Kind = Node::Kind; | ||
auto *class_node = swift_demangle::nodeAtPath( | ||
global, {Kind::TypeMetadata, Kind::Type, Kind::Class}); | ||
if (class_node && class_node->getNumChildren() == 2) { | ||
auto module_node = class_node->getFirstChild(); | ||
auto ident_node = class_node->getLastChild(); | ||
if (module_node->getKind() == Kind::Module && module_node->hasText() && | ||
ident_node->getKind() == Kind::Identifier && ident_node->hasText()) { | ||
auto module_name = module_node->getText(); | ||
auto class_name = ident_node->getText(); | ||
return SwiftNominalType{module_name.str(), class_name.str()}; | ||
} | ||
} | ||
|
||
return {}; | ||
} | ||
|
||
} // namespace | ||
|
||
bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType( | ||
ValueObject &in_value, lldb::DynamicValueType use_dynamic, | ||
TypeAndOrName &class_type_or_name, Address &address, | ||
|
@@ -2967,9 +3043,22 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType( | |
dyn_name.startswith("__NS")) | ||
return false; | ||
|
||
SwiftNominalType swift_class; | ||
|
||
if (auto maybe_swift_class = GetSwiftClass(in_value, *objc_runtime)) { | ||
swift_class = *maybe_swift_class; | ||
std::string type_name = | ||
(llvm::Twine(swift_class.module) + "." + swift_class.identifier).str(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this shorter?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unfortunately it's not |
||
dyn_class_type_or_name.SetName(type_name.data()); | ||
address.SetRawAddress(in_value.GetPointerValue()); | ||
} else { | ||
swift_class.module = swift::MANGLING_MODULE_OBJC; | ||
swift_class.identifier = dyn_name; | ||
} | ||
|
||
std::string remangled; | ||
{ | ||
// Create a mangle tree for __C.dyn_name?. | ||
// Create a mangle tree for Swift.Optional<$module.$class> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could also be a few helper functions (in a follow-up commit). |
||
using namespace swift::Demangle; | ||
NodeFactory factory; | ||
NodePointer global = factory.createNode(Node::Kind::Global); | ||
|
@@ -2980,7 +3069,8 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType( | |
NodePointer ety = factory.createNode(Node::Kind::Type); | ||
bge->addChild(ety, factory); | ||
NodePointer e = factory.createNode(Node::Kind::Enum); | ||
e->addChild(factory.createNode(Node::Kind::Module, "Swift"), factory); | ||
e->addChild(factory.createNode(Node::Kind::Module, swift::STDLIB_NAME), | ||
factory); | ||
e->addChild(factory.createNode(Node::Kind::Identifier, "Optional"), | ||
factory); | ||
ety->addChild(e, factory); | ||
|
@@ -2989,8 +3079,11 @@ bool SwiftLanguageRuntimeImpl::GetDynamicTypeAndAddress_ClangType( | |
NodePointer cty = factory.createNode(Node::Kind::Type); | ||
list->addChild(cty, factory); | ||
NodePointer c = factory.createNode(Node::Kind::Class); | ||
c->addChild(factory.createNode(Node::Kind::Module, "__C"), factory); | ||
c->addChild(factory.createNode(Node::Kind::Identifier, dyn_name), factory); | ||
c->addChild(factory.createNode(Node::Kind::Module, swift_class.module), | ||
factory); | ||
c->addChild( | ||
factory.createNode(Node::Kind::Identifier, swift_class.identifier), | ||
factory); | ||
cty->addChild(c, factory); | ||
|
||
auto mangling = mangleNode(global); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import Foundation | ||
|
||
struct Document { | ||
var kind: Kind | ||
var path: String | ||
|
||
enum Kind { | ||
case binary | ||
case text | ||
} | ||
} | ||
|
||
@objc(App) | ||
class App : NSObject { | ||
var name: String = "Debugger" | ||
var version: (Int, Int) = (1, 0) | ||
var recentDocuments: [Document]? = [ | ||
Document(kind: .binary, path: "/path/to/something"), | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
OBJC_SOURCES := main.m | ||
SWIFT_SOURCES := App.swift | ||
SWIFTFLAGS_EXTRAS := -parse-as-library | ||
|
||
include Makefile.rules |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import lldb | ||
from lldbsuite.test.lldbtest import * | ||
from lldbsuite.test.decorators import * | ||
import lldbsuite.test.lldbutil as lldbutil | ||
|
||
|
||
class TestCase(TestBase): | ||
@skipUnlessFoundation | ||
@swiftTest | ||
def test(self): | ||
"""Verify printing of Swift implemented ObjC objects.""" | ||
self.build() | ||
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m")) | ||
|
||
# For Swift implemented objects, it's assumed the ObjC runtime prints | ||
# only a pointer, and cannot generate any child values. | ||
self.expect("v -d no app", startstr="(App *) app = 0x") | ||
self.expect( | ||
"v -d no -P1 app", | ||
matching=False, | ||
substrs=["name", "version", "recentDocuments"], | ||
) | ||
|
||
# With dynamic typing, the Swift runtime produces Swift child values. | ||
self.expect("v app", substrs=["App?) app = 0x"]) | ||
self.expect("v app.name", startstr='(String) app.name = "Debugger"') | ||
self.expect( | ||
"v app.version", startstr="((Int, Int)) app.version = (0 = 1, 1 = 0)" | ||
) | ||
|
||
documents = """\ | ||
([a.Document]?) app.recentDocuments = 1 value { | ||
[0] = { | ||
kind = binary | ||
path = "/path/to/something" | ||
} | ||
} | ||
""" | ||
self.expect("v app.recentDocuments", startstr=documents) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#import <Foundation/Foundation.h> | ||
|
||
@interface App : NSObject | ||
@end | ||
|
||
int main() { | ||
App *app = [App new]; | ||
printf("break here\n"); | ||
return 0; | ||
} |
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.
There might be one ore two opportunities for comments in this function to explain the algorithm :-)
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.
done