Skip to content

Commit baff373

Browse files
committed
[Macros] Store parsed syntax tres in a LRU cache
1 parent de29db5 commit baff373

File tree

5 files changed

+160
-6
lines changed

5 files changed

+160
-6
lines changed

Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
add_swift_syntax_library(SwiftCompilerPluginMessageHandling
1010
CompilerPluginMessageHandler.swift
1111
Diagnostics.swift
12+
LRUCache.swift
1213
Macros.swift
1314
PluginMacroExpansionContext.swift
1415
PluginMessageCompatibility.swift

Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public class CompilerPluginMessageHandler<Provider: PluginProvider> {
120120

121121
public init(provider: Provider) {
122122
self.provider = provider
123-
self.syntaxRegistry = ParsedSyntaxRegistry()
123+
self.syntaxRegistry = ParsedSyntaxRegistry(cacheCapacity: 16)
124124
self.hostCapability = HostCapability()
125125
}
126126

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Simple LRU cache.
14+
@_spi(Testing)
15+
public class LRUCache<Key: Hashable, Value> {
16+
private class _Node {
17+
unowned var prev: _Node? = nil
18+
unowned var next: _Node? = nil
19+
20+
let key: Key
21+
var value: Value
22+
23+
init(key: Key, value: Value) {
24+
self.key = key
25+
self.value = value
26+
}
27+
}
28+
29+
private var table: [Key: _Node]
30+
31+
// Double linked list
32+
private unowned var head: _Node?
33+
private unowned var tail: _Node?
34+
35+
public let capacity: Int
36+
37+
public init(capacity: Int) {
38+
self.table = [:]
39+
self.head = nil
40+
self.tail = nil
41+
self.capacity = capacity
42+
}
43+
44+
public var count: Int {
45+
return table.count
46+
}
47+
48+
public subscript(key: Key) -> Value? {
49+
get {
50+
guard let node = table[key] else {
51+
return nil
52+
}
53+
moveToHead(node: node)
54+
return node.value
55+
}
56+
57+
set {
58+
switch (table[key], newValue) {
59+
case let (nil, newValue?): // create.
60+
self.ensureCapacityForNewValue()
61+
let node = _Node(key: key, value: newValue)
62+
addToHead(node: node)
63+
table[key] = node
64+
65+
case let (node?, newValue?): // update.
66+
moveToHead(node: node)
67+
node.value = newValue
68+
69+
case let (node?, nil): // delete.
70+
remove(node: node)
71+
table[key] = nil
72+
73+
case (nil, nil): // no-op.
74+
break
75+
}
76+
}
77+
}
78+
79+
private func ensureCapacityForNewValue() {
80+
while self.table.count >= self.capacity, let tail = self.tail {
81+
remove(node: tail)
82+
table[tail.key] = nil
83+
}
84+
}
85+
86+
private func moveToHead(node: _Node) {
87+
if node === self.head {
88+
return
89+
}
90+
remove(node: node)
91+
addToHead(node: node)
92+
}
93+
94+
private func addToHead(node: _Node) {
95+
node.next = self.head
96+
node.next?.prev = node
97+
node.prev = nil
98+
self.head = node
99+
if self.tail == nil {
100+
self.tail = node
101+
}
102+
}
103+
104+
private func remove(node: _Node) {
105+
node.next?.prev = node.prev
106+
node.prev?.next = node.next
107+
if node === self.head {
108+
self.head = node.next
109+
}
110+
if node === self.tail {
111+
self.tail = node.prev
112+
}
113+
node.prev = nil
114+
node.next = nil
115+
}
116+
}

Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ class ParsedSyntaxRegistry {
3131
let kind: PluginMessage.Syntax.Kind
3232
}
3333

34-
private var storage: [Key: Syntax] = [:]
34+
private var storage: LRUCache<Key, Syntax>
35+
36+
init(cacheCapacity: Int) {
37+
self.storage = LRUCache(capacity: cacheCapacity)
38+
}
3539

3640
private func parse(source: String, kind: PluginMessage.Syntax.Kind) -> Syntax {
3741
var parser = Parser(source)
@@ -61,10 +65,6 @@ class ParsedSyntaxRegistry {
6165
storage[key] = node
6266
return node
6367
}
64-
65-
func clear() {
66-
storage = [:]
67-
}
6868
}
6969

7070
/// Manages known source code combined with their filename/fileID. This can be
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 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+
@_spi(Testing) import SwiftCompilerPluginMessageHandling
14+
import XCTest
15+
16+
final class LRUCacheTests: XCTestCase {
17+
func testBasic() {
18+
let cache = LRUCache<String, Int>(capacity: 2)
19+
cache["foo"] = 0
20+
cache["bar"] = 1
21+
XCTAssertEqual(cache["foo"], 0)
22+
cache["baz"] = 2
23+
XCTAssertEqual(cache["foo"], 0)
24+
XCTAssertEqual(cache["bar"], nil)
25+
XCTAssertEqual(cache["baz"], 2)
26+
XCTAssertEqual(cache.count, 2)
27+
28+
cache["qux"] = nil
29+
cache["baz"] = nil
30+
cache["foo"] = 10
31+
XCTAssertEqual(cache["foo"], 10)
32+
XCTAssertEqual(cache["bar"], nil)
33+
XCTAssertEqual(cache["baz"], nil)
34+
XCTAssertEqual(cache["qux"], nil)
35+
XCTAssertEqual(cache.count, 1)
36+
}
37+
}

0 commit comments

Comments
 (0)