Skip to content

[5.0][SourceKit/SwiftLang] Keep sourcekitd response alive for variant lifetime #20375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
2 changes: 1 addition & 1 deletion tools/SourceKit/tools/swift-lang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if(NOT SWIFT_SOURCEKIT_USE_INPROC_LIBRARY AND SWIFT_BUILD_STDLIB)
UIDs.swift.gyb

DEPENDS ${DEPENDS_LIST}
SWIFT_MODULE_DEPENDS_OSX Darwin
SWIFT_MODULE_DEPENDS_OSX Darwin Foundation
PRIVATE_LINK_LIBRARIES ${SOURCEKITD_LINK_LIBS}
SWIFT_COMPILE_FLAGS ${EXTRA_COMPILE_FLAGS}
INSTALL_IN_COMPONENT ${INSTALLED_COMP}
Expand Down
56 changes: 44 additions & 12 deletions tools/SourceKit/tools/swift-lang/SourceKitdResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@
// This file provides convenient APIs to interpret a SourceKitd response.
//===----------------------------------------------------------------------===//

import Foundation
import sourcekitd

public class SourceKitdResponse: CustomStringConvertible {

public struct Dictionary: CustomStringConvertible, CustomReflectable {
// The lifetime of this sourcekitd_variant_t is tied to the response it came
// from, so keep a reference to the response too.
private let dict: sourcekitd_variant_t
private let context: SourceKitdResponse

public init(dict: sourcekitd_variant_t) {

public init(dict: sourcekitd_variant_t, context: SourceKitdResponse) {
assert(sourcekitd_variant_get_type(dict).rawValue ==
SOURCEKITD_VARIANT_TYPE_DICTIONARY.rawValue)
self.dict = dict
self.context = context
}

public func getString(_ key: SourceKitdUID) -> String {
Expand All @@ -47,12 +53,21 @@ public class SourceKitdResponse: CustomStringConvertible {

public func getArray(_ key: SourceKitdUID) -> Array {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
return Array(arr: value)
return Array(arr: value, context: context)
}

public func getDictionary(_ key: SourceKitdUID) -> Dictionary {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
return Dictionary(dict: value)
return Dictionary(dict: value, context: context)
}

public func getData(_ key: SourceKitdUID) -> Data {
let value = sourcekitd_variant_dictionary_get_value(dict, key.uid)
let size = sourcekitd_variant_data_get_size(value)
guard let ptr = sourcekitd_variant_data_get_ptr(value), size > 0 else {
return Data()
}
return Data(bytes: ptr, count: size)
}

public func getOptional(_ key: SourceKitdUID) -> Variant? {
Expand All @@ -61,7 +76,7 @@ public class SourceKitdResponse: CustomStringConvertible {
SOURCEKITD_VARIANT_TYPE_NULL.rawValue {
return nil
}
return Variant(val: value)
return Variant(val: value, context: context)
}

public var description: String {
Expand All @@ -74,17 +89,21 @@ public class SourceKitdResponse: CustomStringConvertible {
}

public struct Array: CustomStringConvertible {
// The lifetime of this sourcekitd_variant_t is tied to the response it came
// from, so keep a reference to the response too.
private let arr: sourcekitd_variant_t
private let context: SourceKitdResponse

public var count: Int {
let count = sourcekitd_variant_array_get_count(arr)
return Int(count)
}

public init(arr: sourcekitd_variant_t) {
public init(arr: sourcekitd_variant_t, context: SourceKitdResponse) {
assert(sourcekitd_variant_get_type(arr).rawValue ==
SOURCEKITD_VARIANT_TYPE_ARRAY.rawValue)
self.arr = arr
self.context = context
}

public func getString(_ index: Int) -> String {
Expand All @@ -109,20 +128,21 @@ public class SourceKitdResponse: CustomStringConvertible {

public func getArray(_ index: Int) -> Array {
let value = sourcekitd_variant_array_get_value(arr, index)
return Array(arr: value)
return Array(arr: value, context: context)
}

public func getDictionary(_ index: Int) -> Dictionary {
let value = sourcekitd_variant_array_get_value(arr, index)
return Dictionary(dict: value)
return Dictionary(dict: value, context: context)
}

public func enumerate(_ applier: (_ index: Int, _ value: Variant) -> Bool) {
// The block passed to sourcekit_variant_array_apply() does not actually
// escape, it's synchronous and not called after returning.
let context = self.context
withoutActuallyEscaping(applier) { escapingApplier in
_ = sourcekitd_variant_array_apply(arr) { (index, elem) -> Bool in
return escapingApplier(Int(index), Variant(val: elem))
return escapingApplier(Int(index), Variant(val: elem, context: context))
}
}
}
Expand All @@ -134,10 +154,14 @@ public class SourceKitdResponse: CustomStringConvertible {
}

public struct Variant: CustomStringConvertible {
// The lifetime of this sourcekitd_variant_t is tied to the response it came
// from, so keep a reference to the response too.
private let val: sourcekitd_variant_t
fileprivate let context: SourceKitdResponse

fileprivate init(val: sourcekitd_variant_t) {
fileprivate init(val: sourcekitd_variant_t, context: SourceKitdResponse) {
self.val = val
self.context = context
}

public func getString() -> String {
Expand Down Expand Up @@ -167,11 +191,19 @@ public class SourceKitdResponse: CustomStringConvertible {
}

public func getArray() -> Array {
return Array(arr: val)
return Array(arr: val, context: context)
}

public func getDictionary() -> Dictionary {
return Dictionary(dict: val)
return Dictionary(dict: val, context: context)
}

public func getData() -> Data {
let size = sourcekitd_variant_data_get_size(val)
guard let ptr = sourcekitd_variant_data_get_ptr(val), size > 0 else {
return Data()
}
return Data(bytes: ptr, count: size)
}

public var description: String {
Expand All @@ -182,7 +214,7 @@ public class SourceKitdResponse: CustomStringConvertible {
private let resp: sourcekitd_response_t

public var value: Dictionary {
return Dictionary(dict: sourcekitd_response_get_value(resp))
return Dictionary(dict: sourcekitd_response_get_value(resp), context: self)
}

/// Copies the raw bytes of the JSON description of this documentation item.
Expand Down