mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #121 from chriseth/dependencies
Resolve binary dependencies properly.
This commit is contained in:
commit
ab433c9a78
@ -25,6 +25,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
#include <libsolidity/ASTForward.h>
|
#include <libsolidity/ASTForward.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -53,6 +54,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation
|
|||||||
/// List of all (direct and indirect) base contracts in order from derived to
|
/// List of all (direct and indirect) base contracts in order from derived to
|
||||||
/// base, including the contract itself.
|
/// base, including the contract itself.
|
||||||
std::vector<ContractDefinition const*> linearizedBaseContracts;
|
std::vector<ContractDefinition const*> linearizedBaseContracts;
|
||||||
|
/// List of contracts this contract creates, i.e. which need to be compiled first.
|
||||||
|
/// Also includes all contracts from @a linearizedBaseContracts.
|
||||||
|
std::set<ContractDefinition const*> contractDependencies;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VariableDeclarationAnnotation: ASTAnnotation
|
struct VariableDeclarationAnnotation: ASTAnnotation
|
||||||
|
@ -170,22 +170,8 @@ bool CompilerStack::compile(bool _optimize, unsigned _runs)
|
|||||||
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
|
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||||
{
|
compileContract(_optimize, _runs, *contract, compiledContracts);
|
||||||
if (!contract->annotation().isFullyImplemented)
|
|
||||||
continue;
|
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
|
|
||||||
compiler->compileContract(*contract, compiledContracts);
|
|
||||||
Contract& compiledContract = m_contracts.at(contract->name());
|
|
||||||
compiledContract.compiler = compiler;
|
|
||||||
compiledContract.object = compiler->assembledObject();
|
|
||||||
compiledContract.runtimeObject = compiler->runtimeObject();
|
|
||||||
compiledContracts[compiledContract.contract] = &compiler->assembly();
|
|
||||||
|
|
||||||
Compiler cloneCompiler(_optimize, _runs);
|
|
||||||
cloneCompiler.compileClone(*contract, compiledContracts);
|
|
||||||
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,6 +361,31 @@ void CompilerStack::resolveImports()
|
|||||||
swap(m_sourceOrder, sourceOrder);
|
swap(m_sourceOrder, sourceOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerStack::compileContract(
|
||||||
|
bool _optimize,
|
||||||
|
unsigned _runs,
|
||||||
|
ContractDefinition const& _contract,
|
||||||
|
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented)
|
||||||
|
return;
|
||||||
|
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||||
|
compileContract(_optimize, _runs, *dependency, _compiledContracts);
|
||||||
|
|
||||||
|
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
|
||||||
|
compiler->compileContract(_contract, _compiledContracts);
|
||||||
|
Contract& compiledContract = m_contracts.at(_contract.name());
|
||||||
|
compiledContract.compiler = compiler;
|
||||||
|
compiledContract.object = compiler->assembledObject();
|
||||||
|
compiledContract.runtimeObject = compiler->runtimeObject();
|
||||||
|
_compiledContracts[compiledContract.contract] = &compiler->assembly();
|
||||||
|
|
||||||
|
Compiler cloneCompiler(_optimize, _runs);
|
||||||
|
cloneCompiler.compileClone(_contract, _compiledContracts);
|
||||||
|
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
||||||
|
}
|
||||||
|
|
||||||
std::string CompilerStack::defaultContractName() const
|
std::string CompilerStack::defaultContractName() const
|
||||||
{
|
{
|
||||||
return contract("").contract->name();
|
return contract("").contract->name();
|
||||||
|
@ -39,6 +39,7 @@ namespace dev
|
|||||||
|
|
||||||
namespace eth
|
namespace eth
|
||||||
{
|
{
|
||||||
|
class Assembly;
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
using AssemblyItems = std::vector<AssemblyItem>;
|
using AssemblyItems = std::vector<AssemblyItem>;
|
||||||
}
|
}
|
||||||
@ -195,6 +196,13 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void resolveImports();
|
void resolveImports();
|
||||||
|
/// Compile a single contract and put the result in @a _compiledContracts.
|
||||||
|
void compileContract(
|
||||||
|
bool _optimize,
|
||||||
|
unsigned _runs,
|
||||||
|
ContractDefinition const& _contract,
|
||||||
|
std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
|
||||||
|
);
|
||||||
|
|
||||||
Contract const& contract(std::string const& _contractName = "") const;
|
Contract const& contract(std::string const& _contractName = "") const;
|
||||||
Source const& source(std::string const& _sourceName = "") const;
|
Source const& source(std::string const& _sourceName = "") const;
|
||||||
|
@ -216,6 +216,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
|||||||
if (result.empty())
|
if (result.empty())
|
||||||
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
|
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
|
||||||
_contract.annotation().linearizedBaseContracts = result;
|
_contract.annotation().linearizedBaseContracts = result;
|
||||||
|
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _T>
|
template <class _T>
|
||||||
|
@ -908,12 +908,15 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
|||||||
typeError(_newExpression, "Trying to create an instance of an abstract contract.");
|
typeError(_newExpression, "Trying to create an instance of an abstract contract.");
|
||||||
|
|
||||||
auto scopeContract = _newExpression.contractName().annotation().contractScope;
|
auto scopeContract = _newExpression.contractName().annotation().contractScope;
|
||||||
auto const& bases = contract->annotation().linearizedBaseContracts;
|
scopeContract->annotation().contractDependencies.insert(contract);
|
||||||
solAssert(!bases.empty(), "Linearized base contracts not yet available.");
|
solAssert(
|
||||||
if (find(bases.begin(), bases.end(), scopeContract) != bases.end())
|
!contract->annotation().linearizedBaseContracts.empty(),
|
||||||
|
"Linearized base contracts not yet available."
|
||||||
|
);
|
||||||
|
if (contractDependenciesAreCyclic(*scopeContract))
|
||||||
typeError(
|
typeError(
|
||||||
_newExpression,
|
_newExpression,
|
||||||
"Circular reference for contract creation: cannot create instance of derived or same contract."
|
"Circular reference for contract creation (cannot create instance of derived or same contract)."
|
||||||
);
|
);
|
||||||
|
|
||||||
auto contractType = make_shared<ContractType>(*contract);
|
auto contractType = make_shared<ContractType>(*contract);
|
||||||
@ -1118,6 +1121,22 @@ void TypeChecker::endVisit(Literal const& _literal)
|
|||||||
fatalTypeError(_literal, "Invalid literal value.");
|
fatalTypeError(_literal, "Invalid literal value.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TypeChecker::contractDependenciesAreCyclic(
|
||||||
|
ContractDefinition const& _contract,
|
||||||
|
std::set<ContractDefinition const*> const& _seenContracts
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
// Naive depth-first search that remembers nodes already seen.
|
||||||
|
if (_seenContracts.count(&_contract))
|
||||||
|
return true;
|
||||||
|
set<ContractDefinition const*> seen(_seenContracts);
|
||||||
|
seen.insert(&_contract);
|
||||||
|
for (auto const* c: _contract.annotation().contractDependencies)
|
||||||
|
if (contractDependenciesAreCyclic(*c, seen))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Declaration const& TypeChecker::dereference(Identifier const& _identifier)
|
Declaration const& TypeChecker::dereference(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored.");
|
solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored.");
|
||||||
|
@ -99,6 +99,11 @@ private:
|
|||||||
virtual void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
virtual void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
||||||
virtual void endVisit(Literal const& _literal) override;
|
virtual void endVisit(Literal const& _literal) override;
|
||||||
|
|
||||||
|
bool contractDependenciesAreCyclic(
|
||||||
|
ContractDefinition const& _contract,
|
||||||
|
std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>()
|
||||||
|
) const;
|
||||||
|
|
||||||
/// @returns the referenced declaration and throws on error.
|
/// @returns the referenced declaration and throws on error.
|
||||||
Declaration const& dereference(Identifier const& _identifier);
|
Declaration const& dereference(Identifier const& _identifier);
|
||||||
|
|
||||||
|
@ -5580,6 +5580,16 @@ BOOST_AUTO_TEST_CASE(version_stamp_for_libraries)
|
|||||||
BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP));
|
BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(contract_binary_dependencies)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract A { function f() { new B(); } }
|
||||||
|
contract B { function f() { } }
|
||||||
|
contract C { function f() { new B(); } }
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
|
BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
@ -2366,6 +2366,26 @@ BOOST_AUTO_TEST_CASE(non_initialized_references)
|
|||||||
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text, true), Warning);
|
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text, true), Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(cyclic_binary_dependency)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract A { function f() { new B(); } }
|
||||||
|
contract B { function f() { new C(); } }
|
||||||
|
contract C { function f() { new A(); } }
|
||||||
|
)";
|
||||||
|
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract A is B { }
|
||||||
|
contract B { function f() { new C(); } }
|
||||||
|
contract C { function f() { new A(); } }
|
||||||
|
)";
|
||||||
|
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user