Skip to content

Commit 226a978

Browse files
ahoppenrintaro
authored andcommitted
[swiftSyntax] Store weak references to RawSyntax nodes in the nodeLookupTable
This way we don't continue to retain RawSyntax nodes that are no longer needed for incremental transfer.
1 parent af573bf commit 226a978

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ extension RawSyntax {
343343

344344
extension RawSyntax: Codable {
345345
/// Creates a RawSyntax from the provided Foundation Decoder.
346-
required convenience init(from decoder: Decoder) throws {
346+
convenience init(from decoder: Decoder) throws {
347347
let container = try decoder.container(keyedBy: CodingKeys.self)
348348
let id = try container.decodeIfPresent(SyntaxNodeId.self, forKey: .id)
349349
let omitted = try container.decodeIfPresent(Bool.self, forKey: .omitted) ?? false

Sources/SwiftSyntax/SwiftSyntax.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ public enum SerializationFormat {
4646
case byteTree
4747
}
4848

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+
4957
/// Deserializes the syntax tree from its serialized form to an object tree in
5058
/// Swift. To deserialize incrementally transferred syntax trees, the same
5159
/// instance of the deserializer must be used for all subsequent
@@ -56,7 +64,14 @@ public final class SyntaxTreeDeserializer {
5664

5765
/// Syntax nodes that have already been parsed and are able to be reused if
5866
/// 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
6075

6176
/// The IDs of the nodes that were reused as part of incremental syntax
6277
/// parsing during the last deserialization
@@ -70,19 +85,23 @@ public final class SyntaxTreeDeserializer {
7085
let decoder = JSONDecoder()
7186
decoder.userInfo[.rawSyntaxDecodedCallback] = self.addToLookupTable
7287
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
7491
}
7592

7693
/// Deserialize the given data as a ByteTree encoded syntax tree
7794
private func deserializeByteTree(_ data: Data) throws -> RawSyntax {
7895
var userInfo: [ByteTreeUserInfoKey: Any] = [:]
7996
userInfo[.rawSyntaxDecodedCallback] = self.addToLookupTable
8097
userInfo[.omittedNodeLookupFunction] = self.lookupNode
81-
return try ByteTreeReader.read(RawSyntax.self, from: data,
98+
let tree = try ByteTreeReader.read(RawSyntax.self, from: data,
8299
userInfo: &userInfo) {
83100
(version: ByteTreeProtocolVersion) in
84101
return version.major == 1
85102
}
103+
self.nodeLookupTree = tree
104+
return tree
86105
}
87106

88107
/// Decode a serialized form of SourceFileSyntax to a syntax tree.
@@ -112,11 +131,17 @@ public final class SyntaxTreeDeserializer {
112131

113132
private func lookupNode(id: SyntaxNodeId) -> RawSyntax? {
114133
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
116141
}
117142

118143
private func addToLookupTable(_ node: RawSyntax) {
119-
nodeLookupTable[node.id] = node
144+
nodeLookupTable[node.id] = WeakReference(node)
120145
}
121146
}
122147

0 commit comments

Comments
 (0)