源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

浅谈在Swift中关于函数指针的实现

  • 时间:2022-04-01 03:32 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:浅谈在Swift中关于函数指针的实现
[b]Swift没有什么?[/b] 苹果工程师给我建的唯一一堵墙是:在Swift中没有任何办法获得一个函数的指针:     注意,C函数指针不会导入到Swift中(来自“Using Swift with Cocoa and Objective-C“) 但是我们怎么知道这种情况下钩子的地址和跳到哪呢?让我们深入了解一下,并且看看Swift的func在字节码层面上的是什么。 当你给一个函数传递一个泛型参数时,Swift并没有直接传递它的地址,而是一个指向trampoline函数(见下文)并带有一些函数元数据信息的指针。并且trampoline自己是包装原始函数的结构的一部分。 [b]这是什么意思?[/b] 让我们用它来举个例子:  
[url=]'@'[/url]),并且是(某种形式)被swift_allocObject() 用来创建一个正确的对象给我们的func:    swift_allocObject(&metadata2->type, 0x20, 0x7); 一旦func 对象被创建,它拥有下面的结构:  
[u]复制代码[/u] 代码如下:
struct swift_func_object {     uint64_t *original_type_ptr;     uint64_t *unknown0;     uint64_t function_address;     uint64_t *self; }
第一个字段是一个指针,用来对应metadata[N]->type 的值,第二个字段似乎是 0x4 | 1 << 24(0x100000004) 并且暗示一些可能 (我不知道是什么)。  function_address 是我们实际挂钩感兴趣的地方,并且self 是 (立即) 自己的指针 (如果我们的对象表示一个普通的函数,这个字段是 NULL)。 好,那么这段我从框架开始如何?事实上,我不明白为什么Swift运行时需要它们,但不论如何,这就是它们原生态的样子:  
[u]复制代码[/u] 代码如下:
void* someFunction_Trampoline(void *unknown, void *arg, struct swift_func_object *desc) {     void* target_function = (void *)desc->function_address;     uint64_t *self = desc->self;       swift_retain_noresult(desc->self); // yeah, retaining self is cool!     swift_release(desc);       _swift_Trampoline(unknown, arg, target_function, self);     return unknown; }   void *_swift_Trampoline(void *unknown, void *arg, void *target_function, void *self) {     target_function(arg, self);     return unknown; }
[b]让我们创建它[/b] 想象一下,在你的Swift代码中有这些函数:  
[u]复制代码[/u] 代码如下:
func takesFunc<T>(f : T) {     ... } func someFunction() {     ... }
而且你想像这样生成它们:  
[u]复制代码[/u] 代码如下:
takesFunc(someFunction)
这一行代码会转换成相当大的C程序:  
[u]复制代码[/u] 代码如下:
struct swift_func_wrapper *wrapper = malloc(sizeof(*wrapper)); wrapper->trampoline_ptr     = &someFunction_Trampoline; wrapper->trampoline_ptr_ptr = &(wrapper.trampoline); wrapper->object = ({     // let's say the metadata for this function is `metadata2`     struct swift_func_object *object = swift_allocObject(&metadata2->type, 0x20, 0x7);     object->function_address = &someFunction;     object->self = NULL;     object; });     // global constant for the type of someFunction's arguments const void *arg_type = &kSomeFunctionArgumentsTypeDescription; // global constant for the return type of someFunction const void *return_type = &kSomeFunctionReturnTypeDescription;   struct swift_func_type_metadata *type_metadata = swift_getFunctionTypeMetadata(arg_type, return_type);   takesFunc(wrapper->trampoline_ptr, type_metadata);
结构体“swift_func_type_metadata”很不透明,因此我也没太多可以说的。 [b]回到函数指针[/b] 既然我们已经知道函数怎样作为一个泛型类型参数表示,让我们借助这个打到你的目的:获取一个真正指向函数的指针! 我们要做的只是需要注意,我们已经拥有一个作为第一个参数传递的trampoline_ptr指针域地址,所以object域的偏移量只是0x8。其他的所有都很容易组合:  
[u]复制代码[/u] 代码如下:
uint64_t _rd_get_func_impl(void *trampoline_ptr) {     struct swift_func_object *obj = (struct swift_func_object *)*(uint64_t *)(trampoline_ptr + 0x8);       return obj->function_address; }
看起来是时候写写  
[u]复制代码[/u] 代码如下:
rd_route(     _rd_get_func_impl(firstFunction),     _rd_get_func_impl(secondFunction),     nil )
但我们怎样从Swift中调用这些C函数呢? 为此,我们将使用Swift非公开的特性:允许我们提供给C函数一个Swift接口的@asmname属性。用法如下:  
[u]复制代码[/u] 代码如下:
@asmname("_rd_get_func_impl")     func rd_get_func_impl<Q>(Q) -> UInt64;    @asmname("rd_route")     func rd_route(UInt64, UInt64, CMutablePointer<UInt64>) -> CInt;
这就是我们在Swift中使用rd_route()需要的一切。 [b]但是它不能处理任何函数![/b] 也就是说,你不能用rd_route()钩住任何带有泛型参数的函数(这可能是Swift的bug,也可能不是,我还没弄清楚)。但是你可以使用extensions轻松的覆盖它们,直接指定参数的类型:  
[u]复制代码[/u] 代码如下:
class DemoClass {     class func template <T : CVarArg>(arg : T, _ num: Int) -> String {         return "\(arg) and \(num)";     } }   DemoClass.template("Test", 5) // "Test and 5"   extension DemoClass {     class func template(arg : String, _ num: Int) -> String {         return "{String}";     }     class func template(arg : Int, _ num: Int) -> String {         return "{Int}";     } }   -- Your extension's methods for String and Int will be preferred over the original ones */ DemoClass.template("Test", 5) -- "{String}" DemoClass.template(42, 5) -- "{Int}" -- But for other types `template(T, Int)` will be used DemoClass.template(["Array", "Item"], 5) --- "[Array, Item] and 5"
[b]SWRoute[/b] 为了在Swift里轻松地勾住函数,我创建了一个名为SWRoute的封装体—它只是一个小类和一个我们之前写过的C函数:
[u]复制代码[/u] 代码如下:
_rd_get_func_impl():   class SwiftRoute {     class func replace<MethodT>(function targetMethod : MethodT, with replacement : MethodT) -> Int     {         return Int(rd_route(rd_get_func_impl(targetMethod), rd_get_func_impl(replacement), nil));     } }
注意,我们无偿进行类型检查因为Swift需要目标方法和替换具有相同的MethoT类型。 而且我们也无法使用一个复制的原始实现,因此我只能把nil作为另一个参数传给函数rd_route()。如果你对如何把这个指针集成到Swift代码有自己的看法,麻烦告诉我! 你可以在资源库中找到大量SWRoute的实例。 这就是所有的了。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部