Skip to content

Commit

Permalink
[swiftSyntax] Store weak references to RawSyntax nodes in the nodeLoo…
Browse files Browse the repository at this point in the history
…kupTable

This way we don't continue to retain RawSyntax nodes that are no longer
needed for incremental transfer.
  • Loading branch information
ahoppen committed Sep 12, 2018
1 parent 6e1f86c commit 0b303c0
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Sources/SwiftSyntax/RawSyntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ extension RawSyntax {

extension RawSyntax: Codable {
/// Creates a RawSyntax from the provided Foundation Decoder.
required convenience init(from decoder: Decoder) throws {
convenience init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let id = try container.decodeIfPresent(SyntaxNodeId.self, forKey: .id)
let omitted = try container.decodeIfPresent(Bool.self, forKey: .omitted) ?? false
Expand Down
35 changes: 30 additions & 5 deletions Sources/SwiftSyntax/SwiftSyntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ public enum SerializationFormat {
case byteTree
}

fileprivate struct WeakReference<T: AnyObject> {
weak private(set) var value: T?

init(_ value: T) {
self.value = value
}
}

/// Deserializes the syntax tree from its serialized form to an object tree in
/// Swift. To deserialize incrementally transferred syntax trees, the same
/// instance of the deserializer must be used for all subsequent
Expand All @@ -56,7 +64,14 @@ public final class SyntaxTreeDeserializer {

/// Syntax nodes that have already been parsed and are able to be reused if
/// they were omitted in an incremental syntax tree transfer
private var nodeLookupTable: [SyntaxNodeId: RawSyntax] = [:]
private var nodeLookupTable: [SyntaxNodeId: WeakReference<RawSyntax>] = [:]

/// Keep a strong reference to the syntax tree that contains the nodes in the
/// `nodeLookupTable`. Because `nodeLookupTable` only holds a weak reference
/// to the RawSyntax nodes, all retired `RawSyntax` nodes will be deallocated
/// once we set a new tree. The weak references in `nodeLookupTable` will then
/// become `nil` but will also never be accessed again.
private var nodeLookupTree: RawSyntax? = nil

/// The IDs of the nodes that were reused as part of incremental syntax
/// parsing during the last deserialization
Expand All @@ -70,19 +85,23 @@ public final class SyntaxTreeDeserializer {
let decoder = JSONDecoder()
decoder.userInfo[.rawSyntaxDecodedCallback] = self.addToLookupTable
decoder.userInfo[.omittedNodeLookupFunction] = self.lookupNode
return try decoder.decode(RawSyntax.self, from: data)
let tree = try decoder.decode(RawSyntax.self, from: data)
self.nodeLookupTree = tree
return tree
}

/// Deserialize the given data as a ByteTree encoded syntax tree
private func deserializeByteTree(_ data: Data) throws -> RawSyntax {
var userInfo: [ByteTreeUserInfoKey: Any] = [:]
userInfo[.rawSyntaxDecodedCallback] = self.addToLookupTable
userInfo[.omittedNodeLookupFunction] = self.lookupNode
return try ByteTreeReader.read(RawSyntax.self, from: data,
let tree = try ByteTreeReader.read(RawSyntax.self, from: data,
userInfo: &userInfo) {
(version: ByteTreeProtocolVersion) in
return version.major == 1
}
self.nodeLookupTree = tree
return tree
}

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

private func lookupNode(id: SyntaxNodeId) -> RawSyntax? {
reusedNodeIds.insert(id)
return nodeLookupTable[id]
guard let weakRef = nodeLookupTable[id] else {
return nil
}
guard let value = weakRef.value else {
fatalError("Trying to retrieve a node that has since been deallocated")
}
return value
}

private func addToLookupTable(_ node: RawSyntax) {
nodeLookupTable[node.id] = node
nodeLookupTable[node.id] = WeakReference(node)
}
}

Expand Down

0 comments on commit 0b303c0

Please sign in to comment.