Skip to content

Commit 33534cb

Browse files
authored
Add a private copy of dlopen code to IndexStore (#304)
We deprecated the public API, but have this one client. This adds a private copy of the code that isn't deprecated to fix the warnings. In a future version, we can simply delete the public copy and keep this private one.
1 parent 3772eb7 commit 33534cb

File tree

1 file changed

+127
-6
lines changed

1 file changed

+127
-6
lines changed

Sources/TSCUtility/IndexStore.swift

Lines changed: 127 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ private final class IndexStoreAPIImpl {
212212
private let path: AbsolutePath
213213

214214
/// Handle of the dynamic library.
215-
private let dylib: DLHandle
215+
private let dylib: _DLHandle
216216

217217
/// The index store API functions.
218218
fileprivate let fn: indexstore_functions_t
@@ -234,14 +234,14 @@ private final class IndexStoreAPIImpl {
234234
public init(dylib path: AbsolutePath) throws {
235235
self.path = path
236236
#if os(Windows)
237-
let flags: DLOpenFlags = []
237+
let flags: _DLOpenFlags = []
238238
#else
239-
let flags: DLOpenFlags = [.lazy, .local, .first, .deepBind]
239+
let flags: _DLOpenFlags = [.lazy, .local, .first, .deepBind]
240240
#endif
241-
self.dylib = try dlopen(path.pathString, mode: flags)
241+
self.dylib = try _dlopen(path.pathString, mode: flags)
242242

243-
func dlsym_required<T>(_ handle: DLHandle, symbol: String) throws -> T {
244-
guard let sym: T = dlsym(handle, symbol: symbol) else {
243+
func dlsym_required<T>(_ handle: _DLHandle, symbol: String) throws -> T {
244+
guard let sym: T = _dlsym(handle, symbol: symbol) else {
245245
throw StringError("Missing required symbol: \(symbol)")
246246
}
247247
return sym
@@ -285,3 +285,124 @@ extension indexstore_string_ref_t {
285285
)!
286286
}
287287
}
288+
289+
// Private, non-deprecated copy of the dlopen code in `dlopen.swift` as we are planning to remove the public API in the next version.
290+
291+
import protocol Foundation.CustomNSError
292+
import var Foundation.NSLocalizedDescriptionKey
293+
import TSCLibc
294+
295+
private final class _DLHandle {
296+
#if os(Windows)
297+
typealias Handle = HMODULE
298+
#else
299+
typealias Handle = UnsafeMutableRawPointer
300+
#endif
301+
var rawValue: Handle? = nil
302+
303+
init(rawValue: Handle) {
304+
self.rawValue = rawValue
305+
}
306+
307+
deinit {
308+
precondition(rawValue == nil, "DLHandle must be closed or explicitly leaked before destroying")
309+
}
310+
311+
public func close() throws {
312+
if let handle = rawValue {
313+
#if os(Windows)
314+
guard FreeLibrary(handle) else {
315+
throw _DLError.close("Failed to FreeLibrary: \(GetLastError())")
316+
}
317+
#else
318+
guard dlclose(handle) == 0 else {
319+
throw _DLError.close(_dlerror() ?? "unknown error")
320+
}
321+
#endif
322+
}
323+
rawValue = nil
324+
}
325+
326+
public func leak() {
327+
rawValue = nil
328+
}
329+
}
330+
331+
private struct _DLOpenFlags: RawRepresentable, OptionSet {
332+
333+
#if !os(Windows)
334+
public static let lazy: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_LAZY)
335+
public static let now: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_NOW)
336+
public static let local: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_LOCAL)
337+
public static let global: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_GLOBAL)
338+
339+
// Platform-specific flags.
340+
#if canImport(Darwin)
341+
public static let first: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_FIRST)
342+
public static let deepBind: _DLOpenFlags = _DLOpenFlags(rawValue: 0)
343+
#else
344+
public static let first: _DLOpenFlags = _DLOpenFlags(rawValue: 0)
345+
#if os(Linux)
346+
public static let deepBind: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_DEEPBIND)
347+
#else
348+
public static let deepBind: _DLOpenFlags = _DLOpenFlags(rawValue: 0)
349+
#endif
350+
#endif
351+
#endif
352+
353+
public var rawValue: Int32
354+
355+
public init(rawValue: Int32) {
356+
self.rawValue = rawValue
357+
}
358+
}
359+
360+
private enum _DLError: Error {
361+
case `open`(String)
362+
case close(String)
363+
}
364+
365+
extension _DLError: CustomNSError {
366+
public var errorUserInfo: [String : Any] {
367+
return [NSLocalizedDescriptionKey: "\(self)"]
368+
}
369+
}
370+
371+
private func _dlsym<T>(_ handle: _DLHandle, symbol: String) -> T? {
372+
#if os(Windows)
373+
guard let ptr = GetProcAddress(handle.rawValue!, symbol) else {
374+
return nil
375+
}
376+
#else
377+
guard let ptr = dlsym(handle.rawValue!, symbol) else {
378+
return nil
379+
}
380+
#endif
381+
return unsafeBitCast(ptr, to: T.self)
382+
}
383+
384+
private func _dlopen(_ path: String?, mode: _DLOpenFlags) throws -> _DLHandle {
385+
#if os(Windows)
386+
guard let handle = path?.withCString(encodedAs: UTF16.self, LoadLibraryW) else {
387+
throw _DLError.open("LoadLibraryW failed: \(GetLastError())")
388+
}
389+
#else
390+
guard let handle = TSCLibc.dlopen(path, mode.rawValue) else {
391+
throw _DLError.open(_dlerror() ?? "unknown error")
392+
}
393+
#endif
394+
return _DLHandle(rawValue: handle)
395+
}
396+
397+
private func _dlclose(_ handle: _DLHandle) throws {
398+
try handle.close()
399+
}
400+
401+
#if !os(Windows)
402+
private func _dlerror() -> String? {
403+
if let err: UnsafeMutablePointer<Int8> = dlerror() {
404+
return String(cString: err)
405+
}
406+
return nil
407+
}
408+
#endif

0 commit comments

Comments
 (0)