程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> 淺談在Swift中關於函數指針的完成

淺談在Swift中關於函數指針的完成

編輯:更多關於編程

淺談在Swift中關於函數指針的完成。本站提示廣大學習愛好者:(淺談在Swift中關於函數指針的完成)文章只能為提供參考,不一定能成為您想要的結果。以下是淺談在Swift中關於函數指針的完成正文


Swift沒有甚麼?

蘋果工程師給我建的獨一一堵牆是:在Swift中沒有任何方法取得一個函數的指針:

    留意,C函數指針不會導入到Swift中(來自“Using Swift with Cocoa and Objective-C“)

然則我們怎樣曉得這類情形下鉤子的地址和跳到哪呢?讓我們深刻懂得一下,而且看看Swift的func在字節碼層面上的是甚麼。

當你給一個函數傳遞一個泛型參數時,Swift並沒有直接傳遞它的地址,而是一個指向trampoline函數(見下文)並帶有一些函數元數據信息的指針。而且trampoline本身是包裝原始函數的構造的一部門。

這是甚麼意思?

讓我們用它來舉個例子:
 
func call_function(f : () -> Int) {
    let b = f()
}
 
func someFunction() -> Int {
    return 0
}

在Swift裡我們只寫 call_function(someFunction).
然則 Swift 編譯器處置代碼後,機能比挪用call_function(&someFunction)好許多
 
struct swift_func_wrapper *wrapper =  ... /* configure wrapper for someFunction() */
struct swift_func_type_metadata *type_metadata = ... /* information about function's arguments and return type */
call_function(wrapper->trampoline, type_metadata);

一個包裝器的構造以下:  
 
struct swift_func_wrapper {
    uint64_t **trampoline_ptr_ptr; // = &trampoline_ptr
    uint64_t *trampoline_ptr;
    struct swift_func_object *object;
}

甚麼是 swift_func_object類型? 為了創立對象,Swift 及時應用了一個全局的叫metadata[N]的的常量(每個 function挪用都是獨一的,似的你的func 作為一個泛型的參數,所以關於以下的代碼:  
 
func callf(f: () -> ()) {
    f();
}
callf(someFunction);
callf(someFunction);

常量metadata和metadata2會被創立).

一個metadata[N]的構造有點兒像如許this:
 
struct metadata {
    uint64_t *destructor_func;
    uint64_t *unknown0;
    const char type:1; // I'm not sure about this and padding,
    char padding[7];   // maybe it's just a uint64_t too...
    uint64_t *self;
}

最後metadataN只要2個字段聚集:destructor_func 和 type。前者是一個函數指針,將用作為應用swift_allocObject() 創立的對象分派內存。後者是對象類型辨認器(函數或辦法的0x40 或許 '@'),而且是(某種情勢)被swift_allocObject() 用來創立一個准確的對象給我們的func: 
 
swift_allocObject(&metadata2->type, 0x20, 0x7);

一旦func 對象被創立,它具有上面的構造:
 
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運轉時須要它們,但豈論若何,這就是它們原生態的模樣:
 
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;
}

讓我們創立它

想象一下,在你的Swift代碼中有這些函數:

 
func takesFunc<T>(f : T) {
    ...
}
func someFunction() {
    ...
}

並且你想像如許生成它們:
 
takesFunc(someFunction)

這一行代碼會轉換成相當年夜的C法式:
 
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”很不通明,是以我也沒太多可以說的。

回到函數指針

既然我們曾經曉得函數如何作為一個泛型類型參數表現,讓我們借助這個打到你的目標:獲得一個真正指向函數的指針!

我們要做的只是須要留意,我們曾經具有一個作為第一個參數傳遞的trampoline_ptr指針域地址,所以object域的偏移量只是0x8。其他的一切都很輕易組合:
 
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;
}

看起來是時刻寫寫

 
rd_route(
    _rd_get_func_impl(firstFunction),
    _rd_get_func_impl(secondFunction),
    nil
)

但我們如何從Swift中挪用這些C函數呢?

為此,我們將應用Swift非地下的特征:許可我們供給給C函數一個Swift接口的@asmname屬性。用法以下:
 
@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()須要的一切。

然則它不克不及處置任何函數!

也就是說,你不克不及用rd_route()鉤住任何帶有泛型參數的函數(這能夠是Swift的bug,也能夠不是,我還沒弄清晰)。然則你可使用extensions輕松的籠罩它們,直接指定參數的類型:
 
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"

SWRoute

為了在Swift裡輕松地勾住函數,我創立了一個名為SWRoute的封裝體—它只是一個小類和一個我們之前寫過的C函數:

_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的實例。

這就是一切的了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved