mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10030 from ethereum/issue-7627
Fix dependency tracking for abstract contracts
This commit is contained in:
commit
0639467237
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* SMTChecker: Fix lack of reporting potential violations when using only the CHC engine.
|
* SMTChecker: Fix lack of reporting potential violations when using only the CHC engine.
|
||||||
|
* Code generator: Fix missing creation dependency tracking for abstract contracts.
|
||||||
|
|
||||||
|
|
||||||
### 0.7.4 (2020-10-19)
|
### 0.7.4 (2020-10-19)
|
||||||
|
@ -50,7 +50,7 @@ using namespace solidity::frontend;
|
|||||||
|
|
||||||
pair<string, string> IRGenerator::run(
|
pair<string, string> IRGenerator::run(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, string const> const& _otherYulSources
|
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
string const ir = yul::reindent(generate(_contract, _otherYulSources));
|
string const ir = yul::reindent(generate(_contract, _otherYulSources));
|
||||||
@ -78,7 +78,7 @@ pair<string, string> IRGenerator::run(
|
|||||||
|
|
||||||
string IRGenerator::generate(
|
string IRGenerator::generate(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, string const> const& _otherYulSources
|
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto subObjectSources = [&_otherYulSources](std::set<ContractDefinition const*, ASTNode::CompareByID> const& subObjects) -> string
|
auto subObjectSources = [&_otherYulSources](std::set<ContractDefinition const*, ASTNode::CompareByID> const& subObjects) -> string
|
||||||
|
@ -53,13 +53,13 @@ public:
|
|||||||
/// (or just pretty-printed, depending on the optimizer settings).
|
/// (or just pretty-printed, depending on the optimizer settings).
|
||||||
std::pair<std::string, std::string> run(
|
std::pair<std::string, std::string> run(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, std::string const> const& _otherYulSources
|
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string generate(
|
std::string generate(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, std::string const> const& _otherYulSources
|
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
||||||
);
|
);
|
||||||
std::string generate(Block const& _block);
|
std::string generate(Block const& _block);
|
||||||
|
|
||||||
|
@ -525,6 +525,7 @@ bool CompilerStack::compile(State _stopAfter)
|
|||||||
|
|
||||||
// Only compile contracts individually which have been requested.
|
// Only compile contracts individually which have been requested.
|
||||||
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
||||||
|
|
||||||
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 (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||||
@ -1160,11 +1161,15 @@ void CompilerStack::compileContract(
|
|||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
||||||
|
|
||||||
if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed())
|
if (_otherCompilers.count(&_contract))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto const* dependency: _contract.annotation().contractDependencies)
|
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||||
compileContract(*dependency, _otherCompilers);
|
compileContract(*dependency, _otherCompilers);
|
||||||
|
|
||||||
|
if (!_contract.canBeDeployed())
|
||||||
|
return;
|
||||||
|
|
||||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||||
|
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_revertStrings, m_optimiserSettings);
|
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_revertStrings, m_optimiserSettings);
|
||||||
@ -1227,21 +1232,20 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
|
|||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateIR with errors."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateIR with errors."));
|
||||||
|
|
||||||
if (!_contract.canBeDeployed())
|
|
||||||
return;
|
|
||||||
|
|
||||||
map<ContractDefinition const*, string const> otherYulSources;
|
|
||||||
|
|
||||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||||
if (!compiledContract.yulIR.empty())
|
if (!compiledContract.yulIR.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string dependenciesSource;
|
string dependenciesSource;
|
||||||
for (auto const* dependency: _contract.annotation().contractDependencies)
|
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||||
{
|
|
||||||
generateIR(*dependency);
|
generateIR(*dependency);
|
||||||
otherYulSources.emplace(dependency, m_contracts.at(dependency->fullyQualifiedName()).yulIR);
|
|
||||||
}
|
if (!_contract.canBeDeployed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
map<ContractDefinition const*, string_view const> otherYulSources;
|
||||||
|
for (auto const& pair: m_contracts)
|
||||||
|
otherYulSources.emplace(pair.second.contract, pair.second.yulIR);
|
||||||
|
|
||||||
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings);
|
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings);
|
||||||
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources);
|
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources);
|
||||||
|
@ -1505,6 +1505,96 @@ BOOST_AUTO_TEST_CASE(stopAfter_ast_output)
|
|||||||
BOOST_CHECK(result["sources"]["a.sol"]["ast"].isObject());
|
BOOST_CHECK(result["sources"]["a.sol"]["ast"].isObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract)
|
||||||
|
{
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources": {
|
||||||
|
"BlockRewardAuRaBase.sol": {
|
||||||
|
"content": " contract Sacrifice { constructor() payable {} } abstract contract BlockRewardAuRaBase { function _transferNativeReward() internal { new Sacrifice(); } function _distributeTokenRewards() internal virtual; } "
|
||||||
|
},
|
||||||
|
"BlockRewardAuRaCoins.sol": {
|
||||||
|
"content": " import \"./BlockRewardAuRaBase.sol\"; contract BlockRewardAuRaCoins is BlockRewardAuRaBase { function transferReward() public { _transferNativeReward(); } function _distributeTokenRewards() internal override {} } "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"outputSelection": {
|
||||||
|
"BlockRewardAuRaCoins.sol": {
|
||||||
|
"BlockRewardAuRaCoins": ["ir", "evm.bytecode.sourceMap"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
Json::Value parsedInput;
|
||||||
|
BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
|
||||||
|
|
||||||
|
solidity::frontend::StandardCompiler compiler;
|
||||||
|
Json::Value result = compiler.compile(parsedInput);
|
||||||
|
|
||||||
|
BOOST_REQUIRE(result["contracts"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"].size() == 1);
|
||||||
|
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].size() == 1);
|
||||||
|
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["ir"].isString());
|
||||||
|
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"]["bytecode"].isObject());
|
||||||
|
BOOST_REQUIRE(result["sources"].isObject());
|
||||||
|
BOOST_REQUIRE(result["sources"].size() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract_yul)
|
||||||
|
{
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources": {
|
||||||
|
"A.sol": {
|
||||||
|
"content": "contract A {} contract B {} contract C { constructor() { new B(); } } contract D {}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"outputSelection": {
|
||||||
|
"A.sol": {
|
||||||
|
"C": ["ir"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
Json::Value parsedInput;
|
||||||
|
BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
|
||||||
|
|
||||||
|
solidity::frontend::StandardCompiler compiler;
|
||||||
|
Json::Value result = compiler.compile(parsedInput);
|
||||||
|
|
||||||
|
BOOST_REQUIRE(result["contracts"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"].size() == 1);
|
||||||
|
BOOST_REQUIRE(result["contracts"]["A.sol"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"]["A.sol"].size() == 1);
|
||||||
|
BOOST_REQUIRE(result["contracts"]["A.sol"]["C"].isObject());
|
||||||
|
BOOST_REQUIRE(result["contracts"]["A.sol"]["C"]["ir"].isString());
|
||||||
|
|
||||||
|
const string& irCode = result["contracts"]["A.sol"]["C"]["ir"].asString();
|
||||||
|
|
||||||
|
// Make sure C and B contracts are deployed
|
||||||
|
BOOST_REQUIRE(irCode.find("object \"C") != string::npos);
|
||||||
|
BOOST_REQUIRE(irCode.find("object \"B") != string::npos);
|
||||||
|
|
||||||
|
// Make sure A and D are NOT deployed as they were not requested and are not
|
||||||
|
// in any dependency
|
||||||
|
BOOST_REQUIRE(irCode.find("object \"A") == string::npos);
|
||||||
|
BOOST_REQUIRE(irCode.find("object \"D") == string::npos);
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_REQUIRE(result["sources"].isObject());
|
||||||
|
BOOST_REQUIRE(result["sources"].size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
} // end namespaces
|
} // end namespaces
|
||||||
|
@ -11,5 +11,7 @@ contract D {
|
|||||||
return test();
|
return test();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// f() -> true
|
// f() -> true
|
||||||
|
@ -11,5 +11,7 @@ contract D {
|
|||||||
return test();
|
return test();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// f() -> 2
|
// f() -> 2
|
||||||
|
Loading…
Reference in New Issue
Block a user