Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/SwiftDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_library(SwiftDriver
"ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift"

SwiftScan/DependencyGraphBuilder.swift
SwiftScan/Loader.swift
SwiftScan/SwiftScan.swift

Driver/CompilerMode.swift
Expand Down
176 changes: 176 additions & 0 deletions Sources/SwiftDriver/SwiftScan/Loader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//===------------------------ SwiftScan.swift -----------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import protocol Foundation.CustomNSError
import var Foundation.NSLocalizedDescriptionKey

#if os(Windows)
import WinSDK
#elseif os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
import Darwin
#else
import Glibc
#endif

internal enum Loader {
}

extension Loader {
internal enum Error: Swift.Error {
case `open`(String)
case close(String)
}
}

extension Loader.Error: CustomNSError {
public var errorUserInfo: [String:Any] {
return [NSLocalizedDescriptionKey: "\(self)"]
}
}

#if !os(Windows)
extension Loader {
private static func error() -> String? {
if let error: UnsafeMutablePointer<CChar> = dlerror() {
return String(cString: error)
}
return nil
}
}
#endif

extension Loader {
internal final class Handle {
#if os(Windows)
typealias ValueType = HMODULE
#else
typealias ValueType = UnsafeMutableRawPointer
#endif

fileprivate var value: ValueType?

init(value: ValueType) {
self.value = value
}

deinit {
precondition(value == nil,
"Handle must be closed or explicitly leaked before deinit")
}

public func close() throws {
if let handle = self.value {
#if os(Windows)
guard FreeLibrary(handle) else {
throw Loader.Error.close("FreeLibrary failure: \(GetLastError())")
}
#else
guard dlclose(handle) == 0 else {
throw Loader.Error.close(Loader.error() ?? "unknown error")
}
#endif
}
self.value = nil
}

public func leak() {
self.value = nil
}
}
}

extension Loader {
internal struct Flags: RawRepresentable, OptionSet {
public var rawValue: Int32

public init(rawValue: Int32) {
self.rawValue = rawValue
}
}
}

#if !os(Windows)
extension Loader.Flags {
public static var lazy: Loader.Flags {
Loader.Flags(rawValue: RTLD_LAZY)
}

public static var now: Loader.Flags {
Loader.Flags(rawValue: RTLD_NOW)
}

public static var local: Loader.Flags {
Loader.Flags(rawValue: RTLD_LOCAL)
}

public static var global: Loader.Flags {
Loader.Flags(rawValue: RTLD_GLOBAL)
}

// Platform-specific flags
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
public static var first: Loader.Flags {
Loader.Flags(rawValue: RTLD_FIRST)
}

public static var deepBind: Loader.Flags {
Loader.Flags(rawValue: 0)
}
#else
public static var first: Loader.Flags {
Loader.Flags(rawValue: 0)
}

#if os(Linux)
public static var deepBind: Loader.Flags {
Loader.Flags(rawValue: RTLD_DEEPBIND)
}
#else
public static var deepBind: Loader.Flags {
Loader.Flags(rawValue: 0)
}
#endif
#endif
}
#endif

extension Loader {
public static func load(_ path: String?, mode: Flags) throws -> Handle {
#if os(Windows)
guard let handle = path?.withCString(encodedAs: UTF16.self, LoadLibraryW) else {
throw Loader.Error.open("LoadLibraryW failure: \(GetLastError())")
}
#else
guard let handle = dlopen(path, mode.rawValue) else {
throw Loader.Error.open(Loader.error() ?? "unknown error")
}
#endif
return Handle(value: handle)
}

public static func lookup<T>(symbol: String, in module: Handle) -> T? {
#if os(Windows)
guard let pointer = GetProcAddress(module.value!, symbol) else {
return nil
}
#else
guard let pointer = dlsym(module.value!, symbol) else {
return nil
}
#endif
return unsafeBitCast(pointer, to: T.self)
}

public static func unload(_ handle: Handle) throws {
try handle.close()
}
}
15 changes: 6 additions & 9 deletions Sources/SwiftDriver/SwiftScan/SwiftScan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
import TSCBasic
import Foundation

import class TSCUtility.DLHandle
import func TSCUtility.dlopen
import func TSCUtility.dlsym
import struct TSCUtility.Version

public enum DependencyScanningError: Error, DiagnosticData {
Expand Down Expand Up @@ -64,7 +61,7 @@ internal final class SwiftScan {
let path: AbsolutePath

/// The handle to the dylib.
let dylib: DLHandle
let dylib: Loader.Handle

/// Lock protecting private state.
let lock: Lock = Lock()
Expand All @@ -78,9 +75,9 @@ internal final class SwiftScan {
@_spi(Testing) public init(dylib path: AbsolutePath) throws {
self.path = path
#if os(Windows)
self.dylib = try dlopen(path.pathString, mode: [])
self.dylib = try Loader.load(path.pathString, mode: [])
#else
self.dylib = try dlopen(path.pathString, mode: [.lazy, .local, .first])
self.dylib = try Loader.load(path.pathString, mode: [.lazy, .local, .first])
#endif
self.api = try swiftscan_functions_t(self.dylib)
guard let scanner = api.swiftscan_scanner_create() else {
Expand Down Expand Up @@ -274,13 +271,13 @@ internal final class SwiftScan {
}

private extension swiftscan_functions_t {
init(_ swiftscan: DLHandle) throws {
init(_ swiftscan: Loader.Handle) throws {
self.init()

// MARK: Optional Methods
// Future optional methods can be queried here
func loadOptional<T>(_ symbol: String) throws -> T? {
guard let sym: T = dlsym(swiftscan, symbol: symbol) else {
guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else {
return nil
}
return sym
Expand All @@ -307,7 +304,7 @@ private extension swiftscan_functions_t {

// MARK: Required Methods
func loadRequired<T>(_ symbol: String) throws -> T {
guard let sym: T = dlsym(swiftscan, symbol: symbol) else {
guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else {
throw DependencyScanningError.missingRequiredSymbol(symbol)
}
return sym
Expand Down