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.