1

我對於大量使用綁定函數參數的函數式編程語言設計有一個想法。我試圖在x86彙編中表達綁定的函數參數,作爲編譯器實現的一部分。在編譯器中實現綁定函數參數

var add = function(x,y) { return x + y; }; 
var add2 = add.bind({}, 2); 
console.log(add2(3));  // prints 5 

對於互操作性的原因,我想產生裸函數指針,所以我的第一個概念想法是在堆上分配一些可執行內存,並在存根複製(一)推動一個額外的參數和( b)調用目標函數。這將是標準庫的一部分,並且會返回一個本機函數指針,我可以從其餘的x86彙編程序中使用它。

我覺得我遇到了這種方法的問題 - 如果存根使用call到達目標函數,那麼堆棧包含一個返回地址,最終被解釋爲函數參數!如果存根使用jmp到達目標函數,則在函數返回時,調用者和被調用者都不知道如何清理堆棧。

這怎麼解決?我想一個寄存器可以永久保留作爲這種行爲的標誌,但這不是很好。

如何在本地編譯器中爲函數式語言實現bind()?

+0

我沒有看到序列'push parameter' +'call function'的任何問題。這是使用參數調用函數的一種相當典型的方式。我不明白返回地址的問題。函數忽略它,它們只用它來返回調用者。 –

+0

在這種情況下使用'push'和'call',我認爲堆棧包含2,[ptr],3。原始函數add()'期待堆棧頂部有兩個int作爲參數,但已經結束使用2和[ptr] ...以及多次調用'bind()'時,返回地址都與您想要的實際參數交錯。 – mappu

+0

如何簡單地按正確的順序推參數?順便說一句,你不能在調用之後在調用者中推送任何額外的參數,因爲'call'傳遞了控制權。所以,只需按照正確的順序排列所有參數(包括綁定的參數),然後調用。 –

回答

1

在一些進一步的想法,我認爲這可以通過使用被調用者清除約定和手動管理我所有的返回地址來完成。它與stdcall類似,但不完全相同,因爲call/ret不能使用(?)。

僞代碼:

main: 
    ; create stub, copy in 0x02 and &add, the resulting function pointer goes in add2 
    local add2 = _create_trampoline 

    ; make the call 
    push [return address] 
    push 0x03 ;arg1 
    jmp add2 

; the resulting stub, held on the heap somewhere 
add2: 
    push 0x02 ;bound argument 
    jmp add 

; var add(x,y) 
add: 
    local x = pop 
    local y = pop 

    eax = x + y;  

    jmp pop 

這樣,add知道堆棧佈局爲y x [ptr]和執行正確返回。

看起來有點激烈,失去了這個呼叫/ ret,並且功能add的堆棧框架非常脆弱,所以我將問題打開至少24小時,希望有更好的解決方案。


編輯:在一些進一步的思考,你可以保持CDECL,來電,清理,呼叫/ RET,一切由綁定蹦牀一起返回地址,只需攜帶(只需要重挫一個寄存器,或將它移動到堆棧並返回)。

僞代碼:

main: 
    ; create stub, copy in 0x02 and &add, the resulting function pointer goes in add2 
    local add2 = _magic(0x02, &add); 

    ; make the call 
    push 0x03; 
    call add2; 

add2: 
    ebx = pop;  ;the return address goes in a temporary 
    push 0x02; 
    push ebx; 
    jmp add 

; var add(x,y) 
add: 
    push ebp; 
    mov ebp, esp; 

    ; local variables are [ebp+8] and [ebp+12] 
    perform calculation into eax 

    leave 
    ret 

在那裏,結果是實現綁定功能參數在堆上可執行對象,保持cdecl調用約定一個相當簡潔的技術。毫無疑問,這種方法在實施時會有問題,但我認爲這是可行的,而且效率不高。

0

您不能將預傳參數存儲在內存中。然後,當你看到「add2」時,你從內存中收集參數,將它們推入堆棧,將其他參數推送到堆棧(根據需要),然後像正常一樣進行函數調用?

我主要是想大聲我不知道這是答案,但它看起來對我有用。

+0

這是一個想法,但它是一個相當大的運行時間開銷(?),我不認爲它可以讓你使用add2作爲一個原始函數。例如,通過重複該過程,存在綁定多個參數的問題。 要做一個'正常的函數調用',你將不得不向後走下堆棧,試圖確定目標函數應該結束多少個其他參數... – mappu