Fix dependency tracking for abstract contracts for Yul codegen

This commit is contained in:
Mathias Baumann 2020-10-14 12:42:44 +02:00
parent e991465336
commit a4dc110b38
6 changed files with 67 additions and 13 deletions

View File

@ -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

View File

@ -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);

View File

@ -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()))
@ -1231,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);

View File

@ -1521,7 +1521,7 @@ BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract)
"settings": { "settings": {
"outputSelection": { "outputSelection": {
"BlockRewardAuRaCoins.sol": { "BlockRewardAuRaCoins.sol": {
"BlockRewardAuRaCoins": ["evm.bytecode.sourceMap"] "BlockRewardAuRaCoins": ["ir", "evm.bytecode.sourceMap"]
} }
} }
} }
@ -1540,11 +1540,61 @@ BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract)
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].size() == 1); BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].size() == 1);
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"].isObject()); BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"].isObject());
BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"].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["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"]["bytecode"].isObject());
BOOST_REQUIRE(result["sources"].isObject()); BOOST_REQUIRE(result["sources"].isObject());
BOOST_REQUIRE(result["sources"].size() == 2); 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

View File

@ -11,5 +11,7 @@ contract D {
return test(); return test();
} }
} }
// ====
// compileViaYul: true
// ---- // ----
// f() -> true // f() -> true

View File

@ -11,5 +11,7 @@ contract D {
return test(); return test();
} }
} }
// ====
// compileViaYul: true
// ---- // ----
// f() -> 2 // f() -> 2