//===--- 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 // //===----------------------------------------------------------------------===// public enum _DebuggerSupport { @_versioned // FIXME(sil-serialize-all) internal enum CollectionStatus { case NotACollection case CollectionOfElements case CollectionOfPairs case Element case Pair case ElementOfPair @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal var isCollection: Bool { return self != .NotACollection } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal func getChildStatus(child: Mirror) -> CollectionStatus { let disposition = child.displayStyle ?? .struct 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 } } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func isClass(_ value: Any) -> Bool { if let _ = type(of: value) as? AnyClass { return true } return false } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func checkValue( _ value: Any, ifClass: (AnyObject) -> T, otherwise: () -> T ) -> T { if isClass(value) { return ifClass(_unsafeDowncastToAnyObject(fromAny: value)) } return otherwise() } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func asObjectIdentifier(_ value: Any) -> ObjectIdentifier? { return checkValue(value, ifClass: { return ObjectIdentifier($0) }, otherwise: { return nil }) } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func asNumericValue(_ value: Any) -> Int { return checkValue(value, ifClass: { return unsafeBitCast($0, to: Int.self) }, otherwise: { return 0 }) } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func asStringRepresentation( value: Any?, mirror: Mirror, count: Int ) -> String? { let ds = mirror.displayStyle ?? .`struct` switch ds { case .optional: if count > 0 { return "\(mirror.subjectType)" } else { if let x = value { return String(reflecting: x) } } case .collection: fallthrough case .dictionary: fallthrough case .set: fallthrough case .tuple: if count == 1 { return "1 element" } else { return "\(count) elements" } case .`struct`: fallthrough case .`enum`: if let x = value { if let cdsc = (x as? CustomDebugStringConvertible) { return cdsc.debugDescription } if let csc = (x as? CustomStringConvertible) { return csc.description } } if count > 0 { return "\(mirror.subjectType)" } case .`class`: if let x = value { if let cdsc = (x as? CustomDebugStringConvertible) { return cdsc.debugDescription } if let csc = (x as? CustomStringConvertible) { return csc.description } // for a Class with no custom summary, mimic the Foundation default return "<\(type(of: x)): 0x\(String(asNumericValue(x), radix: 16, uppercase: false))>" } else { // but if I can't provide a value, just use the type anyway return "\(mirror.subjectType)" } } if let x = value { return String(reflecting: x) } return nil } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func ivarCount(mirror: Mirror) -> Int { let count = Int(mirror.children.count) if let sc = mirror.superclassMirror { return ivarCount(mirror: sc) + count } else { return count } } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func shouldExpand( mirror: Mirror, collectionStatus: CollectionStatus, isRoot: Bool ) -> Bool { if isRoot || collectionStatus.isCollection { return true } let count = Int(mirror.children.count) if count > 0 { return true } if let sc = mirror.superclassMirror { return ivarCount(mirror: sc) > 0 } else { return true } } @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) internal static func printForDebuggerImpl( value: Any?, mirror: Mirror, name: String?, indent: Int, maxDepth: Int, isRoot: Bool, parentCollectionStatus: CollectionStatus, refsAlreadySeen: inout Set, maxItemCounter: inout Int, targetStream: inout StreamType ) { if maxItemCounter <= 0 { return } if !shouldExpand(mirror: mirror, collectionStatus: parentCollectionStatus, isRoot: isRoot) { return } maxItemCounter -= 1 for _ in 0.. 0 { print(" more", terminator: "", to: &targetStream) } if remainder == 1 { print(" child)", to: &targetStream) } else { print(" children)", to: &targetStream) } return } printForDebuggerImpl( value: child, mirror: Mirror(reflecting: child), name: childName, indent: indent + 2, maxDepth: maxDepth - 1, isRoot: false, parentCollectionStatus: collectionStatus, refsAlreadySeen: &refsAlreadySeen, maxItemCounter: &maxItemCounter, targetStream: &targetStream) printedElements += 1 } } // LLDB uses this function in expressions, and if it is inlined the resulting // LLVM IR is enormous. As a result, to improve LLDB performance we are not // making it @_inlineable. public static func stringForPrintObject(_ value: Any) -> String { var maxItemCounter = Int.max var refs = Set() var targetStream = "" printForDebuggerImpl( value: value, mirror: Mirror(reflecting: value), name: nil, indent: 0, maxDepth: maxItemCounter, isRoot: true, parentCollectionStatus: .NotACollection, refsAlreadySeen: &refs, maxItemCounter: &maxItemCounter, targetStream: &targetStream) return targetStream } }