diff options
-rw-r--r-- | libsolidity/CompilerContext.h | 2 | ||||
-rw-r--r-- | libsolidity/ExpressionCompiler.cpp | 18 | ||||
-rw-r--r-- | libsolidity/Types.cpp | 38 | ||||
-rw-r--r-- | libsolidity/Types.h | 8 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 32 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 16 | ||||
-rw-r--r-- | test/libsolidity/solidityExecutionFramework.h | 14 |
7 files changed, 105 insertions, 23 deletions
diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 34b63a9c..39f4e75f 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -112,6 +112,8 @@ public: void appendProgramSize() { return m_asm.appendProgramSize(); } /// Adds data to the data section, pushes a reference to the stack eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); } + /// Appends the address (virtual, will be filled in by linker) of a library. + void appendLibraryAddress(std::string const& _identifier) { m_asm.appendLibraryAddress(_identifier); } /// Resets the stack of visited nodes with a new stack having only @c _node void resetVisitedNodes(ASTNode const* _node); /// Pops the stack of visited nodes diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 9cecf6b6..b22a78dc 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -772,9 +772,15 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) if (dynamic_cast<ContractType const*>(type.actualType().get())) { - auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration()); - solAssert(!!function, "Function not found in member access"); - m_context << m_context.functionEntryLabel(*function).pushTag(); + auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.type()); + if (funType.location() != FunctionType::Location::Internal) + m_context << funType.externalIdentifier(); + else + { + auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration()); + solAssert(!!function, "Function not found in member access"); + m_context << m_context.functionEntryLabel(*function).pushTag(); + } } else if (auto enumType = dynamic_cast<EnumType const*>(type.actualType().get())) m_context << enumType->memberValue(_memberAccess.memberName()); @@ -923,9 +929,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) utils().convertType(*variable->value()->type(), *variable->type()); } } - else if (dynamic_cast<ContractDefinition const*>(declaration)) + else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) { - // no-op + if (contract->isLibrary()) + //@todo name should be unique, change once we have module management + m_context.appendLibraryAddress(contract->name()); } else if (dynamic_cast<EventDefinition const*>(declaration)) { diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index d1c59ba5..5bc7cd43 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -971,7 +971,7 @@ MemberList const& ContractType::members() const for (auto const& it: m_contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(), + it.second->asMemberFunction(false), &it.second->declaration() )); m_members.reset(new MemberList(members)); @@ -1538,7 +1538,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con ); } -FunctionTypePointer FunctionType::asMemberFunction() const +FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const { TypePointers parameterTypes; for (auto const& t: m_parameterTypes) @@ -1563,7 +1563,7 @@ FunctionTypePointer FunctionType::asMemberFunction() const returnParameterTypes, m_parameterNames, returnParameterNames, - m_location, + _inLibrary ? Location::CallCode : m_location, m_arbitraryParameters, m_declaration, m_gasSet, @@ -1633,21 +1633,39 @@ u256 TypeType::storageSize() const << errinfo_comment("Storage size of non-storable type type requested.")); } +unsigned TypeType::sizeOnStack() const +{ + if (auto contractType = dynamic_cast<ContractType const*>(m_actualType.get())) + if (contractType->contractDefinition().isLibrary()) + return 1; + return 0; +} + MemberList const& TypeType::members() const { // We need to lazy-initialize it because of recursive references. if (!m_members) { MemberList::MemberMap members; - if (m_actualType->category() == Category::Contract && m_currentContract != nullptr) + if (m_actualType->category() == Category::Contract) { ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition(); - vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts(); - if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) - // We are accessing the type of a base contract, so add all public and protected - // members. Note that this does not add inherited functions on purpose. - for (Declaration const* decl: contract.inheritableMembers()) - members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); + if (contract.isLibrary()) + for (auto const& it: contract.interfaceFunctions()) + members.push_back(MemberList::Member( + it.second->declaration().name(), + it.second->asMemberFunction(true), // use callcode + &it.second->declaration() + )); + else if (m_currentContract != nullptr) + { + vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts(); + if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) + // We are accessing the type of a base contract, so add all public and protected + // members. Note that this does not add inherited functions on purpose. + for (Declaration const* decl: contract.inheritableMembers()) + members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); + } } else if (m_actualType->category() == Category::Enum) { diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 17ae0ce0..11218b7a 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -517,7 +517,7 @@ private: }; /** - * The type of a contract instance, there is one distinct type for each contract definition. + * The type of a contract instance or library, there is one distinct type for each contract definition. */ class ContractType: public Type { @@ -788,7 +788,8 @@ public: /// removed and the location of reference types is changed from CallData to Memory. /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. - FunctionTypePointer asMemberFunction() const; + /// @param _inLibrary if true, uses CallCode as location. + FunctionTypePointer asMemberFunction(bool _inLibrary) const; private: static TypePointers parseElementaryTypeVector(strings const& _types); @@ -851,6 +852,7 @@ public: /** * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example * of a TypeType. + * For super contracts or libraries, this has members directly. */ class TypeType: public Type { @@ -865,7 +867,7 @@ public: virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned sizeOnStack() const override { return 0; } + virtual unsigned sizeOnStack() const override; virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } virtual MemberList const& members() const override; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index aa423330..c56845aa 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5230,6 +5230,38 @@ BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(library_call) +{ + char const* sourceCode = R"( + library Lib { function m(uint x, uint y) returns (uint) { return x * y; } } + contract Test { + function f(uint x) returns (uint) { + return Lib.m(x, 9); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(33) * 9)); +} + +BOOST_AUTO_TEST_CASE(library_stray_values) +{ + char const* sourceCode = R"( + library Lib { function m(uint x, uint y) returns (uint) { return x * y; } } + contract Test { + function f(uint x) returns (uint) { + Lib; + Lib.m; + return x + 9; + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(42))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index adff9499..99497ff3 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2228,6 +2228,22 @@ BOOST_AUTO_TEST_CASE(valid_library) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(call_to_library_function) +{ + char const* text = R"( + library Lib { + uint constant public pimil = 3141592; + function min(uint x, uint y) returns (uint); + } + contract Test { + function f() { + uint t = Lib.min(Lib.pimil(), 7); + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) { char const* sourceCode = R"( diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index 8823114a..3370044c 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -53,14 +53,17 @@ public: std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "", - bytes const& _arguments = bytes() + bytes const& _arguments = bytes(), + std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>() ) { m_compiler.reset(false, m_addStandardSources); m_compiler.addSource("", _sourceCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - bytes code = m_compiler.object(_contractName).bytecode; - sendMessage(code + _arguments, true, _value); + eth::LinkerObject obj = m_compiler.object(_contractName); + obj.link(_libraryAddresses); + BOOST_REQUIRE(obj.linkReferences.empty()); + sendMessage(obj.bytecode + _arguments, true, _value); return m_output; } @@ -76,10 +79,11 @@ public: std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "", - bytes const& _arguments = bytes() + bytes const& _arguments = bytes(), + std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>() ) { - compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments); + compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments, _libraryAddresses); BOOST_REQUIRE(!m_output.empty()); return m_output; } |