Skip to content

[lldb] Add formatters for Swift.Span #10905

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,12 @@ static void LoadSwiftFormatters(lldb::TypeCategoryImplSP swift_category_sp) {
ConstString("^Swift.Unsafe(Mutable)?(Raw)?(Buffer)?Pointer(<.+>)?$"),
summary_flags, true);

AddCXXSummary(swift_category_sp,
lldb_private::formatters::swift::UnsafeTypeSummaryProvider,
"Swift.[Mutable]Span",
ConstString("^Swift\\.(Mutable)?Span<.+>$"), summary_flags,
true);

DictionaryConfig::Get().RegisterSummaryProviders(swift_category_sp,
summary_flags);
SetConfig::Get().RegisterSummaryProviders(swift_category_sp, summary_flags);
Expand Down Expand Up @@ -395,6 +401,12 @@ static void LoadSwiftFormatters(lldb::TypeCategoryImplSP swift_category_sp) {
ConstString("^Swift.Unsafe(Mutable)?(Raw)?(Buffer)?Pointer(<.+>)?$"),
synth_flags, true);

AddCXXSynthetic(
swift_category_sp,
lldb_private::formatters::swift::UnsafeTypeSyntheticFrontEndCreator,
"Swift.[Mutable]Span", ConstString("^Swift\\.(Mutable)?Span<.+>?"),
synth_flags, true);

DictionaryConfig::Get().RegisterSyntheticChildrenCreators(swift_category_sp,
synth_flags);
SetConfig::Get().RegisterSyntheticChildrenCreators(swift_category_sp,
Expand Down
178 changes: 112 additions & 66 deletions lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h"
#include "lldb/DataFormatters/TypeSynthetic.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-enumerations.h"
#include "llvm/ADT/StringRef.h"

#include <utility>

Expand Down Expand Up @@ -38,6 +41,8 @@ class SwiftUnsafeType {
protected:
SwiftUnsafeType(ValueObject &valobj, UnsafePointerKind kind);
addr_t GetAddress(llvm::StringRef child_name);
std::optional<size_t> GetCountValue(llvm::StringRef child_name);
CompilerType GetArgumentType();

ValueObject &m_valobj;
const UnsafePointerKind m_kind;
Expand Down Expand Up @@ -133,6 +138,70 @@ lldb::addr_t SwiftUnsafeType::GetAddress(llvm::StringRef child_name) {
return pointer_value_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
}

std::optional<size_t>
SwiftUnsafeType::GetCountValue(llvm::StringRef child_name) {
ValueObjectSP count_value_sp(
m_valobj.GetChildMemberWithName(child_name, true));
if (!count_value_sp) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't find ValueObject child member named '{1}'.",
__FUNCTION__, child_name);
return std::nullopt;
}

ValueObjectSP value_provided_child_sp;

// Implement Swift's 'value-providing synthetic children' workaround.
// Depending on whether the ValueObject type is a primitive or a structure,
// lldb should prioritize the synthetic value children.
// If it has no synthetic children then fallback to non synthetic children.
ValueObjectSP synthetic = count_value_sp->GetSyntheticValue();
if (synthetic)
value_provided_child_sp = synthetic->GetChildAtIndex(0, true);
if (!value_provided_child_sp)
value_provided_child_sp = count_value_sp->GetChildAtIndex(0, true);

// If neither child exists, fail.
if (!value_provided_child_sp) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't extract 'value-providing synthetic children' from "
"ValueObject '{1}'.",
__FUNCTION__, child_name);
return std::nullopt;
}

size_t count = value_provided_child_sp->GetValueAsUnsigned(UINT64_MAX);

if (count == UINT64_MAX) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get a valid value for ValueObject '{1}'.",
__FUNCTION__, child_name);
return std::nullopt;
}

return count;
}
Comment on lines +141 to +183
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extracted function, not new code.


CompilerType SwiftUnsafeType::GetArgumentType() {
CompilerType type = m_valobj.GetCompilerType();
if (!type.IsValid()) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get the compiler type for the '{1}' ValueObject.",
__FUNCTION__, type.GetTypeName());
return {};
}

auto type_system = type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>();
if (!type_system) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get {1} type system.", __FUNCTION__,
type.GetTypeName());
return {};
}

return type_system->GetGenericArgumentType(type.GetOpaqueQualType(), 0);
}
Comment on lines +185 to +203
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extracted function, not new code.


class SwiftUnsafeBufferPointer final : public SwiftUnsafeType {
public:
SwiftUnsafeBufferPointer(ValueObject &valobj);
Expand Down Expand Up @@ -162,46 +231,10 @@ lldb::ChildCacheState SwiftUnsafeBufferPointer::Update() {
// pointer address, lldb unfolds every ValueObject child until reaching
// `pointerValue`.

static ConstString g_count("count");
ValueObjectSP count_value_sp(m_valobj.GetChildMemberWithName(g_count, true));
if (!count_value_sp) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't find ValueObject child member named '{1}'.",
__FUNCTION__, g_count);
return ChildCacheState::eRefetch;
}

ValueObjectSP value_provided_child_sp = nullptr;

// Implement Swift's 'value-providing synthetic children' workaround.
// Depending on whether the ValueObject type is a primitive or a structure,
// lldb should prioritize the synthetic value children.
// If it has no synthetic children then fallback to non synthetic children.
ValueObjectSP synthetic = count_value_sp->GetSyntheticValue();
if (synthetic)
value_provided_child_sp = synthetic->GetChildAtIndex(0, true);
if (!value_provided_child_sp)
value_provided_child_sp = count_value_sp->GetChildAtIndex(0, true);

// If neither child exists, fail.
if (!value_provided_child_sp) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't extract 'value-providing synthetic children' from "
"ValueObject 'count'.",
__FUNCTION__);
return lldb::ChildCacheState::eRefetch;
}

size_t count = value_provided_child_sp->GetValueAsUnsigned(UINT64_MAX);

if (count == UINT64_MAX) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get a valid value for ValueObject 'count'.",
__FUNCTION__);
if (auto count = GetCountValue("count"))
m_count = *count;
else
return ChildCacheState::eRefetch;
}

m_count = count;

addr_t start_addr = GetAddress("_position");

Expand Down Expand Up @@ -332,35 +365,10 @@ lldb::ChildCacheState SwiftUnsafePointer::Update() {
// - pointerValue : Int
//

CompilerType type = m_valobj.GetCompilerType();
if (!type.IsValid()) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get the compiler type for the "
"'Swift.UnsafePointer' ValueObject.",
__FUNCTION__, type.GetTypeName());
return ChildCacheState::eRefetch;
}

auto type_system = type.GetTypeSystem().dyn_cast_or_null<TypeSystemSwift>();
if (!type_system) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get {1} type system.", __FUNCTION__,
type.GetTypeName());
return ChildCacheState::eRefetch;
}

CompilerType argument_type =
type_system->GetGenericArgumentType(type.GetOpaqueQualType(), 0);

CompilerType argument_type = GetArgumentType();
if (argument_type.IsValid())
m_elem_type = argument_type;

if (type.GetTypeInfo() & eTypeIsEnumeration) {
CompilerType argument_type =
type_system->GetGenericArgumentType(type.GetOpaqueQualType(), 0);
if (argument_type.IsValid())
m_elem_type = argument_type;
}
assert(
!m_elem_type.GetTypeName().GetStringRef().starts_with("Swift.Optional"));

Expand All @@ -384,6 +392,40 @@ lldb::ChildCacheState SwiftUnsafePointer::Update() {
return ChildCacheState::eReuse;
}

class SwiftSpan final : public SwiftUnsafeType {
public:
SwiftSpan(ValueObject &valobj);
lldb::ChildCacheState Update() override;
};

SwiftSpan::SwiftSpan(ValueObject &valobj)
: SwiftUnsafeType(valobj, UnsafePointerKind::eSwiftUnsafeRawBufferPointer) {
}

lldb::ChildCacheState SwiftSpan::Update() {
if (auto count = GetCountValue("_count"))
m_count = *count;
else
return ChildCacheState::eRefetch;

addr_t start_addr = GetAddress("_pointer");
if (!start_addr || start_addr == LLDB_INVALID_ADDRESS) {
LLDB_LOG(GetLog(LLDBLog::DataFormatters),
"{0}: Couldn't get a valid address for ValueObject '_pointer'.",
__FUNCTION__);
return ChildCacheState::eRefetch;
}
m_start_addr = start_addr;

CompilerType argument_type = GetArgumentType();
if (argument_type.IsValid())
m_elem_type = argument_type;
else
return ChildCacheState::eRefetch;

return ChildCacheState::eReuse;
}

std::unique_ptr<SwiftUnsafeType> SwiftUnsafeType::Create(ValueObject &valobj) {
CompilerType type = valobj.GetCompilerType();
if (!type.IsValid()) {
Expand Down Expand Up @@ -424,8 +466,12 @@ std::unique_ptr<SwiftUnsafeType> SwiftUnsafeType::Create(ValueObject &valobj) {

llvm::StringRef valobj_type_name(type.GetTypeName().GetCString());
valobj_type_name.consume_front("Swift.");
valobj_type_name.consume_front("Unsafe");
bool is_unsafe = valobj_type_name.consume_front("Unsafe");
valobj_type_name.consume_front("Mutable");

if (!is_unsafe && valobj_type_name.consume_front("Span"))
return std::make_unique<SwiftSpan>(valobj);

bool is_raw = valobj_type_name.consume_front("Raw");
bool is_buffer_ptr = valobj_type_name.consume_front("Buffer");
UnsafePointerKind kind =
Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/span/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SWIFT_SOURCES := main.swift
SWIFTFLAGS_EXTRAS := -parse-as-library -Xfrontend -disable-availability-checking
include Makefile.rules
18 changes: 18 additions & 0 deletions lldb/test/API/lang/swift/span/TestSwiftSpan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class TestCase(TestBase):

@swiftTest
def test(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.swift")
)

self.expect("frame var ints_span", substrs=["[0] = 6", "[1] = 7"])
self.expect("frame var strings_span", substrs=['[0] = "six"', '[1] = "seven"'])
self.expect("frame var things_span", substrs=["[0] = (id = 67, odd = true)"])
21 changes: 21 additions & 0 deletions lldb/test/API/lang/swift/span/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
struct Thing {
var id: Int
var odd: Bool

init(_ n: Int) {
id = n
odd = n % 2 == 1
}
}

@main struct Entry {
static func main() {
let ints = [6, 7]
let ints_span = ints.span
let strings = ["six", "seven"]
let strings_span = strings.span
let things = [Thing(67)]
let things_span = things.span
print("break here")
}
}