@@ -46,6 +46,14 @@ public enum SerializationFormat {
46
46
case byteTree
47
47
}
48
48
49
+ fileprivate struct WeakReference < T: AnyObject > {
50
+ weak private( set) var value : T ?
51
+
52
+ init ( _ value: T ) {
53
+ self . value = value
54
+ }
55
+ }
56
+
49
57
/// Deserializes the syntax tree from its serialized form to an object tree in
50
58
/// Swift. To deserialize incrementally transferred syntax trees, the same
51
59
/// instance of the deserializer must be used for all subsequent
@@ -56,7 +64,14 @@ public final class SyntaxTreeDeserializer {
56
64
57
65
/// Syntax nodes that have already been parsed and are able to be reused if
58
66
/// they were omitted in an incremental syntax tree transfer
59
- private var nodeLookupTable : [ SyntaxNodeId : RawSyntax ] = [ : ]
67
+ private var nodeLookupTable : [ SyntaxNodeId : WeakReference < RawSyntax > ] = [ : ]
68
+
69
+ /// Keep a strong reference to the syntax tree that contains the nodes in the
70
+ /// `nodeLookupTable`. Because `nodeLookupTable` only holds a weak reference
71
+ /// to the RawSyntax nodes, all retired `RawSyntax` nodes will be deallocated
72
+ /// once we set a new tree. The weak references in `nodeLookupTable` will then
73
+ /// become `nil` but will also never be accessed again.
74
+ private var nodeLookupTree : RawSyntax ? = nil
60
75
61
76
/// The IDs of the nodes that were reused as part of incremental syntax
62
77
/// parsing during the last deserialization
@@ -70,19 +85,23 @@ public final class SyntaxTreeDeserializer {
70
85
let decoder = JSONDecoder ( )
71
86
decoder. userInfo [ . rawSyntaxDecodedCallback] = self . addToLookupTable
72
87
decoder. userInfo [ . omittedNodeLookupFunction] = self . lookupNode
73
- return try decoder. decode ( RawSyntax . self, from: data)
88
+ let tree = try decoder. decode ( RawSyntax . self, from: data)
89
+ self . nodeLookupTree = tree
90
+ return tree
74
91
}
75
92
76
93
/// Deserialize the given data as a ByteTree encoded syntax tree
77
94
private func deserializeByteTree( _ data: Data ) throws -> RawSyntax {
78
95
var userInfo : [ ByteTreeUserInfoKey : Any ] = [ : ]
79
96
userInfo [ . rawSyntaxDecodedCallback] = self . addToLookupTable
80
97
userInfo [ . omittedNodeLookupFunction] = self . lookupNode
81
- return try ByteTreeReader . read ( RawSyntax . self, from: data,
98
+ let tree = try ByteTreeReader . read ( RawSyntax . self, from: data,
82
99
userInfo: & userInfo) {
83
100
( version: ByteTreeProtocolVersion ) in
84
101
return version. major == 1
85
102
}
103
+ self . nodeLookupTree = tree
104
+ return tree
86
105
}
87
106
88
107
/// Decode a serialized form of SourceFileSyntax to a syntax tree.
@@ -112,11 +131,17 @@ public final class SyntaxTreeDeserializer {
112
131
113
132
private func lookupNode( id: SyntaxNodeId ) -> RawSyntax ? {
114
133
reusedNodeIds. insert ( id)
115
- return nodeLookupTable [ id]
134
+ guard let weakRef = nodeLookupTable [ id] else {
135
+ return nil
136
+ }
137
+ guard let value = weakRef. value else {
138
+ fatalError ( " Trying to retrieve a node that has since been deallocated " )
139
+ }
140
+ return value
116
141
}
117
142
118
143
private func addToLookupTable( _ node: RawSyntax ) {
119
- nodeLookupTable [ node. id] = node
144
+ nodeLookupTable [ node. id] = WeakReference ( node)
120
145
}
121
146
}
122
147
0 commit comments