mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
267 lines
8.0 KiB
Swift
267 lines
8.0 KiB
Swift
//===--- DebuggerSupport.swift --------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftShims
|
|
|
|
@_frozen // namespace
|
|
public enum _DebuggerSupport {
|
|
private enum CollectionStatus {
|
|
case notACollection
|
|
case collectionOfElements
|
|
case collectionOfPairs
|
|
case element
|
|
case pair
|
|
case elementOfPair
|
|
|
|
internal var isCollection: Bool {
|
|
return self != .notACollection
|
|
}
|
|
|
|
internal func getChildStatus(child: Mirror) -> CollectionStatus {
|
|
let disposition = child.displayStyle
|
|
|
|
if disposition == .collection { return .collectionOfElements }
|
|
if disposition == .dictionary { return .collectionOfPairs }
|
|
if disposition == .set { return .collectionOfElements }
|
|
|
|
if self == .collectionOfElements { return .element }
|
|
if self == .collectionOfPairs { return .pair }
|
|
if self == .pair { return .elementOfPair }
|
|
|
|
return .notACollection
|
|
}
|
|
}
|
|
|
|
private static func isClass(_ value: Any) -> Bool {
|
|
return type(of: value) is AnyClass
|
|
}
|
|
|
|
private static func checkValue<T>(
|
|
_ value: Any,
|
|
ifClass: (AnyObject) -> T,
|
|
otherwise: () -> T
|
|
) -> T {
|
|
if isClass(value) {
|
|
return ifClass(_unsafeDowncastToAnyObject(fromAny: value))
|
|
}
|
|
return otherwise()
|
|
}
|
|
|
|
private static func asObjectIdentifier(_ value: Any) -> ObjectIdentifier? {
|
|
return checkValue(value,
|
|
ifClass: { return ObjectIdentifier($0) },
|
|
otherwise: { return nil })
|
|
}
|
|
|
|
private static func asObjectAddress(_ value: Any) -> String {
|
|
let address = checkValue(value,
|
|
ifClass: { return unsafeBitCast($0, to: Int.self) },
|
|
otherwise: { return 0 })
|
|
return String(address, radix: 16, uppercase: false)
|
|
}
|
|
|
|
private static func asStringRepresentation(
|
|
value: Any?,
|
|
mirror: Mirror,
|
|
count: Int
|
|
) -> String? {
|
|
switch mirror.displayStyle {
|
|
case .optional? where count > 0:
|
|
return "\(mirror.subjectType)"
|
|
case .optional?:
|
|
return value.map(String.init(reflecting:))
|
|
case .collection?, .dictionary?, .set?, .tuple?:
|
|
return count == 1 ? "1 element" : "\(count) elements"
|
|
case .`struct`?, .`enum`?, nil:
|
|
switch value {
|
|
case let x as CustomDebugStringConvertible:
|
|
return x.debugDescription
|
|
case let x as CustomStringConvertible:
|
|
return x.description
|
|
case _ where count > 0:
|
|
return "\(mirror.subjectType)"
|
|
default:
|
|
return value.map(String.init(reflecting:))
|
|
}
|
|
case .`class`?:
|
|
switch value {
|
|
case let x as CustomDebugStringConvertible:
|
|
return x.debugDescription
|
|
case let x as CustomStringConvertible:
|
|
return x.description
|
|
case let x?:
|
|
// for a Class with no custom summary, mimic the Foundation default
|
|
return "<\(type(of: x)): 0x\(asObjectAddress(x))>"
|
|
default:
|
|
// but if I can't provide a value, just use the type anyway
|
|
return "\(mirror.subjectType)"
|
|
}
|
|
}
|
|
}
|
|
|
|
private static func ivarCount(mirror: Mirror) -> Int {
|
|
let ivars = mirror.superclassMirror.map(ivarCount) ?? 0
|
|
return ivars + mirror.children.count
|
|
}
|
|
|
|
private static func shouldExpand(
|
|
mirror: Mirror,
|
|
collectionStatus: CollectionStatus,
|
|
isRoot: Bool
|
|
) -> Bool {
|
|
if isRoot || collectionStatus.isCollection { return true }
|
|
if mirror.children.count > 0 { return true }
|
|
if mirror.displayStyle == .`class` { return true }
|
|
if let sc = mirror.superclassMirror { return ivarCount(mirror: sc) > 0 }
|
|
return true
|
|
}
|
|
|
|
private static func printForDebuggerImpl<StreamType : TextOutputStream>(
|
|
value: Any?,
|
|
mirror: Mirror,
|
|
name: String?,
|
|
indent: Int,
|
|
maxDepth: Int,
|
|
isRoot: Bool,
|
|
parentCollectionStatus: CollectionStatus,
|
|
refsAlreadySeen: inout Set<ObjectIdentifier>,
|
|
maxItemCounter: inout Int,
|
|
target: inout StreamType
|
|
) {
|
|
guard maxItemCounter > 0 else { return }
|
|
|
|
guard shouldExpand(mirror: mirror,
|
|
collectionStatus: parentCollectionStatus,
|
|
isRoot: isRoot)
|
|
else { return }
|
|
|
|
maxItemCounter -= 1
|
|
|
|
print(String(repeating: " ", count: indent), terminator: "", to: &target)
|
|
|
|
// do not expand classes with no custom Mirror
|
|
// yes, a type can lie and say it's a class when it's not since we only
|
|
// check the displayStyle - but then the type would have a custom Mirror
|
|
// anyway, so there's that...
|
|
let willExpand = mirror.displayStyle != .`class` || value is CustomReflectable?
|
|
|
|
let count = mirror.children.count
|
|
let bullet = isRoot && (count == 0 || !willExpand) ? ""
|
|
: count == 0 ? "- "
|
|
: maxDepth <= 0 ? "▹ " : "▿ "
|
|
print(bullet, terminator: "", to: &target)
|
|
|
|
let collectionStatus = parentCollectionStatus.getChildStatus(child: mirror)
|
|
|
|
if let name = name {
|
|
print("\(name) : ", terminator: "", to: &target)
|
|
}
|
|
|
|
if let str = asStringRepresentation(value: value, mirror: mirror, count: count) {
|
|
print(str, terminator: "", to: &target)
|
|
}
|
|
|
|
if (maxDepth <= 0) || !willExpand {
|
|
print("", to: &target)
|
|
return
|
|
}
|
|
|
|
if let valueIdentifier = value.flatMap(asObjectIdentifier) {
|
|
if refsAlreadySeen.contains(valueIdentifier) {
|
|
print(" { ... }", to: &target)
|
|
return
|
|
} else {
|
|
refsAlreadySeen.insert(valueIdentifier)
|
|
}
|
|
}
|
|
|
|
print("", to: &target)
|
|
|
|
var printedElements = 0
|
|
|
|
if let sc = mirror.superclassMirror {
|
|
printForDebuggerImpl(
|
|
value: nil,
|
|
mirror: sc,
|
|
name: "super",
|
|
indent: indent + 2,
|
|
maxDepth: maxDepth - 1,
|
|
isRoot: false,
|
|
parentCollectionStatus: .notACollection,
|
|
refsAlreadySeen: &refsAlreadySeen,
|
|
maxItemCounter: &maxItemCounter,
|
|
target: &target)
|
|
}
|
|
|
|
for (optionalName,child) in mirror.children {
|
|
let childName = optionalName ?? "\(printedElements)"
|
|
if maxItemCounter <= 0 {
|
|
print(String(repeating: " ", count: indent+4), terminator: "", to: &target)
|
|
let remainder = count - printedElements
|
|
print("(\(remainder)", terminator: "", to: &target)
|
|
if printedElements > 0 {
|
|
print(" more", terminator: "", to: &target)
|
|
}
|
|
print(remainder == 1 ? " child)" : " children)", to: &target)
|
|
return
|
|
}
|
|
|
|
printForDebuggerImpl(
|
|
value: child,
|
|
mirror: Mirror(reflecting: child),
|
|
name: childName,
|
|
indent: indent + 2,
|
|
maxDepth: maxDepth - 1,
|
|
isRoot: false,
|
|
parentCollectionStatus: collectionStatus,
|
|
refsAlreadySeen: &refsAlreadySeen,
|
|
maxItemCounter: &maxItemCounter,
|
|
target: &target)
|
|
printedElements += 1
|
|
}
|
|
}
|
|
|
|
public static func stringForPrintObject(_ value: Any) -> String {
|
|
var maxItemCounter = Int.max
|
|
var refs = Set<ObjectIdentifier>()
|
|
var target = ""
|
|
|
|
printForDebuggerImpl(
|
|
value: value,
|
|
mirror: Mirror(reflecting: value),
|
|
name: nil,
|
|
indent: 0,
|
|
maxDepth: maxItemCounter,
|
|
isRoot: true,
|
|
parentCollectionStatus: .notACollection,
|
|
refsAlreadySeen: &refs,
|
|
maxItemCounter: &maxItemCounter,
|
|
target: &target)
|
|
|
|
return target
|
|
}
|
|
}
|
|
|
|
public func _stringForPrintObject(_ value: Any) -> String {
|
|
return _DebuggerSupport.stringForPrintObject(value)
|
|
}
|
|
|
|
public func _debuggerTestingCheckExpect(_: String, _: String) { }
|
|
|
|
// Utilities to get refcount(s) of class objects.
|
|
@_silgen_name("swift_retainCount")
|
|
public func _getRetainCount(_ Value: AnyObject) -> UInt
|
|
@_silgen_name("swift_unownedRetainCount")
|
|
public func _getUnownedRetainCount(_ Value : AnyObject) -> UInt
|
|
@_silgen_name("swift_weakRetainCount")
|
|
public func _getWeakRetainCount(_ Value : AnyObject) -> UInt
|