Openzeppelin
如何訪問通過“return(0, returndatasize())”返回的值?
我正在查看
_delegate
OpenZeppelin 的Proxy.sol中的函式:/** * @dev Delegates the current call to `implementation`. * * This function does not return to its internall call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } }
而且我注意到雖然函式聲明缺少 a
returns (type)
,但程式碼通過內聯彙編返回一個值。在我看來,Solidity 一開始就允許對其進行編譯,但無論如何。我試圖了解在哪裡可以訪問它。顯然,來自 Solidity 的高級呼叫無法訪問返回數據。它是否打算被其他使用returndatacopy指令的低級彙編程式碼使用?(評論中提到的“外部呼叫者”)
solidity編譯器 ( solc ) 會將返回語句轉換為
return(ptr, size)
操作碼。呼叫者為了訪問被呼叫者返回的數據,在早期的 EVM 版本中,必須傳遞一個記憶體指針和一個記憶體大小。例如
call
的最後兩個參數是out
和outSize
。其他操作碼delegatecall
,staticcall
,callcode
具有相同的參數。call(gas, address, value, in, insize, out, outsize)
這種機制的一個缺點是呼叫者需要
outSize
提前知道才能保留記憶體。隨著
delegatecall
在Homestead 中引入 fork編寫代理是可能的,但存在代理無法預先分配記憶體的問題。最後,在拜占庭分支中,一些新的操作碼允許代理在呼叫之前不需要分配輸出記憶體:
returndatasize()
和returndatacopy(to, from, size)
.呼叫後
returndatasize()
包含應用程序返回的數據,並且可以使用returndatacopy(to, from, size)
將數據複製到呼叫者記憶體中。let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
在
delegatecall
輸出參數中都設置為零:out = 0
和outSize = 0
。下一條語句將所有返回的數據從位置 0 複製到從 0 開始的代理記憶體。
returndatacopy(0, 0, returndatasize())
筆記:
returndatacopy
如果函式呼叫失敗,returndatasize
也可以用於複製還原原因。