内联汇编
我们可以在 Solidity 语句中交错使用内联汇编,这种汇编语言接近以太坊虚拟机(EVM)的语言。这让我们能够更加精细地控制,尤其是在编写库或优化 gas 使用时非常有用。
在 Solidity 中使用的内联汇编语言叫做 Yul,它有独立的文档。在这一部分,我们仅讨论内联汇编代码如何与周围的 Solidity 代码交互。
内联汇编是一种低级访问以太坊虚拟机的方式。这会绕过 Solidity 的一些重要安全特性和检查。我们应该仅在需要时使用内联汇编,并且只有在我们对其使用充满信心时才使用。
内联汇编代码块用 assembly { … } 标记,花括号中的代码是 Yul 语言的代码。
内联汇编代码可以访问局部的 Solidity 变量,如下所述。
不同的内联汇编块没有共享命名空间,也就是说,无法在不同的内联汇编块中调用 Yul 函数或访问 Yul 变量。
示例
以下示例提供了一个库代码,用于访问另一个合约的代码并将其加载到一个 bytes 变量中。这在“纯 Solidity”中也可以通过使用 <address>.code
来实现。但重点在于,可重用的汇编库可以在不需要编译器更改的情况下增强 Solidity 语言。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
library GetCode {
function at(address addr) public view returns (bytes memory code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(addr)
// allocate output byte array - this could also be done without assembly
// by using code = new bytes(size)
code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(code, size)
// actually retrieve the code, this needs assembly
extcodecopy(addr, add(code, 0x20), 0, size)
}
}
}
当优化器无法生成高效代码时,内联汇编也很有用,例如:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
function sumSolidity(uint[] memory data) public pure returns (uint sum) {
for (uint i = 0; i < data.length; ++i)
sum += data[i];
}
// We know that we only access the array in bounds, so we can avoid the check.
// 0x20 needs to be added to an array because the first