Skip to content

Commit 6b84f8b

Browse files
authored
Merge pull request #190 from owenv/binary-swiftdeps
Support reading binary swiftdeps files
2 parents e9aab7b + 4539676 commit 6b84f8b

File tree

11 files changed

+1077
-34
lines changed

11 files changed

+1077
-34
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ add_library(SwiftDriver
6060
Toolchains/GenericUnixToolchain.swift
6161
Toolchains/Toolchain.swift
6262

63+
Utilities/Bits.swift
64+
Utilities/Bitstream.swift
6365
Utilities/DOTJobGraphSerializer.swift
6466
Utilities/DateAdditions.swift
6567
Utilities/Diagnostics.swift

Sources/SwiftDriver/Incremental Compilation/SourceFileDependencyGraph.swift

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
@_implementationOnly import Yams
12+
import Foundation
1313

14-
public struct SourceFileDependencyGraph: Codable {
14+
public struct SourceFileDependencyGraph {
1515
public static let sourceFileProvidesInterfaceSequenceNumber: Int = 0
1616
public static let sourceFileProvidesImplementationSequenceNumber: Int = 1
1717

18+
public var majorVersion: UInt64
19+
public var minorVersion: UInt64
20+
public var compilerVersionString: String
1821
private var allNodes: [Node]
1922

2023
public var sourceFileNodePair: (interface: Node, implementation: Node) {
@@ -47,20 +50,14 @@ public struct SourceFileDependencyGraph: Codable {
4750
}
4851
return true
4952
}
50-
51-
public init(contents: String) throws {
52-
let decoder = YAMLDecoder()
53-
self = try decoder.decode(Self.self, from: contents)
54-
assert(verify())
55-
}
5653
}
5754

58-
public enum DeclAspect: String, Codable {
55+
public enum DeclAspect: UInt64 {
5956
case interface, implementation
6057
}
6158

62-
public struct DependencyKey: Codable {
63-
public enum Kind: String, Codable {
59+
public struct DependencyKey {
60+
public enum Kind: UInt64 {
6461
case topLevel
6562
case nominal
6663
case potentialMember
@@ -88,9 +85,8 @@ public struct DependencyKey: Codable {
8885
}
8986
}
9087

91-
9288
extension SourceFileDependencyGraph {
93-
public struct Node: Codable {
89+
public struct Node {
9490
public var key: DependencyKey
9591
public var fingerprint: String?
9692
public var sequenceNumber: Int
@@ -111,3 +107,111 @@ extension SourceFileDependencyGraph {
111107
}
112108
}
113109
}
110+
111+
extension SourceFileDependencyGraph {
112+
private static let recordBlockId = 8
113+
114+
private enum RecordKind: UInt64 {
115+
case metadata = 1
116+
case sourceFileDepGraphNode
117+
case fingerprintNode
118+
case dependsOnDefinitionNode
119+
case identifierNode
120+
}
121+
122+
private enum ReadError: Error {
123+
case badMagic
124+
case noRecordBlock
125+
case malformedMetadataRecord
126+
case unexpectedMetadataRecord
127+
case malformedFingerprintRecord
128+
case malformedDependsOnDefinitionRecord
129+
case malformedIdentifierRecord
130+
case malformedSourceFileDepGraphNodeRecord
131+
case unknownRecord
132+
case unexpectedSubblock
133+
}
134+
135+
public init(data: Data) throws {
136+
// FIXME: visit blocks and records incrementally instead of reading the
137+
// entire file up front.
138+
let bitcode = try Bitcode(data: data)
139+
guard bitcode.signature == .init(string: "DEPS") else { throw ReadError.badMagic }
140+
141+
guard bitcode.elements.count == 1,
142+
case .block(let recordBlock) = bitcode.elements.first,
143+
recordBlock.id == Self.recordBlockId else { throw ReadError.noRecordBlock }
144+
145+
guard case .record(let metadataRecord) = recordBlock.elements.first,
146+
RecordKind(rawValue: metadataRecord.id) == .metadata,
147+
metadataRecord.fields.count == 2,
148+
case .blob(let compilerVersionBlob) = metadataRecord.payload,
149+
let compilerVersionString = String(data: compilerVersionBlob, encoding: .utf8)
150+
else { throw ReadError.malformedMetadataRecord }
151+
152+
self.majorVersion = metadataRecord.fields[0]
153+
self.minorVersion = metadataRecord.fields[1]
154+
self.compilerVersionString = compilerVersionString
155+
156+
var nodes: [Node] = []
157+
var node: Node? = nil
158+
var identifiers: [String] = [""] // The empty string is hardcoded as identifiers[0]
159+
var sequenceNumber = 0
160+
for element in recordBlock.elements.dropFirst() {
161+
guard case .record(let record) = element else { throw ReadError.unexpectedSubblock }
162+
guard let kind = RecordKind(rawValue: record.id) else { throw ReadError.unknownRecord }
163+
switch kind {
164+
case .metadata:
165+
throw ReadError.unexpectedMetadataRecord
166+
case .sourceFileDepGraphNode:
167+
if let node = node {
168+
nodes.append(node)
169+
}
170+
guard record.fields.count == 5,
171+
let nodeKind = DependencyKey.Kind(rawValue: record.fields[0]),
172+
let declAspect = DeclAspect(rawValue: record.fields[1]),
173+
record.fields[2] < identifiers.count,
174+
record.fields[3] < identifiers.count else {
175+
throw ReadError.malformedSourceFileDepGraphNodeRecord
176+
}
177+
let context = identifiers[Int(record.fields[2])]
178+
let identifier = identifiers[Int(record.fields[3])]
179+
let isProvides = record.fields[4] != 0
180+
node = Node(key: .init(kind: nodeKind,
181+
aspect: declAspect,
182+
context: context,
183+
name: identifier),
184+
fingerprint: nil,
185+
sequenceNumber: sequenceNumber,
186+
defsIDependUpon: [],
187+
isProvides: isProvides)
188+
sequenceNumber += 1
189+
case .fingerprintNode:
190+
guard node != nil,
191+
record.fields.count == 0,
192+
case .blob(let fingerprintBlob) = record.payload,
193+
let fingerprint = String(data: fingerprintBlob, encoding: .utf8) else {
194+
throw ReadError.malformedFingerprintRecord
195+
}
196+
node?.fingerprint = fingerprint
197+
case .dependsOnDefinitionNode:
198+
guard node != nil,
199+
record.fields.count == 1 else { throw ReadError.malformedDependsOnDefinitionRecord }
200+
node?.defsIDependUpon.append(Int(record.fields[0]))
201+
case .identifierNode:
202+
guard record.fields.count == 0,
203+
case .blob(let identifierBlob) = record.payload,
204+
let identifier = String(data: identifierBlob, encoding: .utf8) else {
205+
throw ReadError.malformedIdentifierRecord
206+
}
207+
identifiers.append(identifier)
208+
}
209+
}
210+
211+
if let node = node {
212+
nodes.append(node)
213+
}
214+
215+
self.allNodes = nodes
216+
}
217+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//===----------------- Bits.swift - Collection of bits --------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 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+
import Foundation
14+
15+
struct Bits: RandomAccessCollection {
16+
var buffer: Data
17+
18+
var startIndex: Int { return 0 }
19+
var endIndex: Int { return buffer.count * 8 }
20+
21+
subscript(index: Int) -> UInt8 {
22+
let byte = buffer[index / 8]
23+
return (byte >> UInt8(index % 8)) & 1
24+
}
25+
26+
func readBits(atOffset offset: Int, count: Int) -> UInt64 {
27+
precondition(count >= 0 && count <= 64)
28+
precondition(offset >= 0)
29+
precondition(offset &+ count >= offset)
30+
precondition(offset &+ count <= self.endIndex)
31+
32+
return buffer.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
33+
let upperBound = offset &+ count
34+
let topByteIndex = upperBound >> 3
35+
var result: UInt64 = 0
36+
if upperBound & 7 != 0 {
37+
let mask: UInt8 = (1 << UInt8(upperBound & 7)) &- 1
38+
result = UInt64(bytes[topByteIndex] & mask)
39+
}
40+
for i in ((offset >> 3)..<(upperBound >> 3)).reversed() {
41+
result <<= 8
42+
result |= UInt64(bytes[i])
43+
}
44+
if offset & 7 != 0 {
45+
result >>= UInt64(offset & 7)
46+
}
47+
return result
48+
}
49+
}
50+
51+
struct Cursor {
52+
enum Error: Swift.Error { case bufferOverflow }
53+
54+
let buffer: Bits
55+
private var offset: Int = 0
56+
57+
init(buffer: Bits) {
58+
self.buffer = buffer
59+
}
60+
61+
init(buffer: Data) {
62+
self.init(buffer: Bits(buffer: buffer))
63+
}
64+
65+
var isAtEnd: Bool {
66+
return offset == buffer.count
67+
}
68+
69+
func peek(_ count: Int) throws -> UInt64 {
70+
if buffer.count - offset < count { throw Error.bufferOverflow }
71+
return buffer.readBits(atOffset: offset, count: count)
72+
}
73+
74+
mutating func read(_ count: Int) throws -> UInt64 {
75+
defer { offset += count }
76+
return try peek(count)
77+
}
78+
79+
mutating func read(bytes count: Int) throws -> Data {
80+
precondition(count >= 0)
81+
precondition(offset & 0b111 == 0)
82+
let newOffset = offset &+ (count << 3)
83+
precondition(newOffset >= offset)
84+
if newOffset > buffer.count { throw Error.bufferOverflow }
85+
defer { offset = newOffset }
86+
return buffer.buffer.dropFirst(offset >> 3).prefix((newOffset - offset) >> 3)
87+
}
88+
89+
mutating func advance(toBitAlignment align: Int) throws {
90+
precondition(align > 0)
91+
precondition(offset &+ (align&-1) >= offset)
92+
precondition(align & (align &- 1) == 0)
93+
if offset % align == 0 { return }
94+
offset = (offset &+ align) & ~(align &- 1)
95+
if offset > buffer.count { throw Error.bufferOverflow }
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)