From d2f493268bf21336322470141c7e70aacd1ced06 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 22 Jan 2019 16:15:55 +0000 Subject: [PATCH] Provide access to the name of contracts. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 2 + libsolidity/analysis/ViewPureChecker.cpp | 3 +- libsolidity/ast/Types.cpp | 5 +- libsolidity/codegen/CompilerUtils.h | 10 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 12 +++++ test/libsolidity/SolidityEndToEndTest.cpp | 49 +++++++++++++++++++ .../syntaxTests/metaTypes/name.sol | 5 ++ .../syntaxTests/metaTypes/name_constant.sol | 4 ++ .../metaTypes/name_other_contract.sol | 10 ++++ 10 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 test/libsolidity/syntaxTests/metaTypes/name.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/name_constant.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol diff --git a/Changelog.md b/Changelog.md index 752808afc..78006cf0c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a431930f6..c4f42057c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -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; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 7df7ac17c..88ca727ce 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -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 static const payableMembers{ {MagicType::Kind::Message, "value"} diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index dc2b2a850..4748b8217 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3490,8 +3490,9 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); if (contract.canBeDeployed()) return MemberList::MemberMap({ - {"creationCode", std::make_shared(DataLocation::Memory)}, - {"runtimeCode", std::make_shared(DataLocation::Memory)} + {"creationCode", make_shared(DataLocation::Memory)}, + {"runtimeCode", make_shared(DataLocation::Memory)}, + {"name", make_shared(DataLocation::Memory, true)}, }); else return {}; diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 610a43bcd..90657626e 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -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
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); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c03cf2c16..69c4b26ec 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1361,6 +1361,18 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ); m_context << Instruction::POP; } + else if (member == "name") + { + TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); + ContractDefinition const& contract = dynamic_cast(*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; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index e4df7f903..a5f8c2743 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -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() } diff --git a/test/libsolidity/syntaxTests/metaTypes/name.sol b/test/libsolidity/syntaxTests/metaTypes/name.sol new file mode 100644 index 000000000..87585b029 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/name.sol @@ -0,0 +1,5 @@ +contract Test { + function f() public pure returns (string memory) { + return type(Test).name; + } +} diff --git a/test/libsolidity/syntaxTests/metaTypes/name_constant.sol b/test/libsolidity/syntaxTests/metaTypes/name_constant.sol new file mode 100644 index 000000000..a90160389 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/name_constant.sol @@ -0,0 +1,4 @@ +contract C { + string public constant name = type(C).name; +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol new file mode 100644 index 000000000..baa4a2286 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol @@ -0,0 +1,10 @@ +contract Test { + function f() public pure returns (string memory) { + return type(C).name; + } +} + +contract C { + function f() pure public { + } +}