配色: 字号:
浅谈在Swift中关于函数指针的实现
2016-12-03 | 阅:  转:  |  分享 
  
浅谈在Swift中关于函数指针的实现

这篇文章主要介绍了浅谈在Swift中关于函数指针的实现,是作者根据C语言的指针特性在Swifft中做出的一个实验,需要的朋友可以参考下

Swift没有什么?



苹果工程师给我建的唯一一堵墙是:在Swift中没有任何办法获得一个函数的指针:



注意,C函数指针不会导入到Swift中(来自“UsingSwiftwithCocoaandObjective-C“)



但是我们怎么知道这种情况下钩子的地址和跳到哪呢?让我们深入了解一下,并且看看Swift的func在字节码层面上的是什么。



当你给一个函数传递一个泛型参数时,Swift并没有直接传递它的地址,而是一个指向trampoline函数(见下文)并带有一些函数元数据信息的指针。并且trampoline自己是包装原始函数的结构的一部分。



这是什么意思?



让我们用它来举个例子:





复制代码代码如下:





funccall_function(f:()->Int){

letb=f()

}



funcsomeFunction()->Int{

return0

}





在Swift里我们只写call_function(someFunction).

但是Swift编译器处理代码后,性能比调用call_function(&someFunction)好很多





复制代码代码如下:





structswift_func_wrapperwrapper=.../configurewrapperforsomeFunction()/

structswift_func_type_metadatatype_metadata=.../informationaboutfunction''sargumentsandreturntype/

call_function(wrapper->trampoline,type_metadata);





一个包装器的结构如下:





复制代码代码如下:





structswift_func_wrapper{

uint64_ttrampoline_ptr_ptr;//=&trampoline_ptr

uint64_ttrampoline_ptr;

structswift_func_objectobject;

}





什么是swift_func_object类型?为了创建对象,Swift实时使用了一个全局的叫metadata[N]的的常量(每一个function调用都是唯一的,似的你的func作为一个泛型的参数,所以对于如下的代码:





复制代码代码如下:





funccallf(f:()->()){

f();

}

callf(someFunction);

callf(someFunction);





常量metadata和metadata2会被创建).



一个metadata[N]的结构有点儿像这样this:





复制代码代码如下:





structmetadata{

uint64_tdestructor_func;

uint64_tunknown0;

constchartype:1;//I''mnotsureaboutthisandpadding,

charpadding[7];//maybeit''sjustauint64_ttoo...

uint64_tself;

}





最初metadataN只有2个字段集合:destructor_func和type。前者是一个函数指针,将用作为使用swift_allocObject()创建的对象分配内存。后者是对象类型识别器(函数或方法的0x40或者''@''),并且是(某种形式)被swift_allocObject()用来创建一个正确的对象给我们的func:



swift_allocObject(&metadata2->type,0x20,0x7);



一旦func对象被创建,它拥有下面的结构:





复制代码代码如下:





structswift_func_object{

uint64_toriginal_type_ptr;

uint64_tunknown0;

uint64_tfunction_address;

uint64_tself;

}





第一个字段是一个指针,用来对应metadata[N]->type的值,第二个字段似乎是0x4|1<<24(0x100000004)并且暗示一些可能(我不知道是什么)。function_address是我们实际挂钩感兴趣的地方,并且self是(立即)自己的指针(如果我们的对象表示一个普通的函数,这个字段是NULL)。





好,那么这段我从框架开始如何?事实上,我不明白为什么Swift运行时需要它们,但不论如何,这就是它们原生态的样子:





复制代码代码如下:





voidsomeFunction_Trampoline(voidunknown,voidarg,structswift_func_objectdesc)

{

voidtarget_function=(void)desc->function_address;

uint64_tself=desc->self;



swift_retain_noresult(desc->self);//yeah,retainingselfiscool!

swift_release(desc);



_swift_Trampoline(unknown,arg,target_function,self);

returnunknown;

}



void_swift_Trampoline(voidunknown,voidarg,voidtarget_function,voidself)

{

target_function(arg,self);

returnunknown;

}





让我们创建它



想象一下,在你的Swift代码中有这些函数:







复制代码代码如下:





functakesFunc(f:T){

...

}

funcsomeFunction(){

...

}





而且你想像这样生成它们:





复制代码代码如下:





takesFunc(someFunction)





这一行代码会转换成相当大的C程序:





复制代码代码如下:





structswift_func_wrapperwrapper=malloc(sizeof(wrapper));

wrapper->trampoline_ptr=&someFunction_Trampoline;

wrapper->trampoline_ptr_ptr=&(wrapper.trampoline);

wrapper->object=({

//let''ssaythemetadataforthisfunctionis`metadata2`

structswift_func_objectobject=swift_allocObject(&metadata2->type,0x20,0x7);

object->function_address=&someFunction;

object->self=NULL;

object;

});





//globalconstantforthetypeofsomeFunction''sarguments

constvoidarg_type=&kSomeFunctionArgumentsTypeDescription;

//globalconstantforthereturntypeofsomeFunction

constvoidreturn_type=&kSomeFunctionReturnTypeDescription;



structswift_func_type_metadatatype_metadata=swift_getFunctionTypewww.hunanwang.netMetadata(arg_type,return_type);



takesFunc(wrapper->trampoline_ptr,type_metadata);





结构体“swift_func_type_metadata”很不透明,因此我也没太多可以说的。



回到函数指针



既然我们已经知道函数怎样作为一个泛型类型参数表示,让我们借助这个打到你的目的:获取一个真正指向函数的指针!



我们要做的只是需要注意,我们已经拥有一个作为第一个参数传递的trampoline_ptr指针域地址,所以object域的偏移量只是0x8。其他的所有都很容易组合:





复制代码代码如下:





uint64_t_rd_get_func_impl(voidtrampoline_ptr)

{

structswift_func_objectobj=(structswift_func_object)(uint64_t)(trampoline_ptr+0x8);



returnobj->function_address;

}





看起来是时候写写







复制代码代码如下:





rd_route(

_rd_get_func_impl(firstFunction),

_rd_get_func_impl(secondFunction),

nil

)





但我们怎样从Swift中调用这些C函数呢?



为此,我们将使用Swift非公开的特性:允许我们提供给C函数一个Swift接口的@asmname属性。用法如下:





复制代码代码如下:





@asmname("_rd_get_func_impl")

funcrd_get_func_impl(Q)->UInt64;



@asmname("rd_route")

funcrd_route(UInt64,UInt64,CMutablePointer)->CInt;





这就是我们在Swift中使用rd_route()需要的一切。



但是它不能处理任何函数!



也就是说,你不能用rd_route()钩住任何带有泛型参数的函数(这可能是Swift的bug,也可能不是,我还没弄清楚)。但是你可以使用extensions轻松的覆盖它们,直接指定参数的类型:





复制代码代码如下:





classDemoClass{

classfunctemplate(arg:T,_num:Int)->String{

return"\(arg)and\(num)";

}

}



DemoClass.template("Test",5)//"Testand5"



extensionDemoClass{

classfunctemplate(arg:String,_num:Int)->String{

return"{String}";

}

classfunctemplate(arg:Int,_num:Int)->String{

return"{Int}";

}

}



--Yourextension''smethodsforStringandIntwillbeprewww.sm136.comferredovertheoriginalones/

DemoClass.template("Test",5)--"{String}"

DemoClass.template(42,5)--"{Int}"

--Butforothertypes`template(T,Int)`willbeused

DemoClass.template(["Array","Item"],5)---"[Array,Item]and5"





SWRoute



为了在Swift里轻松地勾住函数,我创建了一个名为SWRoute的封装体—它只是一个小类和一个我们之前写过的C函数:





复制代码代码如下:



_rd_get_func_impl():



classSwiftRoute{

classfuncreplace(functiontargetMethod:MethodT,withreplacement:MethodT)->Int

{

returnInt(rd_route(rd_get_func_impl(targetMethod),rd_get_func_impl(replacement),nil));

}

}





注意,我们无偿进行类型检查因为Swift需要目标方法和替换具有相同的MethoT类型。



而且我们也无法使用一个复制的原始实现,因此我只能把nil作为另一个参数传给函数rd_route()。如果你对如何把这个指针集成到Swift代码有自己的看法,麻烦告诉我!



你可以在资源库中找到大量SWRoute的实例。



这就是所有的了。





















献花(0)
+1
(本文系白狐一梦首藏)