Skip to content

Commit 57d8231

Browse files
authored
Add CustomDebugDescription conformance to AnyKeyPath (#60133)
* initial * it works demangling mostly works fix dots printing works add tests add conformance to AnyKeyPath implement SPI subscripts fully work comments use cross platform image inspection remove unnecessary comment fix fix issues add conditional conformance add types try to fix the api-digester test cr feedback: move impls behind flag, remove addChain(), switch statement, fallthrough instead of if-elses, move import cr feedback: refactor switch statement fix #ifdef reindent, cr feedback: removes manual memory management fix missing whitespace fix typo fix indentation issues switch to regexes checks should test in on all platforms print types in subscripts add test for empty subscript Update test/api-digester/stability-stdlib-abi-without-asserts.test Co-authored-by: Xiaodi Wu <[email protected]> add commas fix failing test fix stdlib annotation cr feedback: remove global, refactor ifdef cr feedback: switch back to manual memory management switch to 5.8 macro add new weakly linked functions to the allowlist fix one more failing test more cr feedback more cr feedback * fix invisible unicode
1 parent 5ffe847 commit 57d8231

File tree

7 files changed

+350
-10
lines changed

7 files changed

+350
-10
lines changed

include/swift/Demangling/Demangle.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,11 @@ ManglingErrorOr<const char *> mangleNodeAsObjcCString(NodePointer node,
649649
std::string nodeToString(NodePointer Root,
650650
const DemangleOptions &Options = DemangleOptions());
651651

652+
/// Transforms a mangled key path accessor thunk helper
653+
/// into the identfier/subscript that would be used to invoke it in swift code.
654+
std::string keyPathSourceString(const char *MangledName,
655+
size_t MangledNameLength);
656+
652657
/// A class for printing to a std::string.
653658
class DemanglerPrinter {
654659
public:

lib/Demangling/NodePrinter.cpp

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "swift/AST/Ownership.h"
1718
#include "swift/Basic/STLExtras.h"
1819
#include "swift/Demangling/Demangle.h"
19-
#include "swift/AST/Ownership.h"
2020
#include "swift/Strings.h"
2121
#include <cassert>
2222
#include <cstdio>
2323
#include <cstdlib>
24+
#include <vector>
2425

2526
using namespace swift;
2627
using namespace Demangle;
@@ -3211,6 +3212,146 @@ void NodePrinter::printEntityType(NodePointer Entity, NodePointer type,
32113212
}
32123213
}
32133214

3215+
NodePointer
3216+
matchSequenceOfKinds(NodePointer start,
3217+
std::vector<std::tuple<Node::Kind, size_t>> pattern) {
3218+
if (start != nullptr) {
3219+
NodePointer current = start;
3220+
size_t idx = 0;
3221+
while (idx < pattern.size()) {
3222+
std::tuple<Node::Kind, size_t> next = pattern[idx];
3223+
idx += 1;
3224+
NodePointer nextChild = current->getChild(std::get<1>(next));
3225+
if (nextChild != nullptr && nextChild->getKind() == std::get<0>(next)) {
3226+
current = nextChild;
3227+
} else {
3228+
return nullptr;
3229+
}
3230+
}
3231+
if (idx == pattern.size()) {
3232+
return current;
3233+
} else {
3234+
return nullptr;
3235+
}
3236+
} else {
3237+
return nullptr;
3238+
}
3239+
}
3240+
3241+
std::string Demangle::keyPathSourceString(const char *MangledName,
3242+
size_t MangledNameLength) {
3243+
std::string invalid = "";
3244+
std::string unlabelledArg = "_: ";
3245+
Context ctx;
3246+
NodePointer root =
3247+
ctx.demangleSymbolAsNode(StringRef(MangledName, MangledNameLength));
3248+
if (!root)
3249+
return invalid;
3250+
if (root->getNumChildren() >= 1) {
3251+
NodePointer firstChild = root->getChild(0);
3252+
if (firstChild->getKind() == Node::Kind::KeyPathGetterThunkHelper) {
3253+
NodePointer child = firstChild->getChild(0);
3254+
switch (child->getKind()) {
3255+
case Node::Kind::Subscript: {
3256+
std::string subscriptText = "subscript(";
3257+
std::vector<std::string> argumentTypeNames;
3258+
// Multiple arguments case
3259+
NodePointer argList = matchSequenceOfKinds(
3260+
child, {
3261+
std::make_pair(Node::Kind::Type, 2),
3262+
std::make_pair(Node::Kind::FunctionType, 0),
3263+
std::make_pair(Node::Kind::ArgumentTuple, 0),
3264+
std::make_pair(Node::Kind::Type, 0),
3265+
std::make_pair(Node::Kind::Tuple, 0),
3266+
});
3267+
if (argList != nullptr) {
3268+
size_t numArgumentTypes = argList->getNumChildren();
3269+
size_t idx = 0;
3270+
while (idx < numArgumentTypes) {
3271+
NodePointer argumentType = argList->getChild(idx);
3272+
idx += 1;
3273+
if (argumentType->getKind() == Node::Kind::TupleElement) {
3274+
argumentType =
3275+
argumentType->getChild(0)->getChild(0)->getChild(1);
3276+
if (argumentType->getKind() == Node::Kind::Identifier) {
3277+
argumentTypeNames.push_back(
3278+
std::string(argumentType->getText()));
3279+
continue;
3280+
}
3281+
}
3282+
argumentTypeNames.push_back("<Unknown>");
3283+
}
3284+
} else {
3285+
// Case where there is a single argument
3286+
argList = matchSequenceOfKinds(
3287+
child, {
3288+
std::make_pair(Node::Kind::Type, 2),
3289+
std::make_pair(Node::Kind::FunctionType, 0),
3290+
std::make_pair(Node::Kind::ArgumentTuple, 0),
3291+
std::make_pair(Node::Kind::Type, 0),
3292+
});
3293+
if (argList != nullptr) {
3294+
argumentTypeNames.push_back(
3295+
std::string(argList->getChild(0)->getChild(1)->getText()));
3296+
}
3297+
}
3298+
child = child->getChild(1);
3299+
size_t idx = 0;
3300+
// There is an argument label:
3301+
if (child != nullptr) {
3302+
if (child->getKind() == Node::Kind::LabelList) {
3303+
size_t numChildren = child->getNumChildren();
3304+
if (numChildren == 0) {
3305+
subscriptText += unlabelledArg + argumentTypeNames[0];
3306+
} else {
3307+
while (idx < numChildren) {
3308+
Node *argChild = child->getChild(idx);
3309+
idx += 1;
3310+
if (argChild->getKind() == Node::Kind::Identifier) {
3311+
subscriptText += std::string(argChild->getText()) + ": " +
3312+
argumentTypeNames[idx - 1];
3313+
if (idx != numChildren) {
3314+
subscriptText += ", ";
3315+
}
3316+
} else if (argChild->getKind() ==
3317+
Node::Kind::FirstElementMarker ||
3318+
argChild->getKind() == Node::Kind::VariadicMarker) {
3319+
subscriptText += unlabelledArg + argumentTypeNames[idx - 1];
3320+
}
3321+
}
3322+
}
3323+
}
3324+
} else {
3325+
subscriptText += unlabelledArg + argumentTypeNames[0];
3326+
}
3327+
return subscriptText + ")";
3328+
}
3329+
case Node::Kind::Variable: {
3330+
child = child->getChild(1);
3331+
if (child == nullptr) {
3332+
return invalid;
3333+
}
3334+
if (child->getKind() == Node::Kind::PrivateDeclName) {
3335+
child = child->getChild(1);
3336+
if (child == nullptr) {
3337+
return invalid;
3338+
}
3339+
if (child->getKind() == Node::Kind::Identifier) {
3340+
return std::string(child->getText());
3341+
}
3342+
} else if (child->getKind() == Node::Kind::Identifier) {
3343+
return std::string(child->getText());
3344+
}
3345+
break;
3346+
}
3347+
default:
3348+
return invalid;
3349+
}
3350+
}
3351+
}
3352+
return invalid;
3353+
}
3354+
32143355
std::string Demangle::nodeToString(NodePointer root,
32153356
const DemangleOptions &options) {
32163357
if (!root)

stdlib/public/core/KeyPath.swift

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3715,3 +3715,121 @@ internal func _instantiateKeyPathBuffer(
37153715
}
37163716
}
37173717

3718+
#if SWIFT_ENABLE_REFLECTION
3719+
3720+
@_silgen_name("swift_keyPath_dladdr")
3721+
fileprivate func keypath_dladdr(_: UnsafeRawPointer) -> UnsafePointer<CChar>?
3722+
3723+
@_silgen_name("swift_keyPathSourceString")
3724+
fileprivate func demangle(
3725+
name: UnsafePointer<CChar>
3726+
) -> UnsafeMutablePointer<CChar>?
3727+
3728+
fileprivate func dynamicLibraryAddress<Base, Leaf>(
3729+
of pointer: ComputedAccessorsPtr,
3730+
_: Base.Type,
3731+
_ leaf: Leaf.Type
3732+
) -> String {
3733+
let getter: ComputedAccessorsPtr.Getter<Base, Leaf> = pointer.getter()
3734+
let pointer = unsafeBitCast(getter, to: UnsafeRawPointer.self)
3735+
if let cString = keypath_dladdr(UnsafeRawPointer(pointer)) {
3736+
if let demangled = demangle(name: cString)
3737+
.map({ pointer in
3738+
defer {
3739+
pointer.deallocate()
3740+
}
3741+
return String(cString: pointer)
3742+
}) {
3743+
return demangled
3744+
}
3745+
}
3746+
return "<computed \(pointer) (\(leaf))>"
3747+
}
3748+
3749+
#endif
3750+
3751+
@available(SwiftStdlib 5.8, *)
3752+
extension AnyKeyPath: CustomDebugStringConvertible {
3753+
3754+
#if SWIFT_ENABLE_REFLECTION
3755+
@available(SwiftStdlib 5.8, *)
3756+
public var debugDescription: String {
3757+
var description = "\\\(String(describing: Self.rootType))"
3758+
return withBuffer {
3759+
var buffer = $0
3760+
if buffer.data.isEmpty {
3761+
_internalInvariantFailure("key path has no components")
3762+
}
3763+
var valueType: Any.Type = Self.rootType
3764+
while true {
3765+
let (rawComponent, optNextType) = buffer.next()
3766+
let hasEnded = optNextType == nil
3767+
let nextType = optNextType ?? Self.valueType
3768+
switch rawComponent.value {
3769+
case .optionalForce, .optionalWrap, .optionalChain:
3770+
break
3771+
default:
3772+
description.append(".")
3773+
}
3774+
switch rawComponent.value {
3775+
case .class(let offset),
3776+
.struct(let offset):
3777+
let count = _getRecursiveChildCount(valueType)
3778+
let index = (0..<count)
3779+
.first(where: { i in
3780+
_getChildOffset(
3781+
valueType,
3782+
index: i
3783+
) == offset
3784+
})
3785+
if let index = index {
3786+
var field = _FieldReflectionMetadata()
3787+
_ = _getChildMetadata(
3788+
valueType,
3789+
index: index,
3790+
fieldMetadata: &field
3791+
)
3792+
defer {
3793+
field.freeFunc?(field.name)
3794+
}
3795+
description.append(String(cString: field.name))
3796+
} else {
3797+
description.append("<offset \(offset) (\(nextType))>")
3798+
}
3799+
case .get(_, let accessors, _),
3800+
.nonmutatingGetSet(_, let accessors, _),
3801+
.mutatingGetSet(_, let accessors, _):
3802+
func project<Base>(base: Base.Type) -> String {
3803+
func project2<Leaf>(leaf: Leaf.Type) -> String {
3804+
dynamicLibraryAddress(
3805+
of: accessors,
3806+
base,
3807+
leaf
3808+
)
3809+
}
3810+
return _openExistential(nextType, do: project2)
3811+
}
3812+
description.append(
3813+
_openExistential(valueType, do: project)
3814+
)
3815+
case .optionalChain, .optionalWrap:
3816+
description.append("?")
3817+
case .optionalForce:
3818+
description.append("!")
3819+
}
3820+
if hasEnded {
3821+
break
3822+
}
3823+
valueType = nextType
3824+
}
3825+
return description
3826+
}
3827+
}
3828+
#else
3829+
@available(SwiftStdlib 5.8, *)
3830+
public var debugDescription: String {
3831+
"(value cannot be printed without reflection)"
3832+
}
3833+
#endif
3834+
3835+
}

stdlib/public/runtime/ReflectionMirror.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@
1212

1313
#ifdef SWIFT_ENABLE_REFLECTION
1414

15+
#include "../SwiftShims/Reflection.h"
16+
#include "ImageInspection.h"
17+
#include "Private.h"
18+
#include "WeakReference.h"
1519
#include "swift/Basic/Lazy.h"
16-
#include "swift/Runtime/Reflection.h"
20+
#include "swift/Basic/Unreachable.h"
21+
#include "swift/Demangling/Demangle.h"
1722
#include "swift/Runtime/Casting.h"
1823
#include "swift/Runtime/Config.h"
24+
#include "swift/Runtime/Debug.h"
25+
#include "swift/Runtime/Enum.h"
1926
#include "swift/Runtime/HeapObject.h"
2027
#include "swift/Runtime/Metadata.h"
21-
#include "swift/Runtime/Enum.h"
22-
#include "swift/Basic/Unreachable.h"
23-
#include "swift/Demangling/Demangle.h"
24-
#include "swift/Runtime/Debug.h"
2528
#include "swift/Runtime/Portability.h"
26-
#include "Private.h"
27-
#include "WeakReference.h"
28-
#include "../SwiftShims/Reflection.h"
29+
#include "swift/Runtime/Reflection.h"
2930
#include <cassert>
3031
#include <cinttypes>
3132
#include <cstdio>
@@ -1126,4 +1127,26 @@ id swift_reflectionMirror_quickLookObject(OpaqueValue *value, const Metadata *T)
11261127
}
11271128
#endif
11281129

1130+
SWIFT_CC(swift)
1131+
SWIFT_RUNTIME_STDLIB_INTERNAL const char *swift_keyPath_dladdr(void *address) {
1132+
SymbolInfo info;
1133+
if (lookupSymbol(address, &info) == 0) {
1134+
return 0;
1135+
} else {
1136+
return info.symbolName.get();
1137+
}
1138+
}
1139+
1140+
SWIFT_CC(swift)
1141+
SWIFT_RUNTIME_STDLIB_INTERNAL const
1142+
char *swift_keyPathSourceString(char *name) {
1143+
size_t length = strlen(name);
1144+
std::string mangledName = keyPathSourceString(name, length);
1145+
if (mangledName == "") {
1146+
return 0;
1147+
} else {
1148+
return strdup(mangledName.c_str());
1149+
}
1150+
}
1151+
11291152
#endif // SWIFT_ENABLE_REFLECTION

0 commit comments

Comments
 (0)