Skip to content

Commit e20a65c

Browse files
committed
[CompilerPlugin] Remove dependency to Foundation.FileHandle
Instead, use read(2) and write(2) via Darin/Glibc/UCRT
1 parent 61c7338 commit e20a65c

File tree

1 file changed

+67
-56
lines changed

1 file changed

+67
-56
lines changed

Sources/SwiftCompilerPlugin/CompilerPlugin.swift

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@
1414

1515
#if swift(>=6.0)
1616
public import SwiftSyntaxMacros
17-
private import Foundation
1817
@_spi(PluginMessage) private import SwiftCompilerPluginMessageHandling
18+
#if canImport(Darwin)
19+
private import Darwin
20+
#elseif canImport(Glibc)
21+
private import Glibc
22+
#elseif canImport(ucrt)
23+
private import ucrt
24+
#endif
1925
#else
2026
import SwiftSyntaxMacros
21-
import Foundation
2227
@_spi(PluginMessage) import SwiftCompilerPluginMessageHandling
23-
#endif
24-
25-
#if os(Windows)
26-
#if swift(>=6.0)
27-
private import ucrt
28-
#else
28+
#if canImport(Darwin)
29+
import Darwin
30+
#elseif canImport(Glibc)
31+
import Glibc
32+
#elseif canImport(ucrt)
2933
import ucrt
3034
#endif
3135
#endif
@@ -86,7 +90,7 @@ extension CompilerPlugin {
8690
}
8791
}
8892

89-
let pluginPath = CommandLine.arguments.first ?? Bundle.main.executablePath ?? ProcessInfo.processInfo.processName
93+
let pluginPath = CommandLine.arguments.first ?? "<unknown>"
9094
throw CompilerPluginError(
9195
message:
9296
"macro implementation type '\(moduleName).\(typeName)' could not be found in executable plugin '\(pluginPath)'"
@@ -149,8 +153,8 @@ extension CompilerPlugin {
149153

150154
// Open a message channel for communicating with the plugin host.
151155
let connection = PluginHostConnection(
152-
inputStream: FileHandle(fileDescriptor: inputFD),
153-
outputStream: FileHandle(fileDescriptor: outputFD)
156+
inputStream: inputFD,
157+
outputStream: outputFD
154158
)
155159

156160
// Handle messages from the host until the input stream is closed,
@@ -180,56 +184,40 @@ extension CompilerPlugin {
180184
}
181185

182186
internal struct PluginHostConnection: MessageConnection {
183-
fileprivate let inputStream: FileHandle
184-
fileprivate let outputStream: FileHandle
187+
fileprivate let inputStream: CInt
188+
fileprivate let outputStream: CInt
185189

186190
func sendMessage<TX: Encodable>(_ message: TX) throws {
187191
// Encode the message as JSON.
188-
let payload = try PluginMessageJSON.encode(message).withUnsafeBufferPointer { buffer in
189-
Data(buffer: buffer)
190-
}
192+
let payload = try PluginMessageJSON.encode(message)
191193

192194
// Write the header (a 64-bit length field in little endian byte order).
193-
var count = UInt64(payload.count).littleEndian
194-
let header = Swift.withUnsafeBytes(of: &count) { Data($0) }
195-
precondition(header.count == 8)
195+
let count = payload.count
196+
var header = UInt64(count).littleEndian
197+
198+
try withUnsafeBytes(of: &header) { buffer in
199+
precondition(buffer.count == 8)
200+
try _write(outputStream, contentsOf: buffer)
201+
}
196202

197-
// Write the header and payload.
198-
try outputStream._write(contentsOf: header)
199-
try outputStream._write(contentsOf: payload)
203+
try payload.withUnsafeBytes { buffer in
204+
try _write(outputStream, contentsOf: buffer)
205+
}
200206
}
201207

202208
func waitForNextMessage<RX: Decodable>(_ ty: RX.Type) throws -> RX? {
203209
// Read the header (a 64-bit length field in little endian byte order).
204-
guard
205-
let header = try inputStream._read(upToCount: 8),
206-
header.count != 0
207-
else {
208-
return nil
209-
}
210-
guard header.count == 8 else {
211-
throw PluginMessageError.truncatedHeader
212-
}
213-
214-
// Decode the count.
215-
let count = header.withUnsafeBytes {
216-
UInt64(littleEndian: $0.loadUnaligned(as: UInt64.self))
210+
let count = try _reading(inputStream, count: 8) { buffer in
211+
UInt64(littleEndian: buffer.loadUnaligned(as: UInt64.self))
217212
}
218213
guard count >= 2 else {
219214
throw PluginMessageError.invalidPayloadSize
220215
}
221216

222217
// Read the JSON payload.
223-
guard
224-
let payload = try inputStream._read(upToCount: Int(count)),
225-
payload.count == count
226-
else {
227-
throw PluginMessageError.truncatedPayload
228-
}
229-
230-
// Decode and return the message.
231-
return try payload.withUnsafeBytes { buffer in
232-
return try PluginMessageJSON.decode(RX.self, from: buffer.bindMemory(to: UInt8.self))
218+
return try _reading(inputStream, count: Int(count)) { buffer in
219+
// Decode and return the message.
220+
try PluginMessageJSON.decode(RX.self, from: buffer.bindMemory(to: UInt8.self))
233221
}
234222
}
235223

@@ -240,22 +228,45 @@ internal struct PluginHostConnection: MessageConnection {
240228
}
241229
}
242230

243-
private extension FileHandle {
244-
func _write(contentsOf data: Data) throws {
245-
if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
246-
return try self.write(contentsOf: data)
247-
} else {
248-
return self.write(data)
231+
func _write(_ fd: CInt, contentsOf buffer: UnsafeRawBufferPointer) throws {
232+
var ptr = buffer.baseAddress!
233+
var remaining = buffer.count
234+
while remaining > 0 {
235+
switch write(fd, ptr, remaining) {
236+
case 0:
237+
throw CompilerPluginError(message: "write(2) closed")
238+
case -1:
239+
let err = String(cString: strerror(errno))
240+
throw CompilerPluginError(message: "read(2) failed: \(err)")
241+
case let result:
242+
ptr += Int(result)
243+
remaining -= Int(result)
249244
}
250245
}
246+
}
247+
248+
func _reading<T>(_ fd: CInt, count: Int, _ fn: (UnsafeRawBufferPointer) throws -> T) throws -> T {
249+
guard count > 0 else {
250+
return try fn(UnsafeRawBufferPointer(start: nil, count: 0))
251+
}
252+
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: count, alignment: 1)
253+
defer { buffer.deallocate() }
251254

252-
func _read(upToCount count: Int) throws -> Data? {
253-
if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
254-
return try self.read(upToCount: count)
255-
} else {
256-
return self.readData(ofLength: 8)
255+
var ptr = buffer.baseAddress!
256+
var remaining = buffer.count
257+
while remaining > 0 {
258+
switch read(fd, ptr, remaining) {
259+
case 0:
260+
throw CompilerPluginError(message: "read(2) closed")
261+
case -1:
262+
let err = String(cString: strerror(errno))
263+
throw CompilerPluginError(message: "read(2) failed: \(err)")
264+
case let result:
265+
ptr += Int(result)
266+
remaining -= Int(result)
257267
}
258268
}
269+
return try fn(UnsafeRawBufferPointer(buffer))
259270
}
260271

261272
struct CompilerPluginError: Error, CustomStringConvertible {

0 commit comments

Comments
 (0)