-
Notifications
You must be signed in to change notification settings - Fork 353
[LLDB] Add ObjC object typeref support to SwiftRuntimeTypeVisitor #11929
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
base: swift/release/6.3
Are you sure you want to change the base?
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 |
|---|---|---|
|
|
@@ -1015,30 +1015,36 @@ SwiftLanguage::GetHardcodedSynthetics() { | |
| g_formatters.push_back( | ||
| [](lldb_private::ValueObject &valobj, lldb::DynamicValueType dyn_type, | ||
| FormatManager &format_manager) -> lldb::SyntheticChildrenSP { | ||
| struct IsEligible { | ||
| static bool Check(ValueObject &valobj, const CompilerType &type) { | ||
| bool is_imported = false; | ||
|
|
||
| if (type.IsValid()) { | ||
| auto swift_ast_ctx = | ||
| type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>(); | ||
| if (swift_ast_ctx && swift_ast_ctx->IsImportedType( | ||
| type.GetOpaqueQualType(), nullptr)) | ||
| is_imported = true; | ||
| } | ||
| // Determine if this is an Objecrtive-C object that benefits | ||
|
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.
|
||
| // from the ObjC synthetic child provider. | ||
| std::function<bool(ValueObject &, CompilerType)> is_eligible = | ||
| [&](ValueObject &valobj, CompilerType type) { | ||
| bool is_imported = false; | ||
|
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.
|
||
| if (!type.IsValid()) | ||
| return false; | ||
|
|
||
| ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); | ||
| if (is_imported && type.GetNumFields(&exe_ctx) == 0) | ||
| return true; | ||
| if (valobj.IsBaseClass() && type.IsRuntimeGeneratedType()) { | ||
| auto parent(valobj.GetParent()); | ||
| if (!parent) | ||
| CompilerType clang_type; | ||
| if (auto ts = | ||
| type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>()) | ||
| if (ts->IsImportedType(type.GetOpaqueQualType(), &clang_type)) | ||
| is_imported = true; | ||
|
|
||
| // Could be an Objective-C object with no debug info; we | ||
| // can get ivars from the runtime. | ||
| if (is_imported && !clang_type) | ||
| return true; | ||
| if (clang_type && Flags(clang_type.GetTypeClass()) | ||
|
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 a check that this type is any objective c type? Perhaps there could be a comment explaining this, or move this to a helper function. |
||
| .AnySet(eTypeClassObjCInterface | | ||
| eTypeClassObjCObjectPointer | | ||
| eTypeClassObjCObject)) | ||
| return true; | ||
| if (valobj.IsBaseClass() && type.IsRuntimeGeneratedType()) { | ||
| if (ValueObject *parent = valobj.GetParent()) | ||
| return is_eligible(*parent, parent->GetCompilerType()); | ||
| return false; | ||
| return IsEligible::Check(*parent, parent->GetCompilerType()); | ||
| } | ||
| return false; | ||
| } | ||
| }; | ||
| } | ||
| return false; | ||
| }; | ||
|
|
||
| if (dyn_type == lldb::eNoDynamicValues) | ||
| return nullptr; | ||
|
|
@@ -1048,7 +1054,7 @@ SwiftLanguage::GetHardcodedSynthetics() { | |
| g_runtime_synths_category_name, can_create)); | ||
| if (!category_sp || category_sp->IsEnabled() == false) | ||
| return nullptr; | ||
| if (IsEligible::Check(valobj, type)) { | ||
| if (is_eligible(valobj, type)) { | ||
| ProcessSP process_sp(valobj.GetProcessSP()); | ||
| if (!process_sp) | ||
| return nullptr; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "LLDBMemoryReader.h" | ||
| #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" | ||
| #include "Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h" | ||
| #include "ReflectionContextInterface.h" | ||
| #include "SwiftLanguageRuntime.h" | ||
|
|
@@ -33,7 +34,9 @@ | |
| #include "lldb/ValueObject/ValueObjectMemory.h" | ||
| #include "llvm/ADT/STLExtras.h" | ||
| #include "llvm/Support/Casting.h" | ||
| #include "llvm/Support/Error.h" | ||
|
|
||
| #include "lldb/lldb-enumerations.h" | ||
| #include "swift/AST/ASTContext.h" | ||
| #include "swift/AST/ASTMangler.h" | ||
| #include "swift/AST/ASTWalker.h" | ||
|
|
@@ -728,7 +731,7 @@ class SwiftRuntimeTypeVisitor { | |
| CompilerType m_type; | ||
| ValueObject *m_valobj = nullptr; | ||
| bool m_hide_superclass = false; | ||
| bool m_include_clang_types = false; | ||
| bool m_omit_empty_base_classes = true; | ||
| bool m_visit_superclass = false; | ||
|
|
||
| void SetFlavor() { | ||
|
|
@@ -757,26 +760,27 @@ class SwiftRuntimeTypeVisitor { | |
| using VisitCallback = std::function<llvm::Error( | ||
| CompilerType, unsigned, GetChildNameClosure, GetChildInfoClosure)>; | ||
| SwiftRuntimeTypeVisitor(SwiftLanguageRuntime &runtime, CompilerType type, | ||
| ValueObject *valobj) | ||
| : m_runtime(runtime), m_type(type), m_valobj(valobj) { | ||
| ValueObject *valobj, bool omit_empty_base_classes) | ||
| : m_runtime(runtime), m_type(type), m_valobj(valobj), | ||
| m_omit_empty_base_classes(omit_empty_base_classes) { | ||
| if (valobj) | ||
| m_exe_ctx = valobj->GetExecutionContextRef(); | ||
| SetFlavor(); | ||
| } | ||
| SwiftRuntimeTypeVisitor(SwiftLanguageRuntime &runtime, CompilerType type, | ||
| ExecutionContextScope *exe_scope, | ||
| bool hide_superclass, bool include_clang_types) | ||
| bool hide_superclass, bool omit_empty_base_classes) | ||
| : m_runtime(runtime), m_type(type), m_hide_superclass(hide_superclass), | ||
| m_include_clang_types(include_clang_types) { | ||
| m_omit_empty_base_classes(omit_empty_base_classes) { | ||
| if (exe_scope) | ||
| exe_scope->CalculateExecutionContext(m_exe_ctx); | ||
| SetFlavor(); | ||
| } | ||
| SwiftRuntimeTypeVisitor(SwiftLanguageRuntime &runtime, CompilerType type, | ||
| ExecutionContext *exe_ctx, bool hide_superclass, | ||
| bool include_clang_types, bool visit_superclass) | ||
| bool omit_empty_base_classes, bool visit_superclass) | ||
| : m_runtime(runtime), m_type(type), m_hide_superclass(hide_superclass), | ||
| m_include_clang_types(include_clang_types), | ||
| m_omit_empty_base_classes(omit_empty_base_classes), | ||
| m_visit_superclass(visit_superclass) { | ||
| if (exe_ctx) | ||
| m_exe_ctx = *exe_ctx; | ||
|
|
@@ -800,9 +804,7 @@ class SwiftRuntimeTypeVisitor { | |
|
|
||
| llvm::Expected<unsigned> | ||
| SwiftRuntimeTypeVisitor::VisitImpl(std::optional<unsigned> visit_only, | ||
| VisitCallback visit_callback) | ||
|
|
||
| { | ||
| VisitCallback visit_callback) { | ||
| if (!m_type) | ||
| return llvm::createStringError("invalid type"); | ||
|
|
||
|
|
@@ -854,19 +856,91 @@ SwiftRuntimeTypeVisitor::VisitImpl(std::optional<unsigned> visit_only, | |
| return success; | ||
| } | ||
|
|
||
| // FIXME: Remove this entire mode. | ||
| assert(!m_include_clang_types || (m_include_clang_types && count_only)); | ||
| if (m_include_clang_types && count_only) { | ||
| auto visit_clang_type = | ||
| [&](CompilerType clang_type) -> llvm::Expected<unsigned> { | ||
| auto swiftify = [&](CompilerType type) { | ||
| if (!type.GetTypeSystem().isa_and_nonnull<TypeSystemClang>()) | ||
| return type; | ||
| CompilerType swift_type = ts.ConvertClangTypeToSwiftType(type); | ||
| return swift_type ?: type; | ||
| }; | ||
| unsigned depth = 0; | ||
| auto n_or_err = | ||
| clang_type.GetNumChildren(m_omit_empty_base_classes, &m_exe_ctx); | ||
| if (!n_or_err) | ||
| return n_or_err.takeError(); | ||
| unsigned n = *n_or_err; | ||
| bool is_signed, is_enum = clang_type.IsEnumerationType(is_signed); | ||
|
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 the usage of |
||
| if (count_only) | ||
| return is_enum ? 1 : n; | ||
| if (is_enum) { | ||
| auto get_name = [&]() -> std::string { return "rawValue"; }; | ||
| auto get_info = [&]() -> llvm::Expected<ChildInfo> { | ||
| auto ts = | ||
| clang_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>(); | ||
|
|
||
| if (!ts) | ||
| return llvm::createStringError("no clang type system"); | ||
| clang::EnumDecl *enum_decl = ts->GetAsEnumDecl(clang_type); | ||
|
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 it possible to refactor this in a way where we don't directly manipulate the clang decls in swift language runtime? 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. Ideally we'd be able to move a lot of this function to the clang type system, since this is a clang type after all. |
||
| if (!enum_decl) | ||
| return llvm::createStringError("no enum decl"); | ||
| swift::Demangle::Demangler dem; | ||
| CompilerType raw_value = | ||
| CompilerType(ts, enum_decl->getIntegerType().getAsOpaquePtr()); | ||
|
|
||
| auto bit_size = | ||
| raw_value.GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | ||
| if (!bit_size) | ||
| return bit_size.takeError(); | ||
| ChildInfo child; | ||
| child.byte_size = *bit_size / 8; | ||
| child.byte_offset = 0; | ||
|
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 |
||
| child.bitfield_bit_size = 0; | ||
| child.bitfield_bit_offset = 0; | ||
| child.is_base_class = false; | ||
| child.is_deref_of_parent = false; | ||
| child.language_flags = 0; | ||
| return child; | ||
| }; | ||
| return visit_callback(clang_type, depth, get_name, get_info); | ||
| } | ||
| for (unsigned i = 0; i < n; ++i) | ||
| if (!visit_only || *visit_only == i) { | ||
| bool transparent_pointers = false; | ||
| bool ignore_array_bounds = false; | ||
| std::string child_name; | ||
| ChildInfo child; | ||
| auto child_type_or_err = clang_type.GetChildCompilerTypeAtIndex( | ||
| &m_exe_ctx, i, transparent_pointers, m_omit_empty_base_classes, | ||
| ignore_array_bounds, child_name, child.byte_size, child.byte_offset, | ||
| child.bitfield_bit_size, child.bitfield_bit_offset, | ||
| child.is_base_class, child.is_deref_of_parent, nullptr, | ||
| child.language_flags); | ||
| if (!child_type_or_err) | ||
| return child_type_or_err.takeError(); | ||
| CompilerType type = swiftify(*child_type_or_err); | ||
| auto get_name = [&]() -> std::string { return child_name; }; | ||
| auto get_info = [&]() -> llvm::Expected<ChildInfo> { return child; }; | ||
| if (auto err = visit_callback(type, depth, get_name, get_info)) | ||
| return err; | ||
| } | ||
| return success; | ||
| }; | ||
|
|
||
| { | ||
| CompilerType clang_type = m_runtime.LookupAnonymousClangType( | ||
| m_type.GetMangledTypeName().AsCString()); | ||
| if (!clang_type) | ||
| ts.IsImportedType(m_type.GetOpaqueQualType(), &clang_type); | ||
| if (clang_type) { | ||
| clang_type = GetTypedefedTypeRecursive(clang_type); | ||
| bool is_signed; | ||
| if (clang_type.IsEnumerationType(is_signed)) | ||
| return 1; | ||
| return clang_type.GetNumChildren(true, &m_exe_ctx); | ||
| auto result = visit_clang_type(clang_type); | ||
| if (result) | ||
| return result; | ||
| // FIXME: It would be nice not to fall through here. CFStringRef | ||
| // needs this path right now. | ||
| LLDB_LOG_ERRORV(GetLog(LLDBLog::Types), result.takeError(), | ||
| "could not resolve as clang type: {0}"); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1113,6 +1187,57 @@ SwiftRuntimeTypeVisitor::VisitImpl(std::optional<unsigned> visit_only, | |
| if (ts.IsBuiltinType(m_type)) | ||
| return 0; | ||
|
|
||
| // Resolve ObjC references via the ObjC runtime. | ||
| auto visit_objcclass = [&](const swift::reflection::ObjCClassTypeRef &tr) | ||
| -> llvm::Expected<unsigned> { | ||
| const std::string &name = tr.getName(); | ||
| auto *process = m_exe_ctx.GetProcessPtr(); | ||
| if (!process) | ||
| return llvm::createStringError( | ||
| "cannot resolve objc type without process"); | ||
| auto objc_runtime = SwiftLanguageRuntime::GetObjCRuntime(*process); | ||
| if (!process) | ||
|
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.
|
||
| return llvm::createStringError("no Objective-C runtime"); | ||
| AppleObjCRuntime::ObjCISA isa = objc_runtime->GetISA(ConstString(name)); | ||
| if (!isa) | ||
| return llvm::createStringError("no Objective-C class " + name); | ||
| AppleObjCRuntime::ClassDescriptorSP desc_sp = | ||
| objc_runtime->GetClassDescriptorFromISA(isa); | ||
| if (!desc_sp) | ||
| return llvm::createStringError("no class descriptor for " + name); | ||
| bool has_super = !m_hide_superclass && desc_sp->GetSuperclass(); | ||
| unsigned n = 0; | ||
| if (count_only) | ||
| return has_super ? n + 1 : n; | ||
|
|
||
| unsigned depth = 0; | ||
| if (has_super) | ||
| if (!visit_only || *visit_only == i) { | ||
| if (AppleObjCRuntime::ClassDescriptorSP superclass_desc_sp = | ||
| desc_sp->GetSuperclass()) { | ||
| auto get_name = [&]() -> std::string { | ||
| return superclass_desc_sp->GetClassName().GetString(); | ||
| }; | ||
| auto get_info = [&]() -> llvm::Expected<ChildInfo> { | ||
| return ChildInfo(); | ||
| }; | ||
| CompilerType type; | ||
| if (TypeSP type_sp = superclass_desc_sp->GetType()) | ||
| type = type_sp->GetForwardCompilerType(); | ||
| if (auto err = visit_callback(type, depth, get_name, get_info)) | ||
| return err; | ||
| if (visit_only) | ||
| return success; | ||
| } | ||
| } | ||
| // We could query the runtime for the children here but that | ||
| // would be inconsistent with SwiftASTContext. | ||
| return success; | ||
| }; | ||
| if (auto *obj_tr = | ||
| llvm::dyn_cast_or_null<swift::reflection::ObjCClassTypeRef>(tr)) | ||
| return visit_objcclass(*obj_tr); | ||
|
|
||
| LLDBTypeInfoProvider tip(m_runtime, ts); | ||
| auto cti_or_err = reflection_ctx->GetClassInstanceTypeInfo( | ||
| *tr, &tip, ts.GetDescriptorFinder()); | ||
|
|
@@ -1452,9 +1577,9 @@ SwiftLanguageRuntime::GetNumFields(CompilerType type, | |
|
|
||
| llvm::Expected<uint32_t> SwiftLanguageRuntime::GetNumChildren( | ||
| CompilerType type, ExecutionContextScope *exe_scope, | ||
| bool include_superclass, bool include_clang_types) { | ||
| bool include_superclass, bool omit_empty_base_classes) { | ||
| SwiftRuntimeTypeVisitor visitor(*this, type, exe_scope, !include_superclass, | ||
| include_clang_types); | ||
| omit_empty_base_classes); | ||
| return visitor.CountChildren(); | ||
| } | ||
|
|
||
|
|
@@ -1622,8 +1747,10 @@ SwiftLanguageRuntime::ProjectEnum(ValueObject &valobj) { | |
| std::pair<SwiftLanguageRuntime::LookupResult, std::optional<size_t>> | ||
| SwiftLanguageRuntime::GetIndexOfChildMemberWithName( | ||
| CompilerType type, llvm::StringRef name, ExecutionContext *exe_ctx, | ||
| bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) { | ||
| SwiftRuntimeTypeVisitor visitor(*this, type, exe_ctx, false, false, true); | ||
| bool omit_empty_base_classes, bool include_clang_types, | ||
| std::vector<uint32_t> &child_indexes) { | ||
| SwiftRuntimeTypeVisitor visitor(*this, type, exe_ctx, false, | ||
| include_clang_types, true); | ||
| bool found = false; | ||
| unsigned i = 0, last_depth = 0; | ||
| llvm::Error error = visitor.VisitAllChildren( | ||
|
|
@@ -1640,7 +1767,7 @@ SwiftLanguageRuntime::GetIndexOfChildMemberWithName( | |
| return info_or_err.takeError(); | ||
| // All enum children are index 0. | ||
| if (info_or_err->is_enum || name == get_child_name()) { | ||
| // The only access paths supperted are into base classes, | ||
| // The only access paths supported are into base classes, | ||
| // which are always at index 0. | ||
| for (unsigned j = 0; j < depth; ++j) | ||
| child_indexes.push_back(0); | ||
|
|
@@ -1670,7 +1797,7 @@ llvm::Expected<CompilerType> SwiftLanguageRuntime::GetChildCompilerTypeAtIndex( | |
| uint64_t &language_flags) { | ||
| CompilerType child_type; | ||
| bool found = false; | ||
| SwiftRuntimeTypeVisitor visitor(*this, type, valobj); | ||
| SwiftRuntimeTypeVisitor visitor(*this, type, valobj, omit_empty_base_classes); | ||
| llvm::Error error = visitor.VisitChildAtIndex( | ||
| idx, | ||
| [&](CompilerType type, unsigned depth, auto get_child_name, | ||
|
|
||
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.
Why does this change affect how data formatters work?