Merge pull request #6097 from ethereum/meta-name

Provide access to the name of contracts.
This commit is contained in:
chriseth 2019-02-26 15:38:06 +01:00 committed by GitHub
commit da7139afc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 93 additions and 8 deletions

View File

@ -1,6 +1,7 @@
### 0.5.5 (unreleased)
Language Features:
* Meta programming: Provide access to the name of contracts via ``type(C).name``.
Compiler Features:

View File

@ -2154,6 +2154,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"Circular reference for contract code access."
);
}
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
annotation.isPure = true;
}
return false;

View File

@ -340,7 +340,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::Message, "data"},
{MagicType::Kind::Message, "sig"},
{MagicType::Kind::MetaType, "creationCode"},
{MagicType::Kind::MetaType, "runtimeCode"}
{MagicType::Kind::MetaType, "runtimeCode"},
{MagicType::Kind::MetaType, "name"},
};
set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"}

View File

@ -3490,8 +3490,9 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
if (contract.canBeDeployed())
return MemberList::MemberMap({
{"creationCode", std::make_shared<ArrayType>(DataLocation::Memory)},
{"runtimeCode", std::make_shared<ArrayType>(DataLocation::Memory)}
{"creationCode", make_shared<ArrayType>(DataLocation::Memory)},
{"runtimeCode", make_shared<ArrayType>(DataLocation::Memory)},
{"name", make_shared<ArrayType>(DataLocation::Memory, true)},
});
else
return {};

View File

@ -186,6 +186,11 @@ public:
/// Stack post:
void memoryCopy();
/// Stores the given string in memory.
/// Stack pre: mempos
/// Stack post:
void storeStringData(bytesConstRef _data);
/// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true)
/// external function type <address><function identifier> into two stack slots:
/// address (right aligned), function identifier (right aligned)
@ -285,11 +290,6 @@ private:
/// Address of the precompiled identity contract.
static unsigned const identityContractAddress;
/// Stores the given string in memory.
/// Stack pre: mempos
/// Stack post:
void storeStringData(bytesConstRef _data);
/// Appends code that cleans higher-order bits for integer types.
void cleanHigherOrderBits(IntegerType const& _typeOnStack);

View File

@ -1361,6 +1361,18 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
m_context << Instruction::POP;
}
else if (member == "name")
{
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
m_context << u256(contract.name().length() + 32);
utils().allocateMemory();
// store string length
m_context << u256(contract.name().length()) << Instruction::DUP2 << Instruction::MSTORE;
// adjust pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
utils().storeStringData(contract.name());
}
else
solAssert(false, "Unknown magic member.");
break;

View File

@ -15113,6 +15113,55 @@ BOOST_AUTO_TEST_CASE(code_access_content)
ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(contract_name)
{
char const* sourceCode = R"(
contract C {
string public nameAccessor = type(C).name;
string public constant constantNameAccessor = type(C).name;
function name() public pure returns (string memory) {
return type(C).name;
}
}
contract D is C {
function name() public pure returns (string memory) {
return type(D).name;
}
function name2() public pure returns (string memory) {
return type(C).name;
}
}
contract ThisIsAVeryLongContractNameExceeding256bits {
string public nameAccessor = type(ThisIsAVeryLongContractNameExceeding256bits).name;
string public constant constantNameAccessor = type(ThisIsAVeryLongContractNameExceeding256bits).name;
function name() public pure returns (string memory) {
return type(ThisIsAVeryLongContractNameExceeding256bits).name;
}
}
)";
compileAndRun(sourceCode, 0, "C");
bytes argsC = encodeArgs(u256(0x20), u256(1), "C");
ABI_CHECK(callContractFunction("name()"), argsC);
ABI_CHECK(callContractFunction("nameAccessor()"), argsC);
ABI_CHECK(callContractFunction("constantNameAccessor()"), argsC);
compileAndRun(sourceCode, 0, "D");
bytes argsD = encodeArgs(u256(0x20), u256(1), "D");
ABI_CHECK(callContractFunction("name()"), argsD);
ABI_CHECK(callContractFunction("name2()"), argsC);
string longName = "ThisIsAVeryLongContractNameExceeding256bits";
compileAndRun(sourceCode, 0, longName);
bytes argsLong = encodeArgs(u256(0x20), u256(longName.length()), longName);
ABI_CHECK(callContractFunction("name()"), argsLong);
ABI_CHECK(callContractFunction("nameAccessor()"), argsLong);
ABI_CHECK(callContractFunction("constantNameAccessor()"), argsLong);
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -0,0 +1,5 @@
contract Test {
function f() public pure returns (string memory) {
return type(Test).name;
}
}

View File

@ -0,0 +1,4 @@
contract C {
string public constant name = type(C).name;
}
// ----

View File

@ -0,0 +1,10 @@
contract Test {
function f() public pure returns (string memory) {
return type(C).name;
}
}
contract C {
function f() pure public {
}
}