3
3
// OpenSwiftUICore
4
4
//
5
5
// Audited for iOS 18.0
6
- // Status: Blocked by _GraphInputs
6
+ // Status: Complete
7
7
// ID: 3E2D3733C4CBF57EC1EA761D02CE8317 (SwiftUICore)
8
8
9
+ import Foundation
9
10
package import OpenGraphShims
11
+ #if OPENSWIFTUI_SWIFT_LOG
12
+ import Logging
13
+ #else
14
+ import os. log
15
+ #endif
16
+
17
+ // MARK: - IndirectAttributeMap
10
18
11
- public final class IndirectAttributeMap {
19
+ package final class IndirectAttributeMap {
12
20
package final let subgraph : Subgraph
21
+
13
22
package final var map : [ AnyAttribute : AnyAttribute ]
23
+
14
24
package init ( subgraph: Subgraph ) {
15
25
self . subgraph = subgraph
16
26
self . map = [ : ]
17
27
}
18
28
}
19
29
30
+ // MARK: - GraphReusable
31
+
20
32
package protocol GraphReusable {
21
33
static var isTriviallyReusable : Bool { get }
34
+
22
35
mutating func makeReusable( indirectMap: IndirectAttributeMap )
36
+
23
37
func tryToReuse( by other: Self , indirectMap: IndirectAttributeMap , testOnly: Bool ) -> Bool
24
38
}
25
39
@@ -28,6 +42,8 @@ extension GraphReusable {
28
42
package static var isTriviallyReusable : Bool { false }
29
43
}
30
44
45
+ // MARK: - _GraphValue + GraphReusable
46
+
31
47
extension _GraphValue : GraphReusable {
32
48
package mutating func makeReusable( indirectMap: IndirectAttributeMap ) {
33
49
value. makeReusable ( indirectMap: indirectMap)
@@ -41,65 +57,137 @@ extension _GraphValue where Value: GraphReusable {
41
57
package static var isTriviallyReusable : Bool { Value . isTriviallyReusable }
42
58
}
43
59
44
- //extension _GraphInputs : GraphReusable {
45
- // package mutating func makeReusable(indirectMap: IndirectAttributeMap)
46
- // package func tryToReuse(by other: _GraphInputs, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool
47
- //}
60
+ // MARK: - _GraphInputs + GraphReusable [WIP]
61
+
62
+ extension _GraphInputs : GraphReusable {
63
+ package mutating func makeReusable( indirectMap: IndirectAttributeMap ) {
64
+ time. makeReusable ( indirectMap: indirectMap)
65
+ phase. makeReusable ( indirectMap: indirectMap)
66
+ changedDebugProperties. insert ( . phase)
67
+ environment. makeReusable ( indirectMap: indirectMap)
68
+ detachEnvironmentInputs ( )
69
+ changedDebugProperties. insert ( . environment)
70
+ transaction. makeReusable ( indirectMap: indirectMap)
71
+ func project< Input> ( _ type: Input . Type ) where Input: GraphInput {
72
+ guard !Input. isTriviallyReusable else {
73
+ return
74
+ }
75
+ var value = self [ Input . self]
76
+ Input . makeReusable ( indirectMap: indirectMap, value: & value)
77
+ self [ Input . self] = value
78
+ }
79
+ let stack = customInputs [ ReusableInputs . self] . stack
80
+ for value in stack {
81
+ project ( value)
82
+ }
83
+ }
84
+
85
+ package func tryToReuse( by other: _GraphInputs , indirectMap: IndirectAttributeMap , testOnly: Bool ) -> Bool {
86
+ guard time. tryToReuse ( by: other. time, indirectMap: indirectMap, testOnly: testOnly) ,
87
+ phase. tryToReuse ( by: other. phase, indirectMap: indirectMap, testOnly: testOnly) ,
88
+ environment. tryToReuse ( by: other. environment, indirectMap: indirectMap, testOnly: testOnly) ,
89
+ transaction. tryToReuse ( by: other. transaction, indirectMap: indirectMap, testOnly: testOnly)
90
+ else {
91
+ Log . graphReuse ( " Reuse failed: standard inputs " )
92
+ return false
93
+ }
94
+ return reuseCustomInputs ( by: other, indirectMap: indirectMap, testOnly: testOnly)
95
+ }
96
+
97
+ private func reuseCustomInputs( by other: _GraphInputs , indirectMap: IndirectAttributeMap , testOnly: Bool ) -> Bool {
98
+ let reusableInputs = customInputs [ ReusableInputs . self]
99
+ let otherReusableInputs = other. customInputs [ ReusableInputs . self]
100
+ guard reusableInputs. filter == otherReusableInputs. filter else {
101
+ return false
102
+ }
103
+ var reusableInputsArray : [ ObjectIdentifier ] = [ ]
104
+ for value in reusableInputs. stack {
105
+ reusableInputsArray. append ( ObjectIdentifier ( value) )
106
+ }
107
+ var otherReusableInputsArray : [ ObjectIdentifier ] = [ ]
108
+ for value in otherReusableInputs. stack {
109
+ otherReusableInputsArray. append ( ObjectIdentifier ( value) )
110
+ }
111
+ guard reusableInputsArray == otherReusableInputsArray else {
112
+ Log . graphReuse ( " Reuse failed: custom inputs type mismatch " )
113
+ return false
114
+ }
115
+ var ignoredTypes = reusableInputsArray + [ ObjectIdentifier ( ReusableInputs . self) ]
116
+ guard !customInputs. mayNotBeEqual ( to: other. customInputs, ignoredTypes: & ignoredTypes) else {
117
+ Log . graphReuse ( " Reuse failed: custom inputs plist equality " )
118
+ return false
119
+ }
120
+ func project< Input> ( _ type: Input . Type ) -> Bool where Input: GraphInput {
121
+ guard let index = ignoredTypes. firstIndex ( of: ObjectIdentifier ( type) ) else {
122
+ return true
123
+ }
124
+ let lastIndex = ignoredTypes. count - 1
125
+ ignoredTypes. swapAt ( index, lastIndex)
126
+ guard !Input. isTriviallyReusable else {
127
+ return true
128
+ }
129
+ guard Input . tryToReuse ( self [ type] , by: other [ type] , indirectMap: indirectMap, testOnly: testOnly) else {
130
+ Log . graphReuse ( " Reuse failed: custom input \( Input . self) " )
131
+ return false
132
+ }
133
+ return true
134
+ }
135
+ let stack = reusableInputs. stack
136
+ for value in stack {
137
+ guard project ( value) else {
138
+ return false
139
+ }
140
+ continue
141
+ }
142
+ return true
143
+ }
144
+ }
48
145
49
- //extension _GraphInputs {
50
- // private func reuseCustomInputs(by other: _GraphInputs, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
51
- // Log.graphReuse("Reuse failed: custom input \(Self.self)")
52
- // }
53
- //}
146
+ // MARK: - Attribute + GraphReusable
54
147
55
148
extension Attribute : GraphReusable {
56
149
package mutating func makeReusable( indirectMap: IndirectAttributeMap ) {
150
+ let indirect : AnyAttribute
57
151
if let result = indirectMap. map [ identifier] {
58
- identifier = result
152
+ indirect = result
59
153
} else {
60
- let indirect = indirectMap. subgraph. apply {
61
- IndirectAttribute ( source: self )
154
+ indirect = indirectMap. subgraph. apply {
155
+ IndirectAttribute ( source: self ) . identifier
62
156
}
63
- indirectMap. map [ identifier] = indirect. identifier
157
+ indirectMap. map [ identifier] = indirect
64
158
}
159
+ identifier = indirect
65
160
}
66
-
161
+
67
162
package func tryToReuse( by other: Attribute < Value > , indirectMap: IndirectAttributeMap , testOnly: Bool ) -> Bool {
68
- if let result = indirectMap. map [ identifier] {
69
- if testOnly {
70
- return true
71
- } else {
72
- result. source = other. identifier
73
- return true
74
- }
75
- } else {
163
+ guard let result = indirectMap. map [ identifier] else {
76
164
Log . graphReuse ( " Reuse failed: missing indirection for \( Value . self) " )
77
165
return false
78
166
}
167
+ if !testOnly {
168
+ result. source = other. identifier
169
+ }
170
+ return true
79
171
}
80
172
}
81
173
82
- import Foundation
83
- #if canImport(Darwin)
84
- import os. log
85
- #endif
86
-
87
174
private struct EnableGraphReuseLogging : UserDefaultKeyedFeature {
88
175
static var key : String { " org.OpenSwiftUIProject.OpenSwiftUI.GraphReuseLogging " }
176
+
89
177
static var cachedValue : Bool ?
90
178
}
91
179
92
180
extension Log {
93
- #if canImport(Darwin)
94
181
private static let graphReuseLog : Logger = Logger ( subsystem: Log . subsystem, category: " GraphReuse " )
95
- #endif
96
-
182
+
97
183
static func graphReuse( _ message: @autoclosure ( ) -> String ) {
98
- #if canImport(Darwin)
99
184
if EnableGraphReuseLogging . isEnabled {
100
185
let message = message ( )
186
+ #if OPENSWIFTUI_SWIFT_LOG
187
+ graphReuseLog. log ( level: . info, " \( message) " )
188
+ #else
101
189
graphReuseLog. log ( " \( message) " )
190
+ #endif
102
191
}
103
- #endif
104
192
}
105
193
}
0 commit comments