mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5775 from ethereum/codeAccess
Provide access to code of contract types.
This commit is contained in:
@@ -277,7 +277,7 @@
|
||||
"name" : "this",
|
||||
"nodeType" : "Identifier",
|
||||
"overloadedDeclarations" : [],
|
||||
"referencedDeclaration" : 65,
|
||||
"referencedDeclaration" : 66,
|
||||
"src" : "217:4:1",
|
||||
"typeDescriptions" :
|
||||
{
|
||||
|
||||
@@ -424,7 +424,7 @@
|
||||
[
|
||||
null
|
||||
],
|
||||
"referencedDeclaration" : 65,
|
||||
"referencedDeclaration" : 66,
|
||||
"type" : "contract C",
|
||||
"value" : "this"
|
||||
},
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
class Contract;
|
||||
namespace test
|
||||
{
|
||||
|
||||
@@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
Compiler compiler(dev::test::Options::get().evmVersion());
|
||||
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes());
|
||||
compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes());
|
||||
|
||||
return compiler.runtimeAssemblyItems();
|
||||
}
|
||||
|
||||
@@ -14234,6 +14234,113 @@ BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables)
|
||||
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(code_access)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract C {
|
||||
function lengths() public pure returns (bool) {
|
||||
uint crLen = type(D).creationCode.length;
|
||||
uint runLen = type(D).runtimeCode.length;
|
||||
require(runLen < crLen);
|
||||
require(crLen >= 0x20);
|
||||
require(runLen >= 0x20);
|
||||
return true;
|
||||
}
|
||||
function creation() public pure returns (bytes memory) {
|
||||
return type(D).creationCode;
|
||||
}
|
||||
function runtime() public pure returns (bytes memory) {
|
||||
return type(D).runtimeCode;
|
||||
}
|
||||
function runtimeAllocCheck() public pure returns (bytes memory) {
|
||||
uint[] memory a = new uint[](2);
|
||||
bytes memory c = type(D).runtimeCode;
|
||||
uint[] memory b = new uint[](2);
|
||||
a[0] = 0x1111;
|
||||
a[1] = 0x2222;
|
||||
b[0] = 0x3333;
|
||||
b[1] = 0x4444;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
contract D {
|
||||
function f() public pure returns (uint) { return 7; }
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
ABI_CHECK(callContractFunction("lengths()"), encodeArgs(true));
|
||||
bytes codeCreation = callContractFunction("creation()");
|
||||
bytes codeRuntime1 = callContractFunction("runtime()");
|
||||
bytes codeRuntime2 = callContractFunction("runtimeAllocCheck()");
|
||||
ABI_CHECK(codeRuntime1, codeRuntime2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(code_access_create)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract C {
|
||||
function test() public returns (uint) {
|
||||
bytes memory c = type(D).creationCode;
|
||||
D d;
|
||||
assembly {
|
||||
d := create(0, add(c, 0x20), mload(c))
|
||||
}
|
||||
return d.f();
|
||||
}
|
||||
}
|
||||
contract D {
|
||||
uint x;
|
||||
constructor() public { x = 7; }
|
||||
function f() public view returns (uint) { return x; }
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(7));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(code_access_content)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract C {
|
||||
function testRuntime() public returns (bool) {
|
||||
D d = new D();
|
||||
bytes32 runtimeHash = keccak256(type(D).runtimeCode);
|
||||
bytes32 otherHash;
|
||||
uint size;
|
||||
assembly {
|
||||
size := extcodesize(d)
|
||||
extcodecopy(d, mload(0x40), 0, size)
|
||||
otherHash := keccak256(mload(0x40), size)
|
||||
}
|
||||
require(size == type(D).runtimeCode.length);
|
||||
require(runtimeHash == otherHash);
|
||||
return true;
|
||||
}
|
||||
function testCreation() public returns (bool) {
|
||||
D d = new D();
|
||||
bytes32 creationHash = keccak256(type(D).creationCode);
|
||||
require(creationHash == d.x());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
contract D {
|
||||
bytes32 public x;
|
||||
constructor() public {
|
||||
bytes32 codeHash;
|
||||
assembly {
|
||||
let size := codesize()
|
||||
codecopy(mload(0x40), 0, size)
|
||||
codeHash := keccak256(mload(0x40), size)
|
||||
}
|
||||
x = codeHash;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true));
|
||||
ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
||||
@@ -539,7 +539,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved)
|
||||
"supports",
|
||||
"switch",
|
||||
"try",
|
||||
"type",
|
||||
"typedef",
|
||||
"typeof",
|
||||
"unchecked"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
contract Test {
|
||||
function creationOther() public pure returns (bytes memory) {
|
||||
return type(Other).creationCode;
|
||||
}
|
||||
function runtimeOther() public pure returns (bytes memory) {
|
||||
return type(Other).runtimeCode;
|
||||
}
|
||||
}
|
||||
contract Other {
|
||||
function f(uint) public pure returns (uint) {}
|
||||
}
|
||||
// ----
|
||||
@@ -0,0 +1,10 @@
|
||||
contract Test {
|
||||
function creationOther() public pure returns (bytes memory) {
|
||||
return type(Other).creationCode;
|
||||
}
|
||||
}
|
||||
contract Other {
|
||||
function f(uint) public returns (uint);
|
||||
}
|
||||
// ----
|
||||
// TypeError: (97-121): Member "creationCode" not found or not visible after argument-dependent lookup in type(contract Other).
|
||||
@@ -0,0 +1,10 @@
|
||||
contract Test {
|
||||
function runtime() public pure returns (bytes memory) {
|
||||
return type(Other).runtimeCode;
|
||||
}
|
||||
}
|
||||
contract Other {
|
||||
function f(uint) public returns (uint);
|
||||
}
|
||||
// ----
|
||||
// TypeError: (91-114): Member "runtimeCode" not found or not visible after argument-dependent lookup in type(contract Other).
|
||||
@@ -0,0 +1,22 @@
|
||||
contract Base {
|
||||
function f() public pure returns (uint) {}
|
||||
}
|
||||
contract Test is Base {
|
||||
function creation() public pure returns (bytes memory) {
|
||||
return type(Test).creationCode;
|
||||
}
|
||||
function runtime() public pure returns (bytes memory) {
|
||||
return type(Test).runtimeCode;
|
||||
}
|
||||
function creationBase() public pure returns (bytes memory) {
|
||||
return type(Base).creationCode;
|
||||
}
|
||||
function runtimeBase() public pure returns (bytes memory) {
|
||||
return type(Base).runtimeCode;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (165-188): Circular reference for contract code access.
|
||||
// TypeError: (271-293): Circular reference for contract code access.
|
||||
// TypeError: (381-404): Circular reference for contract code access.
|
||||
// TypeError: (491-513): Circular reference for contract code access.
|
||||
@@ -0,0 +1,12 @@
|
||||
contract A {
|
||||
function f() public pure {
|
||||
type(B).runtimeCode;
|
||||
}
|
||||
}
|
||||
contract B {
|
||||
function f() public pure {
|
||||
type(A).runtimeCode;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (133-152): Circular reference for contract code access.
|
||||
@@ -0,0 +1,7 @@
|
||||
contract Test {
|
||||
bytes constant c = type(B).creationCode;
|
||||
bytes constant r = type(B).runtimeCode;
|
||||
|
||||
}
|
||||
contract B { function f() public pure {} }
|
||||
// ----
|
||||
@@ -0,0 +1,12 @@
|
||||
contract Test {
|
||||
function creationOther() public pure returns (bytes memory) {
|
||||
return type(Library).creationCode;
|
||||
}
|
||||
function runtime() public pure returns (bytes memory) {
|
||||
return type(Library).runtimeCode;
|
||||
}
|
||||
}
|
||||
contract Library {
|
||||
function f(uint) public pure returns (uint) {}
|
||||
}
|
||||
// ----
|
||||
@@ -0,0 +1,10 @@
|
||||
contract Test {
|
||||
function f() public pure {
|
||||
type(C).creationCode = new bytes(6);
|
||||
type(C).runtimeCode = new bytes(6);
|
||||
}
|
||||
}
|
||||
contract C {}
|
||||
// ----
|
||||
// TypeError: (55-75): Expression has to be an lvalue.
|
||||
// TypeError: (100-119): Expression has to be an lvalue.
|
||||
@@ -0,0 +1,7 @@
|
||||
contract Test {
|
||||
function creation() public pure returns (bytes memory) {
|
||||
type();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (85-91): This function takes one argument, but 0 were provided.
|
||||
@@ -0,0 +1,17 @@
|
||||
contract Test {
|
||||
function f() public pure returns (uint) {
|
||||
return type(C).runtimeCode.length +
|
||||
type(D).runtimeCode.length +
|
||||
type(C).creationCode.length +
|
||||
type(D).creationCode.length;
|
||||
}
|
||||
}
|
||||
contract C {
|
||||
constructor() public { assembly {} }
|
||||
}
|
||||
contract D is C {
|
||||
constructor() public {}
|
||||
}
|
||||
// ----
|
||||
// Warning: (77-96): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
|
||||
// Warning: (118-137): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
|
||||
@@ -0,0 +1,7 @@
|
||||
contract Test {
|
||||
function creation() public pure returns (bytes memory) {
|
||||
type(1, 2);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (85-95): This function takes one argument, but 2 were provided.
|
||||
@@ -0,0 +1,3 @@
|
||||
contract type { }
|
||||
// ----
|
||||
// ParserError: (9-13): Expected identifier but got 'type'
|
||||
@@ -0,0 +1,6 @@
|
||||
contract Test {
|
||||
function type() public pure {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// ParserError: (29-33): Expected identifier but got 'type'
|
||||
@@ -0,0 +1,6 @@
|
||||
contract Test {
|
||||
function f(uint type) public pure {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// ParserError: (36-40): Expected ',' but got 'type'
|
||||
@@ -0,0 +1,5 @@
|
||||
contract Test {
|
||||
uint type;
|
||||
}
|
||||
// ----
|
||||
// ParserError: (25-29): Expected identifier but got 'type'
|
||||
@@ -0,0 +1,7 @@
|
||||
contract Test {
|
||||
function f() public pure {
|
||||
uint type;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// ParserError: (60-64): Expected ';' but got 'type'
|
||||
@@ -0,0 +1,6 @@
|
||||
contract Test {
|
||||
function f() public pure returns (bytes memory) {
|
||||
type(Test);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
@@ -0,0 +1,8 @@
|
||||
contract Test {
|
||||
function f() public pure {
|
||||
type(type(type(Test)));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (65-75): Invalid type for argument in function call. Contract type required, but type(contract Test) provided.
|
||||
// TypeError: (60-76): Invalid type for argument in function call. Contract type required, but tuple() provided.
|
||||
@@ -0,0 +1,9 @@
|
||||
contract Test {
|
||||
struct S { uint x; }
|
||||
function f() public pure {
|
||||
// Unsupported for now, but might be supported in the future
|
||||
type(S);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (154-155): Invalid type for argument in function call. Contract type required, but type(struct Test.S) provided.
|
||||
Reference in New Issue
Block a user