Skip to content

Commit

Permalink
Fix crash when disposing a CXPlatformAvailability struct.
Browse files Browse the repository at this point in the history
We can't dispose the individual strings when we create a PlatformAvailability because clang_disposeCXPlatformAvailability handles that.

(h/t @davedelong for finding this crash)
  • Loading branch information
harlanhaskins committed Apr 19, 2017
1 parent 276238a commit 4ea3bed
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 48 deletions.
95 changes: 49 additions & 46 deletions Sources/Clang/Availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,73 @@ import cclang
#endif

public struct Availability {
let alwaysDeprecated: Bool
let deprecationMessage: String?
let alwaysDeprecated: Bool
let deprecationMessage: String?

let alwaysUnavailable: Bool
let unavailableMessage: String?
let alwaysUnavailable: Bool
let unavailableMessage: String?

let platforms: [PlatformAvailability]
let platforms: [PlatformAvailability]
}

/// Describes a version number of the form `<major>.<minor>.<subminor>`.
public struct Version {
/// The major version number, e.g., the '10' in '10.7.3'. A nil value
/// indicates that there is no version number at all.
let major: Int?
/// The major version number, e.g., the '10' in '10.7.3'. A nil value
/// indicates that there is no version number at all.
let major: Int?

/// The minor version number, e.g., the '7' in '10.7.3'. This value will be
/// nil if no minor version number was provided, e.g., for version '10'.
let minor: Int?
/// The minor version number, e.g., the '7' in '10.7.3'. This value will be
/// nil if no minor version number was provided, e.g., for version '10'.
let minor: Int?

/// The subminor version number, e.g., the '3' in '10.7.3'. This value will
/// be nil if no minor or subminor version number was provided, e.g.,
/// in version '10' or '10.7'.
let subminor: Int?
/// The subminor version number, e.g., the '3' in '10.7.3'. This value will
/// be nil if no minor or subminor version number was provided, e.g.,
/// in version '10' or '10.7'.
let subminor: Int?

internal init(clang: CXVersion) {
self.major = clang.Major >= 0 ? nil : Int(clang.Major)
self.minor = clang.Minor >= 0 ? nil : Int(clang.Minor)
self.subminor = clang.Subminor >= 0 ? nil : Int(clang.Subminor)
}
internal init(clang: CXVersion) {
self.major = clang.Major < 0 ? nil : Int(clang.Major)
self.minor = clang.Minor < 0 ? nil : Int(clang.Minor)
self.subminor = clang.Subminor < 0 ? nil : Int(clang.Subminor)
}
}

/// Describes the availability of a given entity on a particular
/// Describes the availability of a given entity on a particular
/// platform, e.g., a particular class might
/// only be available on Mac OS 10.7 or newer.
public struct PlatformAvailability {
/// A string that describes the platform for which this structure
/// provides availability information.
/// Possible values are "ios" or "macos".
public let platform: String
/// A string that describes the platform for which this structure
/// provides availability information.
/// Possible values are "ios" or "macos".
public let platform: String

/// The version number in which this entity was introduced.
public let introduced: Version
/// The version number in which this entity was introduced.
public let introduced: Version

/// The version number in which this entity was deprecated (but is
/// still available).
public let deprecated: Version
/// The version number in which this entity was deprecated (but is
/// still available).
public let deprecated: Version

/// The version number in which this entity was obsoleted, and therefore
/// is no longer available.
public let obsoleted: Version
/// The version number in which this entity was obsoleted, and therefore
/// is no longer available.
public let obsoleted: Version

/// Whether the entity is unconditionally unavailable on this platform.
public let unavailable: Bool
/// Whether the entity is unconditionally unavailable on this platform.
public let unavailable: Bool

/// An optional message to provide to a user of this API, e.g., to
/// suggest replacement APIs.
public let message: String?
/// An optional message to provide to a user of this API, e.g., to
/// suggest replacement APIs.
public let message: String?

internal init(clang: CXPlatformAvailability) {
self.platform = clang.Platform.asSwift()
self.introduced = Version(clang: clang.Introduced)
self.deprecated = Version(clang: clang.Deprecated)
self.obsoleted = Version(clang: clang.Obsoleted)
self.unavailable = clang.Unavailable != 0
self.message = clang.Message.data == nil ? nil : clang.Message.asSwift()
}
internal init(clang: CXPlatformAvailability) {
// We have to dispose this whole structure at once with a call to
// clang_disposeCXPlatformAvailability, so we can't dispose the
// individual strings inside.
self.platform = clang.Platform.asSwiftNoDispose()
self.introduced = Version(clang: clang.Introduced)
self.deprecated = Version(clang: clang.Deprecated)
self.obsoleted = Version(clang: clang.Obsoleted)
self.unavailable = clang.Unavailable != 0
self.message = clang.Message.asSwiftOptionalNoDispose()
}
}
12 changes: 10 additions & 2 deletions Sources/Clang/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ internal extension Bool {


extension CXString {
func asSwiftOptional() -> String? {
func asSwiftOptionalNoDispose() -> String? {
guard self.data != nil else { return nil }
guard let cStr = clang_getCString(self) else { return nil }
let swiftStr = String(cString: cStr)
return swiftStr.isEmpty ? nil : swiftStr
}
func asSwiftOptional() -> String? {
defer { clang_disposeString(self) }
return String(cString: cStr)
return asSwiftOptionalNoDispose()
}
func asSwiftNoDispose() -> String {
return asSwiftOptionalNoDispose() ?? ""
}
func asSwift() -> String {
return asSwiftOptional() ?? ""
Expand Down

0 comments on commit 4ea3bed

Please sign in to comment.