Last active
December 8, 2021 22:23
-
-
Save jpsim/1b86d116808cb4e9bc30 to your computer and use it in GitHub Desktop.
Swift and Objective-C Class Parsing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////// | |
// | |
// Swift and Objective-C Class Parsing | |
// | |
//////////////////////////////////////////////// | |
import Foundation | |
// Class parsing | |
enum ClassType { | |
case Swift, ObjectiveC | |
func toString() -> String { | |
switch self { | |
case .Swift: | |
return "Swift" | |
case .ObjectiveC: | |
return "Objective-C" | |
} | |
} | |
} | |
struct ParsedClass { | |
let type: ClassType | |
let name: String | |
let mangledName: String? | |
let moduleName: String? | |
} | |
extension String { | |
subscript (r: Range<Int>) -> String { | |
get { | |
let startIndex = advance(self.startIndex, r.startIndex) | |
let endIndex = advance(startIndex, r.endIndex) | |
return self[Range(start: startIndex, end: endIndex)] | |
} | |
} | |
} | |
func parseClass(aClass: AnyClass) -> ParsedClass { | |
// Swift mangling details found here: http://www.eswick.com/2014/06/inside-swift | |
let originalName = NSStringFromClass(aClass) | |
if !originalName.hasPrefix("_T") { | |
// Not a Swift symbol | |
return ParsedClass(type: ClassType.ObjectiveC, | |
name: originalName, | |
mangledName: nil, | |
moduleName: nil) | |
} | |
let originalNameLength = originalName.utf16count | |
var cursor = 4 | |
var substring = originalName[cursor..originalNameLength-cursor] | |
// Module | |
let moduleLength = substring.bridgeToObjectiveC().integerValue | |
let moduleLengthLength = "\(moduleLength)".utf16count | |
let moduleName = substring[moduleLengthLength..moduleLength] | |
// Update cursor and substring | |
cursor += moduleLengthLength + moduleName.utf16count | |
substring = originalName[cursor..originalNameLength-cursor] | |
// Class name | |
let classLength = substring.bridgeToObjectiveC().integerValue | |
let classLengthLength = "\(classLength)".utf16count | |
let className = substring[classLengthLength..classLength] | |
return ParsedClass(type: ClassType.Swift, | |
name: className, | |
mangledName: originalName, | |
moduleName: moduleName) | |
} | |
// Playground start | |
class MySwiftClass { | |
// Empty class | |
} | |
var parsedClass = parseClass(MySwiftClass.self) | |
parsedClass.type.toString() | |
parsedClass.name | |
parsedClass.mangledName! | |
parsedClass.moduleName! | |
parsedClass = parseClass(NSObject.self) | |
parsedClass.type.toString() | |
parsedClass.name | |
parsedClass.mangledName! | |
parsedClass.moduleName! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func demangle(className: String) -> String { | |
let tmpClassName = ("__TMP" + className).bridgeToObjectiveC().UTF8String | |
let propertyName: CString = "tmpProperty" | |
let tmpClass: AnyClass! = objc_allocateClassPair(NSObject.self, tmpClassName, 0) | |
objc_registerClassPair(tmpClass) | |
let propertyType = "@\"\(className)\"".bridgeToObjectiveC().UTF8String | |
let attr = objc_property_attribute_t(name: "T", value: propertyType) | |
class_addProperty(tmpClass, propertyName, [attr], 1) | |
let property = class_getProperty(tmpClass, propertyName) | |
var attCount: CUnsignedInt = 0; | |
let atts = property_copyAttributeList(property, &attCount) | |
objc_disposeClassPair(tmpClass) | |
return "\(atts[0].value)" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is very nice 👍
Is there any licensing to this code?
I borrowed your v1 code and wrote my debugging tool here, just working like C macro.
https://github.com/inamiy/DebugLog