Skip to content

Commit e05ff89

Browse files
authored
Merge pull request swiftlang#73 from akyrtzi/opt2-syntax-data
Make creation and visitation of SyntaxData more efficient
2 parents 91670f8 + 1859b17 commit e05ff89

25 files changed

+1076
-480
lines changed

Diff for: Package.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import PackageDescription
55
let package = Package(
66
name: "SwiftSyntax",
77
targets: [
8-
.target(name: "SwiftSyntax"),
8+
.target(name: "_CSwiftSyntax"),
9+
.target(name: "SwiftSyntax", dependencies: ["_CSwiftSyntax"]),
910
.testTarget(name: "SwiftSyntaxTest", dependencies: ["SwiftSyntax"], exclude: ["Inputs"]),
1011
.target(name: "lit-test-helper", dependencies: ["SwiftSyntax"])
1112
]

Diff for: Sources/SwiftSyntax/AtomicCounter.swift

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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+
// This is using the C function from '_CSwiftSyntax/src/atomic-counter.c' by
14+
// dynamically loading it using `dlsym`. The reason this mechanism is used
15+
// instead of importing a header is so that we preserve the property that
16+
// SwiftSyntax is a self-contained Swift module.
17+
// (also see '_CSwiftSyntax/include/README.md')
18+
19+
#if os(Linux)
20+
import Glibc
21+
#else
22+
import Darwin.C
23+
#endif
24+
25+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
26+
fileprivate let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
27+
#elseif os(Linux)
28+
fileprivate let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0)
29+
#endif
30+
31+
fileprivate typealias get_unique_counter_ty = (@convention(c) ()->UInt64)
32+
fileprivate let get_unique_counter_str = "cswiftsyntax_get_unique_counter"
33+
34+
/// Provides API to get an atomically increasing counter value.
35+
struct AtomicCounter {
36+
/// Get an atomically increasing counter value.
37+
static func next() -> UInt64 {
38+
return AtomicCounter.cswiftsyntax_get_unique_counter()
39+
}
40+
41+
static fileprivate let cswiftsyntax_get_unique_counter: get_unique_counter_ty = { ()->get_unique_counter_ty in
42+
let ptr = dlsym(RTLD_DEFAULT, get_unique_counter_str)!
43+
return unsafeBitCast(ptr, to: get_unique_counter_ty.self)
44+
}()
45+
}

Diff for: Sources/SwiftSyntax/IncrementalEditTransition.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ public final class IncrementalEditTransition: IncrementalParseLookup {
102102
return node
103103
}
104104

105-
fileprivate func lookUpFrom(_ node: Syntax, nodeOffset: Int, prevOffset: Int,
106-
kind: SyntaxKind) -> (Int, Syntax?) {
105+
fileprivate func lookUpFrom(_ node: _SyntaxBase, nodeOffset: Int, prevOffset: Int,
106+
kind: SyntaxKind) -> (Int, _SyntaxBase?) {
107107
if nodeCanBeReused(node, nodeOffset: nodeOffset, prevOffset: prevOffset,
108108
kind: kind) {
109109
return (nodeOffset, node)
@@ -117,7 +117,7 @@ public final class IncrementalEditTransition: IncrementalParseLookup {
117117
}
118118
let childEnd = childOffset + child.byteSize
119119
if childOffset <= prevOffset && prevOffset < childEnd {
120-
return lookUpFrom(child, nodeOffset: childOffset,
120+
return lookUpFrom(child.base, nodeOffset: childOffset,
121121
prevOffset: prevOffset, kind: kind)
122122
}
123123
// The next child starts where the previous child ended
@@ -126,7 +126,7 @@ public final class IncrementalEditTransition: IncrementalParseLookup {
126126
return (0, nil)
127127
}
128128

129-
fileprivate func nodeCanBeReused(_ node: Syntax, nodeOffset: Int,
129+
fileprivate func nodeCanBeReused(_ node: _SyntaxBase, nodeOffset: Int,
130130
prevOffset: Int, kind: SyntaxKind) -> Bool {
131131
// Computing the value of NodeStart on the fly is faster than determining a
132132
// node's absolute position, but make sure the values match in an assertion

Diff for: Sources/SwiftSyntax/LazyNonThreadSafeCache.swift

-43
This file was deleted.

Diff for: Sources/SwiftSyntax/RawSyntax.swift

+30-19
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,10 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
/// Box a value type into a reference type
14-
class Box<T> {
15-
let value: T
16-
17-
init(_ value: T) {
18-
self.value = value
19-
}
20-
}
21-
2213
/// The data that is specific to a tree or token node
2314
fileprivate enum RawSyntaxData {
2415
/// A tree node with a kind and an array of children
25-
case node(kind: SyntaxKind, layout: [RawSyntax?])
16+
case node(kind: SyntaxKind, layout: [RawSyntax?], totalNodes: Int)
2617
/// A token with a token kind, leading trivia, and trailing trivia
2718
case token(kind: TokenKind, leadingTrivia: Trivia, trailingTrivia: Trivia)
2819
}
@@ -37,7 +28,11 @@ final class RawSyntax {
3728

3829
init(kind: SyntaxKind, layout: [RawSyntax?], length: SourceLength,
3930
presence: SourcePresence) {
40-
self.data = .node(kind: kind, layout: layout)
31+
var totalSubNodes = 0
32+
for subnode in layout {
33+
totalSubNodes += subnode?.totalNodes ?? 0
34+
}
35+
self.data = .node(kind: kind, layout: layout, totalNodes: totalSubNodes)
4136
self.totalLength = length
4237
self.presence = presence
4338
}
@@ -53,26 +48,42 @@ final class RawSyntax {
5348
/// The syntax kind of this raw syntax.
5449
var kind: SyntaxKind {
5550
switch data {
56-
case .node(let kind, _): return kind
51+
case .node(let kind, _, _): return kind
5752
case .token(_, _, _): return .token
5853
}
5954
}
6055

6156
var tokenKind: TokenKind? {
6257
switch data {
63-
case .node(_, _): return nil
58+
case .node(_, _, _): return nil
6459
case .token(let kind, _, _): return kind
6560
}
6661
}
6762

6863
/// The layout of the children of this Raw syntax node.
6964
var layout: [RawSyntax?] {
7065
switch data {
71-
case .node(_, let layout): return layout
66+
case .node(_, let layout, _): return layout
7267
case .token(_, _, _): return []
7368
}
7469
}
7570

71+
/// The number of children, `present` or `missing`, in this node.
72+
var numberOfChildren: Int {
73+
switch data {
74+
case .node(_, let layout, _): return layout.count
75+
case .token(_, _, _): return 0
76+
}
77+
}
78+
79+
/// Total number of nodes in this sub-tree, including `self` node.
80+
var totalNodes: Int {
81+
switch data {
82+
case .node(_, _, let totalSubNodes): return totalSubNodes+1
83+
case .token(_, _, _): return 1
84+
}
85+
}
86+
7687
/// Whether or not this node is a token one.
7788
var isToken: Bool {
7889
return tokenKind != nil
@@ -117,7 +128,7 @@ final class RawSyntax {
117128
/// - Parameter newLayout: The children of the new node you're creating.
118129
func replacingLayout(_ newLayout: [RawSyntax?]) -> RawSyntax {
119130
switch data {
120-
case let .node(kind, _):
131+
case let .node(kind, _, _):
121132
return .createAndCalcLength(kind: kind, layout: newLayout,
122133
presence: presence)
123134
case .token(_, _, _): return self
@@ -164,7 +175,7 @@ extension RawSyntax: TextOutputStreamable {
164175
func write<Target>(to target: inout Target)
165176
where Target: TextOutputStream {
166177
switch data {
167-
case .node(_, let layout):
178+
case .node(_, let layout, _):
168179
for child in layout {
169180
guard let child = child else { continue }
170181
child.write(to: &target)
@@ -185,7 +196,7 @@ extension RawSyntax: TextOutputStreamable {
185196
extension RawSyntax {
186197
var leadingTrivia: Trivia? {
187198
switch data {
188-
case .node(_, let layout):
199+
case .node(_, let layout, _):
189200
for child in layout {
190201
guard let child = child else { continue }
191202
guard let result = child.leadingTrivia else { break }
@@ -199,7 +210,7 @@ extension RawSyntax {
199210

200211
var trailingTrivia: Trivia? {
201212
switch data {
202-
case .node(_, let layout):
213+
case .node(_, let layout, _):
203214
for child in layout.reversed() {
204215
guard let child = child else { continue }
205216
guard let result = child.trailingTrivia else { break }
@@ -284,7 +295,7 @@ extension RawSyntax {
284295
defer { visitor.moveUp() }
285296
guard isPresent else { return }
286297
switch data {
287-
case .node(let kind,let layout):
298+
case .node(let kind,let layout, _):
288299
let shouldVisit = visitor.shouldVisit(kind)
289300
var visitChildren = true
290301
if shouldVisit {

Diff for: Sources/SwiftSyntax/SourceLocation.swift

+44-16
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,41 @@ public final class SourceLocationConverter {
156156
}
157157
}
158158

159+
extension _SyntaxBase {
160+
func startLocation(
161+
converter: SourceLocationConverter,
162+
afterLeadingTrivia: Bool = true
163+
) -> SourceLocation {
164+
let pos = afterLeadingTrivia ?
165+
data.positionAfterSkippingLeadingTrivia :
166+
data.position
167+
return converter.location(for: pos)
168+
}
169+
170+
func endLocation(
171+
converter: SourceLocationConverter,
172+
afterTrailingTrivia: Bool = false
173+
) -> SourceLocation {
174+
var pos = data.position
175+
pos += raw.leadingTriviaLength
176+
pos += raw.contentLength
177+
if afterTrailingTrivia {
178+
pos += raw.trailingTriviaLength
179+
}
180+
return converter.location(for: pos)
181+
}
182+
183+
func sourceRange(
184+
converter: SourceLocationConverter,
185+
afterLeadingTrivia: Bool = true,
186+
afterTrailingTrivia: Bool = false
187+
) -> SourceRange {
188+
let start = startLocation(converter: converter, afterLeadingTrivia: afterLeadingTrivia)
189+
let end = endLocation(converter: converter, afterTrailingTrivia: afterTrailingTrivia)
190+
return SourceRange(start: start, end: end)
191+
}
192+
}
193+
159194
extension Syntax {
160195
/// The starting location, in the provided file, of this Syntax node.
161196
/// - Parameters:
@@ -167,10 +202,8 @@ extension Syntax {
167202
converter: SourceLocationConverter,
168203
afterLeadingTrivia: Bool = true
169204
) -> SourceLocation {
170-
let pos = afterLeadingTrivia ?
171-
data.positionAfterSkippingLeadingTrivia :
172-
data.position
173-
return converter.location(for: pos)
205+
return base.startLocation(converter: converter,
206+
afterLeadingTrivia: afterLeadingTrivia)
174207
}
175208

176209
/// The ending location, in the provided file, of this Syntax node.
@@ -183,13 +216,8 @@ extension Syntax {
183216
converter: SourceLocationConverter,
184217
afterTrailingTrivia: Bool = false
185218
) -> SourceLocation {
186-
var pos = data.position
187-
pos += raw.leadingTriviaLength
188-
pos += raw.contentLength
189-
if afterTrailingTrivia {
190-
pos += raw.trailingTriviaLength
191-
}
192-
return converter.location(for: pos)
219+
return base.endLocation(converter: converter,
220+
afterTrailingTrivia: afterTrailingTrivia)
193221
}
194222

195223
/// The source range, in the provided file, of this Syntax node.
@@ -205,9 +233,9 @@ extension Syntax {
205233
afterLeadingTrivia: Bool = true,
206234
afterTrailingTrivia: Bool = false
207235
) -> SourceRange {
208-
let start = startLocation(converter: converter, afterLeadingTrivia: afterLeadingTrivia)
209-
let end = endLocation(converter: converter, afterTrailingTrivia: afterTrailingTrivia)
210-
return SourceRange(start: start, end: end)
236+
return base.sourceRange(converter: converter,
237+
afterLeadingTrivia: afterLeadingTrivia,
238+
afterTrailingTrivia: afterTrailingTrivia)
211239
}
212240
}
213241

@@ -225,8 +253,8 @@ fileprivate func computeLines(
225253
lines.append(position)
226254
}
227255
var curPrefix: SourceLength = .zero
228-
tree.forEachToken {
229-
curPrefix = $0.forEachLineLength(prefix: curPrefix, body: addLine)
256+
for token in tree.tokens {
257+
curPrefix = token.forEachLineLength(prefix: curPrefix, body: addLine)
230258
}
231259
position += curPrefix
232260
return (lines, position)

Diff for: Sources/SwiftSyntax/SourcePresence.swift

-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
import Foundation
14-
1513
/// An indicator of whether a Syntax node was found or written in the source.
1614
///
1715
/// A `missing` node does not mean, necessarily, that the source item is

0 commit comments

Comments
 (0)