Skip to content

Commit 43284f7

Browse files
author
Enrico Granata
committed
Initial implementation of PrintForDebugger as part of the Swift standard library
1 parent c5bbfa2 commit 43284f7

File tree

4 files changed

+350
-1
lines changed

4 files changed

+350
-1
lines changed

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ set(SWIFTLIB_ESSENTIAL
7878
Pointer.swift
7979
Policy.swift
8080
Print.swift
81+
PrintForDebugger.swift
8182
RandomAccessCollection.swift
8283
Range.swift.gyb
8384
RangeReplaceableCollection.swift.gyb

stdlib/public/core/GroupInfo.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
"Process.swift",
142142
"Tuple.swift",
143143
"NewtypeWrapper.swift",
144-
"UnsafeBitMap.swift"
144+
"UnsafeBitMap.swift",
145+
"PrintForDebugger.swift"
145146
]
146147
}
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
//===--- PrintForDebugger.swift -------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public enum _PrintForDebugger {
14+
15+
enum CollectionStatus {
16+
case NotACollection
17+
case CollectionOfElements
18+
case CollectionOfPairs
19+
case Element
20+
case Pair
21+
case ElementOfPair
22+
23+
var isCollection: Bool {
24+
get {
25+
return self != .NotACollection
26+
}
27+
}
28+
29+
func getChildStatus(child: Mirror) -> CollectionStatus {
30+
let disposition = child.displayStyle ?? .struct
31+
32+
if disposition == .collection { return .CollectionOfElements }
33+
if disposition == .dictionary { return .CollectionOfPairs }
34+
if disposition == .set { return .CollectionOfElements }
35+
36+
if self == .CollectionOfElements { return .Element }
37+
if self == .CollectionOfPairs { return .Pair }
38+
if self == .Pair { return .ElementOfPair }
39+
40+
return .NotACollection
41+
}
42+
}
43+
44+
static func asObjectIdentifier(value: Any) -> ObjectIdentifier? {
45+
if let ao = value as? AnyObject {
46+
return ObjectIdentifier(ao)
47+
} else {
48+
return nil
49+
}
50+
}
51+
52+
static func asNumericValue(value: Any) -> Int {
53+
if let ao = value as? AnyObject {
54+
return unsafeBitCast(ao, to: Int.self)
55+
} else {
56+
return 0
57+
}
58+
}
59+
60+
static func asStringRepresentation(value: Any?,
61+
mirror: Mirror,
62+
count: Int) -> String? {
63+
let ds = mirror.displayStyle ?? .`struct`
64+
switch ds {
65+
case .optional:
66+
if count > 0 {
67+
return "\(mirror.subjectType)"
68+
}
69+
else {
70+
if let x = value {
71+
return String(reflecting: x)
72+
}
73+
}
74+
case .collection:
75+
fallthrough
76+
case .dictionary:
77+
fallthrough
78+
case .set:
79+
fallthrough
80+
case .tuple:
81+
return "\(Int(mirror.children.count)) elements"
82+
case .`struct`:
83+
fallthrough
84+
case .`enum`:
85+
if let x = value {
86+
if let cdsc = (x as? CustomDebugStringConvertible) {
87+
return cdsc.debugDescription
88+
}
89+
if let csc = (x as? CustomStringConvertible) {
90+
return csc.description
91+
}
92+
}
93+
if count > 0 {
94+
return "\(mirror.subjectType)"
95+
}
96+
case .`class`:
97+
if let x = value {
98+
if let cdsc = (x as? CustomDebugStringConvertible) {
99+
return cdsc.debugDescription
100+
}
101+
if let csc = (x as? CustomStringConvertible) {
102+
return csc.description
103+
}
104+
// for a Class with no custom summary, mimic the Foundation default
105+
return "<\(x.dynamicType): 0x\(String(asNumericValue(value: x), radix: 16, uppercase: false))>"
106+
} else {
107+
// but if I can't provide a value, just use the type anyway
108+
return "\(mirror.subjectType)"
109+
}
110+
}
111+
if let x = value {
112+
return String(reflecting: x)
113+
}
114+
return nil
115+
}
116+
117+
static func ivarCount(mirror: Mirror) -> Int {
118+
let count = Int(mirror.children.count)
119+
if let sc = mirror.superclassMirror {
120+
return ivarCount(mirror: sc) + count
121+
} else {
122+
return count
123+
}
124+
}
125+
126+
127+
static func shouldExpand(mirror: Mirror,
128+
collectionStatus: CollectionStatus,
129+
isRoot: Bool) -> Bool {
130+
if isRoot || collectionStatus.isCollection { return true }
131+
let count = Int(mirror.children.count)
132+
if count > 0 { return true }
133+
if let sc = mirror.superclassMirror {
134+
return ivarCount(mirror: sc) > 0
135+
} else {
136+
return true
137+
}
138+
}
139+
140+
static func printForDebuggerImpl<StreamType: OutputStream>(
141+
value: Any?,
142+
mirror: Mirror,
143+
name: String?,
144+
indent: Int,
145+
maxDepth: Int,
146+
isRoot: Bool,
147+
parentCollectionStatus: CollectionStatus,
148+
refsAlreadySeen: inout Set<ObjectIdentifier>,
149+
maxItemCounter: inout Int,
150+
targetStream: inout StreamType) {
151+
if maxItemCounter <= 0 {
152+
return
153+
}
154+
155+
if !shouldExpand(mirror: mirror,
156+
collectionStatus: parentCollectionStatus,
157+
isRoot: isRoot) {
158+
return
159+
}
160+
161+
maxItemCounter -= 1
162+
163+
for _ in 0..<indent {
164+
print(" ", terminator: "", to: &targetStream)
165+
}
166+
167+
// do not expand classes with no custom Mirror
168+
// yes, a type can lie and say it's a class when it's not since we only
169+
// check the displayStyle - but then the type would have a custom Mirror
170+
// anyway, so there's that...
171+
var willExpand = true
172+
if let ds = mirror.displayStyle {
173+
if ds == .`class` {
174+
if let x = value {
175+
if !(x is CustomReflectable) {
176+
willExpand = false
177+
}
178+
}
179+
}
180+
}
181+
182+
let count = Int(mirror.children.count)
183+
let bullet = isRoot && (count == 0 || willExpand == false) ? ""
184+
: count == 0 ? "- "
185+
: maxDepth <= 0 ? "" : ""
186+
print("\(bullet)", terminator: "", to: &targetStream)
187+
188+
let collectionStatus = parentCollectionStatus.getChildStatus(child: mirror)
189+
190+
if let nam = name {
191+
print("\(nam) : ", terminator: "", to: &targetStream)
192+
}
193+
194+
if let str = asStringRepresentation(value: value, mirror: mirror, count: count) {
195+
print("\(str)", terminator: "", to: &targetStream)
196+
}
197+
198+
if (maxDepth <= 0) || !willExpand {
199+
print("", to: &targetStream)
200+
return
201+
}
202+
203+
if let x = value {
204+
if let valueIdentifier = asObjectIdentifier(value: x) {
205+
if refsAlreadySeen.contains(valueIdentifier) {
206+
print(" { ... }", to: &targetStream)
207+
return
208+
} else {
209+
refsAlreadySeen.insert(valueIdentifier)
210+
}
211+
}
212+
}
213+
214+
print("", to: &targetStream)
215+
216+
var printedElements = 0
217+
218+
if let sc = mirror.superclassMirror {
219+
printForDebuggerImpl(value: nil,
220+
mirror: sc,
221+
name: "super",
222+
indent: indent + 2,
223+
maxDepth: maxDepth - 1,
224+
isRoot: false,
225+
parentCollectionStatus: .NotACollection,
226+
refsAlreadySeen: &refsAlreadySeen,
227+
maxItemCounter: &maxItemCounter,
228+
targetStream: &targetStream)
229+
}
230+
231+
for (optionalName,child) in mirror.children {
232+
let childName = optionalName ?? "\(printedElements)"
233+
if maxItemCounter <= 0 {
234+
for _ in 0..<(indent+4) {
235+
print(" ", terminator: "", to: &targetStream)
236+
}
237+
let remainder = count - printedElements
238+
print("(\(remainder)", terminator: "", to: &targetStream)
239+
if printedElements > 0 {
240+
print(" more", terminator: "", to: &targetStream)
241+
}
242+
if remainder == 1 {
243+
print(" child)", to: &targetStream)
244+
} else {
245+
print(" children)", to: &targetStream)
246+
}
247+
return
248+
}
249+
250+
printForDebuggerImpl(value: child,
251+
mirror: Mirror(reflecting: child),
252+
name: childName,
253+
indent: indent + 2,
254+
maxDepth: maxDepth - 1,
255+
isRoot: false,
256+
parentCollectionStatus: collectionStatus,
257+
refsAlreadySeen: &refsAlreadySeen,
258+
maxItemCounter: &maxItemCounter,
259+
targetStream: &targetStream)
260+
printedElements += 1
261+
}
262+
}
263+
264+
struct StringOutput: OutputStream {
265+
var data = ""
266+
mutating func _lock() {
267+
}
268+
269+
mutating func _unlock() {
270+
}
271+
272+
mutating func write(_ string: String) {
273+
data += string
274+
}
275+
}
276+
277+
public static func printForDebugger(value: Any) -> String {
278+
var maxItemCounter = Int.max
279+
var refs = Set<ObjectIdentifier>()
280+
var targetStream = StringOutput()
281+
282+
printForDebuggerImpl(value: value,
283+
mirror: Mirror(reflecting: value),
284+
name: nil,
285+
indent: 0,
286+
maxDepth: maxItemCounter,
287+
isRoot: true,
288+
parentCollectionStatus: .NotACollection,
289+
refsAlreadySeen: &refs,
290+
maxItemCounter: &maxItemCounter,
291+
targetStream: &targetStream)
292+
293+
return targetStream.data
294+
}
295+
}
296+

test/1_stdlib/PrintForDebugger.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %target-run-simple-swift | FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
struct StructWithMembers {
5+
var a = 1
6+
var b = "Hello World"
7+
}
8+
9+
print(_PrintForDebugger.printForDebugger(value: StructWithMembers()))
10+
// CHECK: StructWithMembers
11+
// CHECK: a : 1
12+
// CHECK: b : "Hello World"
13+
14+
class ClassWithMembers {
15+
var a = 1
16+
var b = "Hello World"
17+
}
18+
19+
print(_PrintForDebugger.printForDebugger(value: ClassWithMembers()))
20+
// CHECK: <ClassWithMembers: 0x
21+
22+
class ClassWithMirror: CustomReflectable {
23+
var customMirror: Mirror {
24+
return Mirror(self, children: ["a" : 1, "b" : "Hello World"])
25+
}
26+
}
27+
28+
print(_PrintForDebugger.printForDebugger(value: ClassWithMirror()))
29+
// CHECK: ClassWithMirror
30+
// CHECK: a : 1
31+
// CHECK: b : "Hello World"
32+
33+
print(_PrintForDebugger.printForDebugger(value: [1,2,3,4]))
34+
// CHECK: 4 elements
35+
// CHECK: - 0 : 1
36+
// CHECK: - 1 : 2
37+
// CHECK: - 2 : 3
38+
// CHECK: - 3 : 4
39+
40+
print(_PrintForDebugger.printForDebugger(value: [1:2, 3:4]))
41+
// CHECK: 2 elements
42+
// CHECK: 0 : 2 elements
43+
// CHECK: 1 : 2 elements
44+
45+
print(_PrintForDebugger.printForDebugger(value: nil as Int?))
46+
// CHECK: nil
47+
48+
print(_PrintForDebugger.printForDebugger(value: 3 as Int?))
49+
// CHECK: Optional<Int>
50+
// CHECK: - some : 3
51+

0 commit comments

Comments
 (0)