mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10555 from ethereum/address-code
[BREAKING] Introduce address(...).code
This commit is contained in:
commit
8db2cad45c
@ -19,6 +19,7 @@ Breaking Changes:
|
|||||||
* Type System: Disallow ``type(super)``.
|
* Type System: Disallow ``type(super)``.
|
||||||
* Type System: Disallow enums with more than 256 members.
|
* Type System: Disallow enums with more than 256 members.
|
||||||
* Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type.
|
* Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type.
|
||||||
|
* Type System: Introduce ``address(...).code`` to retrieve the code as ``bytes memory``. The size can be obtained via ``address(...).code.length``, but it will currently always include copying the code.
|
||||||
* Type System: Explicit conversions between two types are disallowed if it changes more than one of sign, width or kind at the same time.
|
* Type System: Explicit conversions between two types are disallowed if it changes more than one of sign, width or kind at the same time.
|
||||||
* Type System: Explicit conversions from literals to enums are only allowed if the value fits in the enum.
|
* Type System: Explicit conversions from literals to enums are only allowed if the value fits in the enum.
|
||||||
* Type System: Explicit conversions from literals to integer type is as strict as implicit conversions.
|
* Type System: Explicit conversions from literals to integer type is as strict as implicit conversions.
|
||||||
|
@ -115,6 +115,7 @@ Global Variables
|
|||||||
- ``super``: the contract one level higher in the inheritance hierarchy
|
- ``super``: the contract one level higher in the inheritance hierarchy
|
||||||
- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address
|
- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address
|
||||||
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
||||||
|
- ``<address>.code`` (``bytes memory``): code at the :ref:`address` (can be empty)
|
||||||
- ``<address>.codehash`` (``bytes32``): the codehash of the :ref:`address`
|
- ``<address>.codehash`` (``bytes32``): the codehash of the :ref:`address`
|
||||||
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`,
|
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`,
|
||||||
returns ``false`` on failure
|
returns ``false`` on failure
|
||||||
|
@ -222,6 +222,9 @@ Members of Address Types
|
|||||||
``<address>.balance`` (``uint256``)
|
``<address>.balance`` (``uint256``)
|
||||||
balance of the :ref:`address` in Wei
|
balance of the :ref:`address` in Wei
|
||||||
|
|
||||||
|
``<address>.code`` (``bytes memory``)
|
||||||
|
code at the :ref:`address` (can be empty)
|
||||||
|
|
||||||
``<address>.codehash`` (``bytes32``)
|
``<address>.codehash`` (``bytes32``)
|
||||||
the codehash of the :ref:`address`
|
the codehash of the :ref:`address`
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
switch (_memberAccess.expression().annotation().type->category())
|
switch (_memberAccess.expression().annotation().type->category())
|
||||||
{
|
{
|
||||||
case Type::Category::Address:
|
case Type::Category::Address:
|
||||||
if (member == "balance" || member == "codehash")
|
if (member == "balance" || member == "code" || member == "codehash")
|
||||||
mutability = StateMutability::View;
|
mutability = StateMutability::View;
|
||||||
break;
|
break;
|
||||||
case Type::Category::Magic:
|
case Type::Category::Magic:
|
||||||
|
@ -456,6 +456,7 @@ MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const
|
|||||||
{
|
{
|
||||||
MemberList::MemberMap members = {
|
MemberList::MemberMap members = {
|
||||||
{"balance", TypeProvider::uint256()},
|
{"balance", TypeProvider::uint256()},
|
||||||
|
{"code", TypeProvider::array(DataLocation::Memory)},
|
||||||
{"codehash", TypeProvider::fixedBytes(32)},
|
{"codehash", TypeProvider::fixedBytes(32)},
|
||||||
{"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
|
{"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
|
||||||
{"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
|
{"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
|
||||||
|
@ -1508,6 +1508,39 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
);
|
);
|
||||||
m_context << Instruction::BALANCE;
|
m_context << Instruction::BALANCE;
|
||||||
}
|
}
|
||||||
|
else if (member == "code")
|
||||||
|
{
|
||||||
|
// Stack: <address>
|
||||||
|
utils().convertType(
|
||||||
|
*_memberAccess.expression().annotation().type,
|
||||||
|
*TypeProvider::address(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE;
|
||||||
|
// Stack post: <address> <size>
|
||||||
|
|
||||||
|
m_context << Instruction::DUP1;
|
||||||
|
// Account for the size field of `bytes memory`
|
||||||
|
m_context << u256(32) << Instruction::ADD;
|
||||||
|
utils().allocateMemory();
|
||||||
|
// Stack post: <address> <size> <mem_offset>
|
||||||
|
|
||||||
|
// Store size at mem_offset
|
||||||
|
m_context << Instruction::DUP2 << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
|
|
||||||
|
m_context << u256(0) << Instruction::SWAP1 << Instruction::DUP1;
|
||||||
|
// Stack post: <address> <size> 0 <mem_offset> <mem_offset>
|
||||||
|
|
||||||
|
m_context << u256(32) << Instruction::ADD << Instruction::SWAP1;
|
||||||
|
// Stack post: <address> <size> 0 <mem_offset_adjusted> <mem_offset>
|
||||||
|
|
||||||
|
m_context << Instruction::SWAP4;
|
||||||
|
// Stack post: <mem_offset> <size> 0 <mem_offset_adjusted> <address>
|
||||||
|
|
||||||
|
m_context << Instruction::EXTCODECOPY;
|
||||||
|
// Stack post: <mem_offset>
|
||||||
|
}
|
||||||
else if (member == "codehash")
|
else if (member == "codehash")
|
||||||
{
|
{
|
||||||
utils().convertType(
|
utils().convertType(
|
||||||
|
@ -4055,3 +4055,21 @@ string YulUtilFunctions::copyConstructorArgumentsToMemoryFunction(
|
|||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::externalCodeFunction()
|
||||||
|
{
|
||||||
|
string functionName = "external_code_at";
|
||||||
|
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
return util::Whiskers(R"(
|
||||||
|
function <functionName>(addr) -> mpos {
|
||||||
|
let length := extcodesize(addr)
|
||||||
|
mpos := <allocateArray>(length)
|
||||||
|
extcodecopy(addr, add(mpos, 0x20), 0, length)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("allocateArray", allocateMemoryArrayFunction(*TypeProvider::bytesMemory()))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -460,6 +460,11 @@ public:
|
|||||||
std::string const& _creationObjectName
|
std::string const& _creationObjectName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// @returns the name of a function that copies code from a given address to a newly
|
||||||
|
/// allocated byte array in memory.
|
||||||
|
/// Signature: (address) -> mpos
|
||||||
|
std::string externalCodeFunction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Special case of conversion functions - handles all array conversions.
|
/// Special case of conversion functions - handles all array conversions.
|
||||||
std::string arrayConversionFunction(ArrayType const& _from, ArrayType const& _to);
|
std::string arrayConversionFunction(ArrayType const& _from, ArrayType const& _to);
|
||||||
|
@ -1622,6 +1622,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
"balance(" <<
|
"balance(" <<
|
||||||
expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
|
expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
|
||||||
")\n";
|
")\n";
|
||||||
|
else if (member == "code")
|
||||||
|
define(_memberAccess) <<
|
||||||
|
m_utils.externalCodeFunction() <<
|
||||||
|
"(" <<
|
||||||
|
expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
|
||||||
|
")\n";
|
||||||
else if (member == "codehash")
|
else if (member == "codehash")
|
||||||
define(_memberAccess) <<
|
define(_memberAccess) <<
|
||||||
"extcodehash(" <<
|
"extcodehash(" <<
|
||||||
|
20
test/libsolidity/semanticTests/various/address_code.sol
Normal file
20
test/libsolidity/semanticTests/various/address_code.sol
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
contract C {
|
||||||
|
bytes public initCode;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// This should catch problems, but lets also test the case the optimiser is buggy.
|
||||||
|
assert(address(this).code.length == 0);
|
||||||
|
initCode = address(this).code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To avoid dependency on exact length.
|
||||||
|
function f() public view returns (bool) { return address(this).code.length > 400; }
|
||||||
|
function g() public view returns (uint) { return address(0).code.length; }
|
||||||
|
function h() public view returns (uint) { return address(1).code.length; }
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// constructor() ->
|
||||||
|
// initCode() -> 0x20, 0
|
||||||
|
// f() -> true
|
||||||
|
// g() -> 0
|
||||||
|
// h() -> 0
|
@ -0,0 +1,19 @@
|
|||||||
|
contract A {
|
||||||
|
constructor() {
|
||||||
|
assembly {
|
||||||
|
// This is only 7 bytes here.
|
||||||
|
mstore(0, 0x48aa5566000000)
|
||||||
|
return(0, 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public returns (bytes memory) { return address(new A()).code; }
|
||||||
|
function g() public returns (uint) { return address(new A()).code.length; }
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 0x20, 0x48aa5566000000
|
||||||
|
// g() -> 0x20
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public view returns (address) { return address(this); }
|
||||||
|
function g() public view returns (uint) { return f().balance; }
|
||||||
|
function h() public view returns (bytes memory) { return f().code; }
|
||||||
|
function i() public view returns (uint) { return f().code.length; }
|
||||||
|
function j() public view returns (uint) { return h().length; }
|
||||||
|
}
|
||||||
|
// ----
|
@ -1,6 +1,6 @@
|
|||||||
contract C {
|
contract C {
|
||||||
function f() public returns (C) { return this; }
|
function f() public returns (C) { return this; }
|
||||||
function g() public returns (uint) { return f().balance(); }
|
function g() public returns (uint) { return f().balance; }
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 3125: (114-125): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(...).balance" to access this address member.
|
// TypeError 3125: (114-125): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(...).balance" to access this address member.
|
||||||
|
Loading…
Reference in New Issue
Block a user