diff --git a/bin/swiftshield b/bin/swiftshield index 06eb5dc..aa0c05e 100755 Binary files a/bin/swiftshield and b/bin/swiftshield differ diff --git a/swiftshield-Sources/AutomaticSwiftShield.swift b/swiftshield-Sources/AutomaticSwiftShield.swift index f65bf1e..3f88077 100644 --- a/swiftshield-Sources/AutomaticSwiftShield.swift +++ b/swiftshield-Sources/AutomaticSwiftShield.swift @@ -37,6 +37,7 @@ final class AutomaticSwiftShield: Protector { let modules = projectBuilder.getModulesAndCompilerArguments() let modulesToObfuscate = modules.filter { modulesToIgnore.contains($0.name) == false } let obfuscationData = index(modules: modulesToObfuscate) + obfuscationData.storyboardToObfuscate = modulesToObfuscate.flatMap { $0.xibFiles } if obfuscationData.obfuscationDict.isEmpty { Logger.log(.foundNothingError) exit(error: true) @@ -52,7 +53,7 @@ extension AutomaticSwiftShield { let obfuscationData = ObfuscationData() var fileDataArray: [(file: File, module: Module)] = [] for module in modules { - for file in module.files { + for file in module.sourceFiles { fileDataArray.append((file, module)) } } @@ -115,7 +116,7 @@ extension AutomaticSwiftShield { let dict = SKApi.sourcekitd_response_get_value(indexResponse) SK.recurseOver(childID: SK.entitiesID, resp: dict, block: { dict in let kind = dict.getUUIDString(key: SK.kindID) - guard SK.referenceType(kind: kind) != nil else { + guard let type = SK.referenceType(kind: kind) else { return } guard let usr = dict.getString(key: SK.usrID), let name = dict.getString(key: SK.nameID)?.trueName else { @@ -127,10 +128,10 @@ extension AutomaticSwiftShield { //Operators only get indexed as such if they are declared in a global scope //Unfortunately, most people use public static func //So we avoid obfuscating methods with small names to prevent obfuscating operators. - guard SK.referenceType(kind: kind) != .method || name.count > 4 else { + if type == .method && name.count <= 4 { return } - guard self.isReferencingInternalMethod(kind: kind, dict: dict, obfuscationData: obfuscationData, sourceKit: SK) == false else { + guard self.isReferencingInternal(type: type, kind: kind, dict: dict, obfuscationData: obfuscationData, sourceKit: SK) == false else { return } let newName = obfuscationData.obfuscationDict[name] ?? name @@ -143,15 +144,15 @@ extension AutomaticSwiftShield { overwriteFiles(obfuscationData: obfuscationData) } - private func isReferencingInternalMethod(kind: String, dict: sourcekitd_variant_t, obfuscationData: ObfuscationData, sourceKit: SourceKit) -> Bool { - guard sourceKit.referenceType(kind: kind) == .method else { + private func isReferencingInternal(type: SourceKit.DeclarationType, kind: String, dict: sourcekitd_variant_t, obfuscationData: ObfuscationData, sourceKit: SourceKit) -> Bool { + guard type == .method || type == .property else { return false } guard let usr = dict.getString(key: sourceKit.usrID) else { return false } if let relDict = obfuscationData.usrRelationDict[usr], relDict.data != dict.data { - return isReferencingInternalMethod(kind: kind, dict: relDict, obfuscationData: obfuscationData, sourceKit: sourceKit) + return isReferencingInternal(type: type, kind: kind, dict: relDict, obfuscationData: obfuscationData, sourceKit: sourceKit) } var isReference = false sourceKit.recurseOver(childID: sourceKit.relatedID, resp: dict) { dict in @@ -164,7 +165,7 @@ extension AutomaticSwiftShield { if obfuscationData.usrDict.contains(usr) == false { isReference = true } else if let relDict = obfuscationData.usrRelationDict[usr] { - isReference = self.isReferencingInternalMethod(kind: kind, dict: relDict, obfuscationData: obfuscationData, sourceKit: sourceKit) + isReference = self.isReferencingInternal(type: type, kind: kind, dict: relDict, obfuscationData: obfuscationData, sourceKit: sourceKit) } } return isReference @@ -205,9 +206,9 @@ extension AutomaticSwiftShield { currentCharIndex += 1 } } - let joined = charArray.joined() + let obfuscatedFile = charArray.joined() do { - try joined.write(toFile: file.path, atomically: false, encoding: String.Encoding.utf8) + try obfuscatedFile.write(toFile: file.path, atomically: false, encoding: .utf8) } catch { Logger.log(.fatal(error: error.localizedDescription)) exit(error: true) diff --git a/swiftshield-Sources/Logger.swift b/swiftshield-Sources/Logger.swift index 62c4ae4..108ce06 100644 --- a/swiftshield-Sources/Logger.swift +++ b/swiftshield-Sources/Logger.swift @@ -81,7 +81,7 @@ enum LogType { case .finished: return "Finished." case .version: - return "SwiftShield 3.1.1" + return "SwiftShield 3.2.0" case .verbose: return "Verbose Mode" case .mode: diff --git a/swiftshield-Sources/ManualSwiftShield.swift b/swiftshield-Sources/ManualSwiftShield.swift index 4f54d9b..adf0fb5 100644 --- a/swiftshield-Sources/ManualSwiftShield.swift +++ b/swiftshield-Sources/ManualSwiftShield.swift @@ -13,6 +13,7 @@ final class ManualSwiftShield: Protector { let files = getSourceFiles() Logger.log(.scanningDeclarations) var obfsData = ObfuscationData() + obfsData.storyboardToObfuscate = getStoryboardsAndXibs() files.forEach { protect(file: $0, obfsData: &obfsData) } return obfsData } diff --git a/swiftshield-Sources/Module.swift b/swiftshield-Sources/Module.swift index e39f0fc..f948673 100644 --- a/swiftshield-Sources/Module.swift +++ b/swiftshield-Sources/Module.swift @@ -2,12 +2,14 @@ import Cocoa struct Module { let name: String - let files: [File] + let sourceFiles: [File] + let xibFiles: [File] let compilerArguments: [String] - init(name: String, files: [File], compilerArguments: [String]) { + init(name: String, sourceFiles: [File], xibFiles: [File], compilerArguments: [String]) { self.name = name - self.files = files + self.sourceFiles = sourceFiles + self.xibFiles = xibFiles self.compilerArguments = compilerArguments } } diff --git a/swiftshield-Sources/ObfuscationData.swift b/swiftshield-Sources/ObfuscationData.swift index 772e84c..f511733 100644 --- a/swiftshield-Sources/ObfuscationData.swift +++ b/swiftshield-Sources/ObfuscationData.swift @@ -7,7 +7,8 @@ final class ObfuscationData { var usrRelationDict: [String: sourcekitd_variant_t] = [:] var indexedFiles: [(File,sourcekitd_response_t)] = [] var allObfuscatedNames: Set = [] - + var storyboardToObfuscate: [File] = [] + func add(reference: ReferenceData, toFile file: File) { if referencesDict[file] == nil { referencesDict[file] = [] diff --git a/swiftshield-Sources/Protector.swift b/swiftshield-Sources/Protector.swift index 95958e1..b3ba88b 100644 --- a/swiftshield-Sources/Protector.swift +++ b/swiftshield-Sources/Protector.swift @@ -23,7 +23,7 @@ class Protector { func protectStoryboards(data obfuscationData: ObfuscationData) { Logger.log(.overwritingStoryboards) - for file in getStoryboardsAndXibs() { + for file in obfuscationData.storyboardToObfuscate { Logger.log(.checking(file: file)) //TODO: We can do the index approach here as well instead of replacingOccurences. let data = try! String(contentsOfFile: file.path, encoding: .utf8) @@ -38,6 +38,9 @@ class Protector { } Logger.log(.protectedReference(originalName: `class`, protectedName: protectedClass)) overwrittenData = overwrittenData.replacingOccurrences(of: Storyboard.customClass(class: `class`), with: Storyboard.customClass(class: protectedClass)) + if `class`.count > 4 { + overwrittenData = overwrittenData.replacingOccurrences(of: Storyboard.actionSelector(method: `class`), with: Storyboard.actionSelector(method: protectedClass)) + } } guard overwrittenData != data else { Logger.log(.fileNotModified(file: file)) diff --git a/swiftshield-Sources/SKAPI.swift b/swiftshield-Sources/SKAPI.swift index a551ac9..5b33f18 100644 --- a/swiftshield-Sources/SKAPI.swift +++ b/swiftshield-Sources/SKAPI.swift @@ -1,6 +1,18 @@ +// Created by John Holdsworth on 19/12/2015. +// Copyright © 2015 John Holdsworth. All rights reserved. +// +// $Id: //depot/Refactorator/refactord/SourceKit.swift#25 $ +// +// Repo: https://github.com/johnno1962/Refactorator +// + +/** Thanks to: https://github.com/jpsim/SourceKitten/blob/master/Source/SourceKittenFramework/library_wrapper_sourcekitd.swift **/ + import Foundation -class SKAPI { +let SKApi = SKAPI() + +final class SKAPI { static var verbose = false @@ -19,51 +31,51 @@ class SKAPI { internal let sourcekitd_request_dictionary_set_stringbuf: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, UnsafePointer, Int) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_stringbuf") internal let sourcekitd_request_dictionary_set_int64: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, Int64) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_int64") internal let sourcekitd_request_dictionary_set_uid: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, sourcekitd_uid_t) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_uid") - internal let sourcekitd_request_array_create: @convention(c) (UnsafePointer?, Int) -> (sourcekitd_object_t!) = library.load(symbol: "sourcekitd_request_array_create") + internal let sourcekitd_request_array_create: @convention(c) (UnsafePointer?, Int) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_array_create") internal let sourcekitd_request_array_set_value: @convention(c) (sourcekitd_object_t, Int, sourcekitd_object_t) -> () = library.load(symbol: "sourcekitd_request_array_set_value") internal let sourcekitd_request_array_set_string: @convention(c) (sourcekitd_object_t, Int, UnsafePointer) -> () = library.load(symbol: "sourcekitd_request_array_set_string") internal let sourcekitd_request_array_set_stringbuf: @convention(c) (sourcekitd_object_t, Int, UnsafePointer, Int) -> () = library.load(symbol: "sourcekitd_request_array_set_stringbuf") internal let sourcekitd_request_array_set_int64: @convention(c) (sourcekitd_object_t, Int, Int64) -> () = library.load(symbol: "sourcekitd_request_array_set_int64") internal let sourcekitd_request_array_set_uid: @convention(c) (sourcekitd_object_t, Int, sourcekitd_uid_t) -> () = library.load(symbol: "sourcekitd_request_array_set_uid") - internal let sourcekitd_request_int64_create: @convention(c) (Int64) -> (sourcekitd_object_t!) = library.load(symbol: "sourcekitd_request_int64_create") - internal let sourcekitd_request_string_create: @convention(c) (UnsafePointer) -> (sourcekitd_object_t!) = library.load(symbol: "sourcekitd_request_string_create") - internal let sourcekitd_request_uid_create: @convention(c) (sourcekitd_uid_t) -> (sourcekitd_object_t!) = library.load(symbol: "sourcekitd_request_uid_create") - internal let sourcekitd_request_create_from_yaml: @convention(c) (UnsafePointer, UnsafeMutablePointer?>?) -> (sourcekitd_object_t!) = library.load(symbol: "sourcekitd_request_create_from_yaml") + internal let sourcekitd_request_int64_create: @convention(c) (Int64) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_int64_create") + internal let sourcekitd_request_string_create: @convention(c) (UnsafePointer) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_string_create") + internal let sourcekitd_request_uid_create: @convention(c) (sourcekitd_uid_t) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_uid_create") + internal let sourcekitd_request_create_from_yaml: @convention(c) (UnsafePointer, UnsafeMutablePointer?>?) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_create_from_yaml") internal let sourcekitd_request_description_dump: @convention(c) (sourcekitd_object_t) -> () = library.load(symbol: "sourcekitd_request_description_dump") - internal let sourcekitd_request_description_copy: @convention(c) (sourcekitd_object_t) -> (UnsafeMutablePointer!) = library.load(symbol: "sourcekitd_request_description_copy") + internal let sourcekitd_request_description_copy: @convention(c) (sourcekitd_object_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_request_description_copy") internal let sourcekitd_response_dispose: @convention(c) (sourcekitd_response_t) -> () = library.load(symbol: "sourcekitd_response_dispose") internal let sourcekitd_response_is_error: @convention(c) (sourcekitd_response_t) -> (Bool) = library.load(symbol: "sourcekitd_response_is_error") internal let sourcekitd_response_error_get_kind: @convention(c) (sourcekitd_response_t) -> (sourcekitd_error_t) = library.load(symbol: "sourcekitd_response_error_get_kind") - internal let sourcekitd_response_error_get_description: @convention(c) (sourcekitd_response_t) -> (UnsafePointer!) = library.load(symbol: "sourcekitd_response_error_get_description") + internal let sourcekitd_response_error_get_description: @convention(c) (sourcekitd_response_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_response_error_get_description") internal let sourcekitd_response_get_value: @convention(c) (sourcekitd_response_t) -> (sourcekitd_variant_t) = library.load(symbol: "sourcekitd_response_get_value") internal let sourcekitd_variant_get_type: @convention(c) (sourcekitd_variant_t) -> (sourcekitd_variant_type_t) = library.load(symbol: "sourcekitd_variant_get_type") internal let sourcekitd_variant_dictionary_get_value: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (sourcekitd_variant_t) = library.load(symbol: "sourcekitd_variant_dictionary_get_value") - internal let sourcekitd_variant_dictionary_get_string: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (UnsafePointer!) = library.load(symbol: "sourcekitd_variant_dictionary_get_string") + internal let sourcekitd_variant_dictionary_get_string: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_variant_dictionary_get_string") internal let sourcekitd_variant_dictionary_get_int64: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (Int64) = library.load(symbol: "sourcekitd_variant_dictionary_get_int64") internal let sourcekitd_variant_dictionary_get_bool: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (Bool) = library.load(symbol: "sourcekitd_variant_dictionary_get_bool") - internal let sourcekitd_variant_dictionary_get_uid: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (sourcekitd_uid_t!) = library.load(symbol: "sourcekitd_variant_dictionary_get_uid") + internal let sourcekitd_variant_dictionary_get_uid: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_variant_dictionary_get_uid") internal let sourcekitd_variant_dictionary_apply_f: @convention(c) (sourcekitd_variant_t, @escaping sourcekitd_variant_dictionary_applier_f_t, UnsafeMutableRawPointer?) -> (Bool) = library.load(symbol: "sourcekitd_variant_dictionary_apply_f") internal let sourcekitd_variant_array_get_count: @convention(c) (sourcekitd_variant_t) -> (Int) = library.load(symbol: "sourcekitd_variant_array_get_count") internal let sourcekitd_variant_array_get_value: @convention(c) (sourcekitd_variant_t, Int) -> (sourcekitd_variant_t) = library.load(symbol: "sourcekitd_variant_array_get_value") - internal let sourcekitd_variant_array_get_string: @convention(c) (sourcekitd_variant_t, Int) -> (UnsafePointer!) = library.load(symbol: "sourcekitd_variant_array_get_string") + internal let sourcekitd_variant_array_get_string: @convention(c) (sourcekitd_variant_t, Int) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_variant_array_get_string") internal let sourcekitd_variant_array_get_int64: @convention(c) (sourcekitd_variant_t, Int) -> (Int64) = library.load(symbol: "sourcekitd_variant_array_get_int64") internal let sourcekitd_variant_array_get_bool: @convention(c) (sourcekitd_variant_t, Int) -> (Bool) = library.load(symbol: "sourcekitd_variant_array_get_bool") - internal let sourcekitd_variant_array_get_uid: @convention(c) (sourcekitd_variant_t, Int) -> (sourcekitd_uid_t!) = library.load(symbol: "sourcekitd_variant_array_get_uid") + internal let sourcekitd_variant_array_get_uid: @convention(c) (sourcekitd_variant_t, Int) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_variant_array_get_uid") internal let sourcekitd_variant_array_apply_f: @convention(c) (sourcekitd_variant_t, @escaping sourcekitd_variant_array_applier_f_t, UnsafeMutableRawPointer?) -> (Bool) = library.load(symbol: "sourcekitd_variant_array_apply_f") internal let sourcekitd_variant_array_apply: @convention(c) (sourcekitd_variant_t, @escaping sourcekitd_variant_array_applier_t) -> (Bool) = library.load(symbol: "sourcekitd_variant_array_apply") internal let sourcekitd_variant_int64_get_value: @convention(c) (sourcekitd_variant_t) -> (Int64) = library.load(symbol: "sourcekitd_variant_int64_get_value") internal let sourcekitd_variant_bool_get_value: @convention(c) (sourcekitd_variant_t) -> (Bool) = library.load(symbol: "sourcekitd_variant_bool_get_value") internal let sourcekitd_variant_string_get_length: @convention(c) (sourcekitd_variant_t) -> (Int) = library.load(symbol: "sourcekitd_variant_string_get_length") - internal let sourcekitd_variant_string_get_ptr: @convention(c) (sourcekitd_variant_t) -> (UnsafePointer!) = library.load(symbol: "sourcekitd_variant_string_get_ptr") - internal let sourcekitd_variant_uid_get_value: @convention(c) (sourcekitd_variant_t) -> (sourcekitd_uid_t!) = library.load(symbol: "sourcekitd_variant_uid_get_value") + internal let sourcekitd_variant_string_get_ptr: @convention(c) (sourcekitd_variant_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_variant_string_get_ptr") + internal let sourcekitd_variant_uid_get_value: @convention(c) (sourcekitd_variant_t) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_variant_uid_get_value") internal let sourcekitd_response_description_dump: @convention(c) (sourcekitd_response_t) -> () = library.load(symbol: "sourcekitd_response_description_dump") internal let sourcekitd_response_description_dump_filedesc: @convention(c) (sourcekitd_response_t, Int32) -> () = library.load(symbol: "sourcekitd_response_description_dump_filedesc") - internal let sourcekitd_response_description_copy: @convention(c) (sourcekitd_response_t) -> (UnsafeMutablePointer!) = library.load(symbol: "sourcekitd_response_description_copy") + internal let sourcekitd_response_description_copy: @convention(c) (sourcekitd_response_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_response_description_copy") internal let sourcekitd_variant_description_dump: @convention(c) (sourcekitd_variant_t) -> () = library.load(symbol: "sourcekitd_variant_description_dump") internal let sourcekitd_variant_description_dump_filedesc: @convention(c) (sourcekitd_variant_t, Int32) -> () = library.load(symbol: "sourcekitd_variant_description_dump_filedesc") - internal let sourcekitd_variant_description_copy: @convention(c) (sourcekitd_variant_t) -> (UnsafeMutablePointer!) = library.load(symbol: "sourcekitd_variant_description_copy") - internal let sourcekitd_variant_json_description_copy: @convention(c) (sourcekitd_variant_t) -> (UnsafeMutablePointer!) = library.load(symbol: "sourcekitd_variant_json_description_copy") - internal let sourcekitd_send_request_sync: @convention(c) (sourcekitd_object_t) -> (sourcekitd_response_t!) = library.load(symbol: "sourcekitd_send_request_sync") + internal let sourcekitd_variant_description_copy: @convention(c) (sourcekitd_variant_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_variant_description_copy") + internal let sourcekitd_variant_json_description_copy: @convention(c) (sourcekitd_variant_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_variant_json_description_copy") + internal let sourcekitd_send_request_sync: @convention(c) (sourcekitd_object_t) -> (sourcekitd_response_t?) = library.load(symbol: "sourcekitd_send_request_sync") internal let sourcekitd_send_request: @convention(c) (sourcekitd_object_t, UnsafeMutablePointer?, sourcekitd_response_receiver_t?) -> () = library.load(symbol: "sourcekitd_send_request") } diff --git a/swiftshield-Sources/SourceKit.swift b/swiftshield-Sources/SourceKit.swift index ed70b82..89a9955 100755 --- a/swiftshield-Sources/SourceKit.swift +++ b/swiftshield-Sources/SourceKit.swift @@ -1,22 +1,55 @@ -// -// SourceKit.swift -// refactord -// -// Created by John Holdsworth on 19/12/2015. -// Copyright © 2015 John Holdsworth. All rights reserved. -// -// $Id: //depot/Refactorator/refactord/SourceKit.swift#25 $ -// -// Repo: https://github.com/johnno1962/Refactorator -// - import Foundation -/** Thanks to: https://github.com/jpsim/SourceKitten/blob/master/Source/SourceKittenFramework/library_wrapper_sourcekitd.swift **/ +final class SourceKit { + fileprivate static let swiftLangPrefix = "source.lang.swift" + + init() { + SKApi.sourcekitd_initialize() + } + + func referenceType(kind: String) -> DeclarationType? { + return declarationType(for: kind) ?? declarationType(kind: kind, firstSuffix: ".ref.") + } -let SKApi = SKAPI() + func declarationType(for kind: String) -> DeclarationType? { + return declarationType(kind: kind, firstSuffix: ".decl.") + } + + func declarationType(kind: String, firstSuffix: String) -> DeclarationType? { + let prefix = SourceKit.swiftLangPrefix + firstSuffix + guard kind.hasPrefix(prefix) else { + return nil + } + let prefixIndex = kind.index(kind.startIndex, offsetBy: prefix.count) + let kindSuffix = String(kind[prefixIndex...]) + switch kindSuffix { + case "class", + "struct", + "protocol": + return .object +// case "var.instance", +// "var.class": +// return .property + case "function.free", + "function.method.instance", + "function.method.static", + "function.method.class": + return .method + default: + return nil + } + } + + // + // + // + // + // John's SKAPI Requests + // + // + // + // -class SourceKit { /** request types */ private lazy var indexRequestID = SKApi.sourcekitd_uid_get_from_cstr("source.request.indexsource")! private lazy var requestID = SKApi.sourcekitd_uid_get_from_cstr("key.request")! @@ -41,69 +74,14 @@ class SourceKit { lazy var colID = SKApi.sourcekitd_uid_get_from_cstr("key.column")! lazy var usrID = SKApi.sourcekitd_uid_get_from_cstr("key.usr")! - /** declarations */ - - fileprivate static let swiftLangPrefix = "source.lang.swift" - fileprivate static let classIDString = "source.lang.swift.decl.class" - fileprivate static let structIDString = "source.lang.swift.decl.struct" - fileprivate static let protocolIDString = "source.lang.swift.decl.protocol" - fileprivate static let enumIDString = "source.lang.swift.decl.enum" - fileprivate static let enumCaseIDString = "source.lang.swift.decl.enumelement" - fileprivate static let typealiasIDString = "source.lang.swift.decl.typealias" - fileprivate static let associatedTypeIDString = "source.lang.swift.decl.associatedtype" - - fileprivate static let instanceMethodIDString = "source.lang.swift.decl.function.method.instance" - fileprivate static let globalInstanceMethodIDString = "source.lang.swift.decl.function.free" - fileprivate static let staticMethodIDString = "source.lang.swift.decl.function.method.static" - fileprivate static let classMethodIDString = "source.lang.swift.decl.function.method.class" - - init() { - SKApi.sourcekitd_initialize() - } - - func referenceType(kind: String) -> DeclarationType? { - if let type = declarationType(for: kind) { - return type - } - if kind.contains("ref.class") || - kind.contains("ref.struct") || - kind.contains("ref.protocol") || - kind.contains("ref.typealias") { - return .object - } else if kind.contains("ref.function.method.instance") || - kind.contains("ref.function.free") || - kind.contains("ref.function.method.static") || - kind.contains("ref.function.method.class") { - return .method - } else { - return nil - } - } - - func declarationType(for kind: String) -> DeclarationType? { - switch kind { - case SourceKit.classIDString, - SourceKit.structIDString, - SourceKit.protocolIDString: - return .object - case SourceKit.instanceMethodIDString, - SourceKit.globalInstanceMethodIDString, - SourceKit.staticMethodIDString, - SourceKit.classMethodIDString: - return .method - default: - return nil - } - } - func array(argv: [String]) -> sourcekitd_object_t { let objects = argv.map { SKApi.sourcekitd_request_string_create($0) } - return SKApi.sourcekitd_request_array_create(objects, objects.count) + return SKApi.sourcekitd_request_array_create(objects, objects.count)! } func error(resp: sourcekitd_response_t) -> String? { if SKApi.sourcekitd_response_is_error(resp) { - return String(cString: SKApi.sourcekitd_response_error_get_description(resp)) + return String(cString: SKApi.sourcekitd_response_error_get_description(resp)!) } return nil } diff --git a/swiftshield-Sources/SourceKitDeclarationType.swift b/swiftshield-Sources/SourceKitDeclarationType.swift index 279217e..4078279 100644 --- a/swiftshield-Sources/SourceKitDeclarationType.swift +++ b/swiftshield-Sources/SourceKitDeclarationType.swift @@ -1,6 +1,7 @@ extension SourceKit { enum DeclarationType { case object + case property case method } } diff --git a/swiftshield-Sources/Storyboard.swift b/swiftshield-Sources/Storyboard.swift index 937df0d..c51a397 100644 --- a/swiftshield-Sources/Storyboard.swift +++ b/swiftshield-Sources/Storyboard.swift @@ -4,4 +4,8 @@ class Storyboard { static func customClass(`class`: String) -> String { return "customClass=\"\(`class`)\"" } + + static func actionSelector(method: String) -> String { + return "action selector=\"\(method):\"" + } } diff --git a/swiftshield-Sources/String.swift b/swiftshield-Sources/String.swift index a9c7cd4..258c0cf 100644 --- a/swiftshield-Sources/String.swift +++ b/swiftshield-Sources/String.swift @@ -10,6 +10,10 @@ import Foundation typealias RegexClosure = ((NSTextCheckingResult) -> String?) +func firstMatch(for regex: String, in text: String) -> String? { + return matches(for: regex, in: text).first +} + func matches(for regex: String, in text: String) -> [String] { do { let regex = try NSRegularExpression(pattern: regex) @@ -62,7 +66,7 @@ extension String { } static var storyboardClassNameRegex: String { - return "(?<=customClass=\").*?(?=\")" + return "((?<=customClass=\").*?(?=\" customModule)|(?<=action selector=\").*?(?=:\"))" } static var helpText: String { diff --git a/swiftshield-Sources/XcodeProjectBuilder.swift b/swiftshield-Sources/XcodeProjectBuilder.swift index fcc138b..9c501d4 100644 --- a/swiftshield-Sources/XcodeProjectBuilder.swift +++ b/swiftshield-Sources/XcodeProjectBuilder.swift @@ -1,6 +1,10 @@ import Foundation struct XcodeProjectBuilder { + + private typealias MutableModuleData = (source: [File], xibs: [File], args: [String]) + private typealias MutableModuleDictionary = [String: MutableModuleData] + let projectToBuild: String let schemeToBuild: String @@ -28,34 +32,42 @@ struct XcodeProjectBuilder { let outdata = outpipe.fileHandleForReading.readDataToEndOfFile() guard let output = String(data: outdata, encoding: .utf8) else { Logger.log(.compilerArgumentsError) - exit(1) + exit(error: true) } return parseModulesFrom(xcodeBuildOutput: output) } func parseModulesFrom(xcodeBuildOutput output: String) -> [Module] { let lines = output.components(separatedBy: "\n") - var modules: [Module] = [] + var modules: MutableModuleDictionary = [:] for line in lines { - guard line.contains("-module-name") else { - continue - } - let moduleName = matches(for: "(?<=-module-name ).*?(?= )", in: line)[0] - guard modules.contains(where: {$0.name == moduleName}) == false else { - continue + if let moduleName = firstMatch(for: "(?<=-module-name ).*?(?= )", in: line) { + parseMergeSwiftModulePhase(line: line, moduleName: moduleName, modules: &modules) + } else if let moduleName = firstMatch(for: "(?<=--module ).*?(?= )", in: line) { + parseCompileXibPhase(line: line, moduleName: moduleName, modules: &modules) } - Logger.log(.found(module: moduleName)) - let fullRelevantArguments = matches(for: "/usr/bin/swiftc.*-module-name \(moduleName) .*", in: line)[0] - let spacedFolderPlaceholder = "\u{0}" - let relevantArguments = fullRelevantArguments.replacingOccurrences(of: "\\ ", with: spacedFolderPlaceholder) - .components(separatedBy: " ") - .map { $0.replacingOccurrences(of: spacedFolderPlaceholder, with: " ")} - let files = parseModuleFiles(from: relevantArguments) - let compilerArguments = parseCompilerArguments(from: relevantArguments) - let module = Module(name: moduleName, files: files, compilerArguments: compilerArguments) - modules.append(module) } - return modules + return modules.map { + Module(name: $0.key, sourceFiles: $0.value.source, xibFiles: $0.value.xibs, compilerArguments: $0.value.args) + } + } + + private func parseMergeSwiftModulePhase(line: String, moduleName: String, modules: inout MutableModuleDictionary) { + guard modules[moduleName]?.args.isEmpty != false else { + return + } + guard let fullRelevantArguments = firstMatch(for: "/usr/bin/swiftc.*-module-name \(moduleName) .*", in: line) else { + return + } + Logger.log(.found(module: moduleName)) + let spacedFolderPlaceholder = "\u{0}" + let relevantArguments = fullRelevantArguments.replacingOccurrences(of: "\\ ", with: spacedFolderPlaceholder) + .components(separatedBy: " ") + .map { $0.replacingOccurrences(of: spacedFolderPlaceholder, with: " ")} + let files = parseModuleFiles(from: relevantArguments) + let compilerArguments = parseCompilerArguments(from: relevantArguments) + modules[moduleName, default: ([], [], [])].source = files + modules[moduleName]?.args = compilerArguments } private func parseModuleFiles(from relevantArguments: [String]) -> [File] { @@ -94,4 +106,15 @@ struct XcodeProjectBuilder { args.append(contentsOf: ["-D", "DEBUG"]) return args.compactMap { $0 } } + + private func parseCompileXibPhase(line: String, moduleName: String, modules: inout MutableModuleDictionary) { + guard let xibPath = firstMatch(for: "(?=)[^ ]*$", in: line) else { + return + } + guard xibPath.hasSuffix(".xib") || xibPath.hasSuffix(".storyboard") else { + return + } + let file = File(filePath: xibPath) + modules[moduleName, default: ([], [], [])].xibs.append(file) + } } diff --git a/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate b/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate index 4fc2e94..74e62b3 100644 Binary files a/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate and b/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate differ