Skip to content

Commit a7542a5

Browse files
Kyle Macomberairspeedswift
Kyle Macomber
andauthored
Refactor Mirror to reduce metadata allocation (#32041) (#32957)
- Refactor Mirror.descendents - Add _Either sequence - Create custom reflected children type - Switch Mirror to use _Either Co-authored-by: Ben Cohen <[email protected]>
1 parent 9e57fbf commit a7542a5

8 files changed

+300
-91
lines changed

stdlib/public/core/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ set(SWIFTLIB_ESSENTIAL
6060
DictionaryVariant.swift
6161
DropWhile.swift
6262
Dump.swift
63+
EitherSequence.swift
6364
EmptyCollection.swift
6465
Equatable.swift
6566
ErrorType.swift

stdlib/public/core/DebuggerSupport.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public enum _DebuggerSupport {
110110

111111
private static func ivarCount(mirror: Mirror) -> Int {
112112
let ivars = mirror.superclassMirror.map(ivarCount) ?? 0
113-
return ivars + mirror.children.count
113+
return ivars + mirror._children.count
114114
}
115115

116116
private static func shouldExpand(
@@ -119,7 +119,7 @@ public enum _DebuggerSupport {
119119
isRoot: Bool
120120
) -> Bool {
121121
if isRoot || collectionStatus.isCollection { return true }
122-
if !mirror.children.isEmpty { return true }
122+
if !mirror._children.isEmpty { return true }
123123
if mirror.displayStyle == .`class` { return true }
124124
if let sc = mirror.superclassMirror { return ivarCount(mirror: sc) > 0 }
125125
return true
@@ -154,7 +154,7 @@ public enum _DebuggerSupport {
154154
// anyway, so there's that...
155155
let willExpand = mirror.displayStyle != .`class` || value is CustomReflectable?
156156

157-
let count = mirror.children.count
157+
let count = mirror._children.count
158158
let bullet = isRoot && (count == 0 || !willExpand) ? ""
159159
: count == 0 ? "- "
160160
: maxDepth <= 0 ? "" : ""
@@ -202,7 +202,7 @@ public enum _DebuggerSupport {
202202
target: &target)
203203
}
204204

205-
for (optionalName,child) in mirror.children {
205+
for (optionalName,child) in mirror._children {
206206
let childName = optionalName ?? "\(printedElements)"
207207
if maxItemCounter <= 0 {
208208
print(String(repeating: " ", count: indent+4), terminator: "", to: &target)

stdlib/public/core/Dump.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ internal func _dump_unlocked<TargetStream: TextOutputStream>(
9999
for _ in 0..<indent { target.write(" ") }
100100

101101
let mirror = Mirror(reflecting: value)
102-
let count = mirror.children.count
102+
let count = mirror._children.count
103103
let bullet = count == 0 ? "-"
104104
: maxDepth <= 0 ? "" : ""
105105
target.write(bullet)
@@ -149,7 +149,7 @@ internal func _dump_unlocked<TargetStream: TextOutputStream>(
149149
visitedItems: &visitedItems)
150150
}
151151

152-
var currentIndex = mirror.children.startIndex
152+
var currentIndex = mirror._children.startIndex
153153
for i in 0..<count {
154154
if maxItemCounter <= 0 {
155155
for _ in 0..<(indent+4) {
@@ -167,8 +167,8 @@ internal func _dump_unlocked<TargetStream: TextOutputStream>(
167167
return
168168
}
169169

170-
let (name, child) = mirror.children[currentIndex]
171-
mirror.children.formIndex(after: &currentIndex)
170+
let (name, child) = mirror._children[currentIndex]
171+
mirror._children.formIndex(after: &currentIndex)
172172
_dump_unlocked(
173173
child,
174174
to: &target,
@@ -196,7 +196,7 @@ internal func _dumpSuperclass_unlocked<TargetStream: TextOutputStream>(
196196

197197
for _ in 0..<indent { target.write(" ") }
198198

199-
let count = mirror.children.count
199+
let count = mirror._children.count
200200
let bullet = count == 0 ? "-"
201201
: maxDepth <= 0 ? "" : ""
202202
target.write(bullet)
@@ -216,7 +216,7 @@ internal func _dumpSuperclass_unlocked<TargetStream: TextOutputStream>(
216216
visitedItems: &visitedItems)
217217
}
218218

219-
var currentIndex = mirror.children.startIndex
219+
var currentIndex = mirror._children.startIndex
220220
for i in 0..<count {
221221
if maxItemCounter <= 0 {
222222
for _ in 0..<(indent+4) {
@@ -234,8 +234,8 @@ internal func _dumpSuperclass_unlocked<TargetStream: TextOutputStream>(
234234
return
235235
}
236236

237-
let (name, child) = mirror.children[currentIndex]
238-
mirror.children.formIndex(after: &currentIndex)
237+
let (name, child) = mirror._children[currentIndex]
238+
mirror._children.formIndex(after: &currentIndex)
239239
_dump_unlocked(
240240
child,
241241
to: &target,
+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
////===--- _EitherSequence.swift - A sequence type-erasing two sequences -----===//
2+
////
3+
//// This source file is part of the Swift.org open source project
4+
////
5+
//// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
//// Licensed under Apache License v2.0 with Runtime Library Exception
7+
////
8+
//// See https://swift.org/LICENSE.txt for license information
9+
//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
////
11+
////===----------------------------------------------------------------------===//
12+
13+
// Not public stdlib API, currently used in Mirror.children implementation.
14+
15+
internal enum _Either<Left, Right> {
16+
case left(Left), right(Right)
17+
}
18+
19+
extension _Either {
20+
internal init(_ left: Left, or other: Right.Type) { self = .left(left) }
21+
internal init(_ left: Left) { self = .left(left) }
22+
internal init(_ right: Right) { self = .right(right) }
23+
}
24+
25+
extension _Either: Equatable where Left: Equatable, Right: Equatable {
26+
internal static func == (lhs: Self, rhs: Self) -> Bool {
27+
switch (lhs, rhs) {
28+
case let (.left(l), .left(r)): return l == r
29+
case let (.right(l), .right(r)): return l == r
30+
case (.left, .right), (.right, .left): return false
31+
}
32+
}
33+
}
34+
35+
extension _Either: Comparable where Left: Comparable, Right: Comparable {
36+
internal static func < (lhs: Self, rhs: Self) -> Bool {
37+
switch (lhs, rhs) {
38+
case let (.left(l), .left(r)): return l < r
39+
case let (.right(l), .right(r)): return l < r
40+
case (.left, .right): return true
41+
case (.right, .left): return false
42+
}
43+
}
44+
}
45+
46+
/// A sequence that type erases two sequences. A lighter-weight alternative to
47+
/// AnySequence when more can be statically known, and which is more easily
48+
/// specialized.
49+
///
50+
/// If you only know about one of the types, the second one can be
51+
/// AnySequence, giving you a fast path for the known one.
52+
///
53+
/// If you have 3+ types to erase, you can nest them.
54+
typealias _EitherSequence<L: Sequence, R: Sequence> =
55+
_Either<L,R> where L.Element == R.Element
56+
57+
extension _EitherSequence {
58+
internal struct Iterator {
59+
var left: Left.Iterator?
60+
var right: Right.Iterator?
61+
}
62+
}
63+
64+
extension _Either.Iterator: IteratorProtocol {
65+
internal typealias Element = Left.Element
66+
67+
internal mutating func next() -> Element? {
68+
left?.next() ?? right?.next()
69+
}
70+
}
71+
72+
extension _EitherSequence: Sequence {
73+
internal typealias Element = Left.Element
74+
75+
internal func makeIterator() -> Iterator {
76+
switch self {
77+
case let .left(l):
78+
return Iterator(left: l.makeIterator(), right: nil)
79+
case let .right(r):
80+
return Iterator(left: nil, right: r.makeIterator())
81+
}
82+
}
83+
}
84+
85+
internal typealias _EitherCollection<
86+
T: Collection, U: Collection
87+
> = _EitherSequence<T,U> where T.Element == U.Element
88+
89+
extension _EitherCollection: Collection {
90+
internal typealias Index = _Either<Left.Index, Right.Index>
91+
92+
internal var startIndex: Index {
93+
switch self {
94+
case let .left(s): return .left(s.startIndex)
95+
case let .right(s): return .right(s.startIndex)
96+
}
97+
}
98+
99+
internal var endIndex: Index {
100+
switch self {
101+
case let .left(s): return .left(s.endIndex)
102+
case let .right(s): return .right(s.endIndex)
103+
}
104+
}
105+
106+
internal subscript(position: Index) -> Element {
107+
switch (self,position) {
108+
case let (.left(s),.left(i)): return s[i]
109+
case let (.right(s),.right(i)): return s[i]
110+
default: fatalError("_EitherCollecton: Sequence used with other index type")
111+
}
112+
}
113+
114+
internal func index(after i: Index) -> Index {
115+
switch (self,i) {
116+
case let (.left(s),.left(i)): return .left(s.index(after: i))
117+
case let (.right(s),.right(i)): return .right(s.index(after: i))
118+
default: fatalError("_EitherCollecton: wrong type of index used")
119+
}
120+
}
121+
122+
internal func index(
123+
_ i: Index,
124+
offsetBy distance: Int,
125+
limitedBy limit: Index
126+
) -> Index? {
127+
switch (self,i,limit) {
128+
case let (.left(s),.left(i),.left(limit)):
129+
return s.index(i, offsetBy: distance, limitedBy: limit).map { .left($0) }
130+
case let (.right(s),.right(i),.right(limit)):
131+
return s.index(i, offsetBy: distance, limitedBy: limit).map { .right($0) }
132+
default: fatalError("_EitherCollecton: wrong type of index used")
133+
}
134+
}
135+
136+
internal func index(_ i: Index, offsetBy distance: Int) -> Index {
137+
switch (self,i) {
138+
case let (.left(s),.left(i)): return .left(s.index(i, offsetBy: distance))
139+
case let (.right(s),.right(i)): return .right(s.index(i, offsetBy: distance))
140+
default: fatalError("_EitherCollecton: wrong type of index used")
141+
}
142+
}
143+
144+
internal func distance(from start: Index, to end: Index) -> Int {
145+
switch (self,start,end) {
146+
case let (.left(s),.left(i),.left(j)):
147+
return s.distance(from: i, to: j)
148+
case let (.right(s),.right(i),.right(j)):
149+
return s.distance(from: i, to: j)
150+
default: fatalError("_EitherCollecton: wrong type of index used")
151+
}
152+
}
153+
}
154+
155+
internal typealias _EitherBidirectionalCollection<
156+
L: BidirectionalCollection, R: BidirectionalCollection
157+
> = _Either<L,R> where L.Element == R.Element
158+
159+
extension _EitherBidirectionalCollection: BidirectionalCollection {
160+
internal func index(before i: Index) -> Index {
161+
switch (self,i) {
162+
case let (.left(s),.left(i)): return .left(s.index(before: i))
163+
case let (.right(s),.right(i)): return .right(s.index(before: i))
164+
default: fatalError("_EitherCollecton: wrong type of index used")
165+
}
166+
}
167+
}
168+
169+
internal typealias _EitherRandomAccessCollection<
170+
L: RandomAccessCollection, R: RandomAccessCollection
171+
> = _Either<L,R> where L.Element == R.Element
172+
173+
extension _EitherRandomAccessCollection: RandomAccessCollection { }
174+
175+
extension _Either {
176+
init<T, C: Collection>(
177+
_ collection: C
178+
) where Right == AnyCollection<T>, C.Element == T {
179+
self = .right(AnyCollection(collection))
180+
}
181+
}
182+
183+
extension AnyCollection {
184+
init<L: Collection,R: Collection>(
185+
_ other: _Either<L,R>
186+
) where L.Element == Element, R.Element == Element {
187+
// Strip away the Either and put the actual collection into the existential,
188+
// trying to use the custom initializer from another AnyCollection.
189+
switch other {
190+
case let .left(l as Self): self = .init(l)
191+
case let .right(r as Self): self = .init(r)
192+
case let .left(l): self = .init(l)
193+
case let .right(r): self = .init(r)
194+
}
195+
}
196+
}
197+

stdlib/public/core/GroupInfo.json

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"RandomAccessCollection.swift",
6565
"MutableCollection.swift",
6666
"CollectionAlgorithms.swift",
67+
"EitherSequence.swift",
6768
"EmptyCollection.swift",
6869
"Stride.swift",
6970
"Repeat.swift",

0 commit comments

Comments
 (0)