Swift: Wherein I do a horrible thing
Don't do this; code responsibly
Please don't do this. It's horrible, unsupported, and may crash your program.
That said, let's demangle a class name by calling an internal Swift library function.
@asmname("swift_demangleSimpleClass")
func demangleSimpleClass(mangledName: ConstUnsafePointer<Int8>,
moduleName: UnsafePointer<Int>,
className: UnsafePointer<Int>) -> Bool
let someInstance = ...
let namen = object_className(someInstance)
let p2 = UnsafePointer<Int>.alloc(1)
let p3 = UnsafePointer<Int>.alloc(1)
let ret = demangleSimpleClass(namen, p2, p3)
if ret {
let p2_2 = UnsafePointer<Int8>(p2.memory)
let p3_2 = UnsafePointer<Int8>(p3.memory)
let moduleName = String.fromCString(p2_2)!
let className = String.fromCString(p3_2)!
println("Type Name: \(moduleName).\(className)")
} else {
println("Failure")
}
p2.dealloc(1)
p3.dealloc(1)
Update: A little birdie pointed out that I'm doing things the hard way here and it seems so obvious now I can only hang my head in shame. Here's a better version that gets rid of the double indirection.
I think I originally forgot to change the let to var, as a result the inout parameters wouldn't work. I am going to file a bug on that - the compiler gives a strange error message "'Int' is not convertible to '@lvalue inout $T3'", instead of something sensible like "Cannot pass 'let' constant reference to parameter expecting mutable 'inout'"
@asmname("swift_demangleSimpleClass")
func demangleSimpleClass(mangledName: ConstUnsafePointer<Int8>,
inout moduleName: Int,
inout className: Int) -> Bool
let someInstance = ...
let namen = object_className(someInstance)
var p2 = 0, p3 = 0
let ret = demangleSimpleClass(namen, &p2, &p3)
if ret {
let moduleName = String.fromCString(UnsafePointer<CChar>(p2))!
let className = String.fromCString(UnsafePointer<CChar>(p3))!
println("Module: \(moduleName), Class: \(className)")
} else {
println("Failure")
}
First off notice the @asm attribute. This is the equivalent of DllImport or extern. It tells Swift that we are going to link in some library that defines a function with the given name and matching the given arguments. "Just trust me Swift, I know what I'm doing". Hint: You had better know what you're doing.
So after much probing, testing, and examination of assembly I think I have the function declaration correct but it's all guesswork so don't rely on it.
We declare some Ints (which are always pointer-sized) and pass those by reference, then use that memory address to get a char * for the module and class names, then we turn that into a String. (The updated version is much cleaner and clearer).
So far the results look good; for my own class called "Mine" in project "HorribleThings" I get HorribleThings.Mine. For String, I get Foundation._NSContiguousString.
Unfortunately this isn't a general solution; it only works for classes.
When working with the REPL, I can get Mirror to spit out the type names via valueType:Any.Type even for structs, Ints, and so on, but no amount of coaxing will get Swift to put that type name into a string. Any attempt yields the description which is just (ExistentialMetatype). We'll have to wait for Apple to release an update that includes more complete reflection support.
Disclaimer
This code is horrible. It does horrible things. You are a horrible person if you use it. Please don't.
This blog represents my own personal opinion and is not endorsed by my employer.