Only avoid collision if it's the same file

@chriseth had suggested that it would be better if contracts were
referenced in a file:contract notation, and that we output .bin files
that prepend original path names if necessary to avoid a collision.

This commit is mostly a draft; it still needs to be run through the test
suite.
This commit is contained in:
Rhett Aultman 2016-12-11 14:17:38 -08:00 committed by Rhett Aultman
parent 9e88f1eebe
commit 071b936b37
5 changed files with 52 additions and 14 deletions

View File

@ -190,6 +190,12 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat
}
std::string ContractDefinition::fullyQualifiedName() const
{
std::string qualifiedName = *(location().sourceName) + ":" + name();
return qualifiedName;
}
vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
{
if (!m_inheritableMembers)

View File

@ -358,6 +358,8 @@ public:
Json::Value const& devDocumentation() const;
void setDevDocumentation(Json::Value const& _devDocumentation);
std::string fullyQualifiedName() const;
virtual TypePointer type() const override;
virtual ContractDefinitionAnnotation& annotation() const override;

View File

@ -21,6 +21,7 @@
* Full-stack compiler that converts a source code string to bytecode.
*/
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/Version.h>
@ -180,17 +181,18 @@ bool CompilerStack::parse()
if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false;
if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false;
if (!resolver.resolveNamesAndTypes(*contract)) return false;
if (m_contracts.find(contract->name()) != m_contracts.end())
if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end())
{
const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract;
const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract;
if (contract != existingContract)
BOOST_THROW_EXCEPTION(DeclarationError() <<
BOOST_THROW_EXCEPTION(CompilerError() <<
errinfo_sourceLocation(contract->location()) <<
errinfo_comment(contract->name() + " is already defined.") <<
errinfo_secondarySourceLocation(
SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:"));
SecondarySourceLocation().append("Previous definition is here:", existingContract->location())));
}
m_contracts[contract->name()].contract = contract;
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
if (!checkLibraryNameClashes())
@ -211,9 +213,9 @@ bool CompilerStack::parse()
else
noErrors = false;
if (m_contracts.find(contract->name()) != m_contracts.end())
if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end())
{
const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract;
const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract;
if (contract != existingContract)
BOOST_THROW_EXCEPTION(CompilerError() <<
errinfo_sourceLocation(contract->location()) <<
@ -221,7 +223,7 @@ bool CompilerStack::parse()
+ *(existingContract->location().sourceName)));
}
m_contracts[contract->name()].contract = contract;
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
if (noErrors)
@ -335,6 +337,28 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
return c.runtimeSourceMapping.get();
}
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
{
// Look up the contract (by its fully-qualified name)
Contract const& matchContract = m_contracts.at(_contractName);
// Check to see if it could collide on name
for (auto const& contract: m_contracts)
{
if (contract.second.contract->name() == matchContract.contract->name() &&
contract.second.contract != matchContract.contract)
{
// If it does, then return its fully-qualified name, made fs-friendly
std::string friendlyName = boost::algorithm::replace_all_copy(_contractName, "/", "_");
boost::algorithm::replace_all(friendlyName, ":", "_");
boost::algorithm::replace_all(friendlyName, ".", "_");
return friendlyName;
}
}
// If no collision, return the contract's name
// String is copied to ensure that the contract's name can't be messed with
return std::string(matchContract.contract->name());
}
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
{
return contract(_contractName).object;
@ -598,7 +622,7 @@ bool CompilerStack::checkLibraryNameClashes()
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
if (contract->isLibrary())
{
if (libraries.count(contract->name()))
if (libraries.count(contract->fullyQualifiedName()))
{
auto err = make_shared<Error>(Error::Type::DeclarationError);
*err <<
@ -615,7 +639,7 @@ bool CompilerStack::checkLibraryNameClashes()
clashFound = true;
}
else
libraries[contract->name()] = contract->location();
libraries[contract->fullyQualifiedName()] = contract->location();
}
return !clashFound;
}
@ -648,7 +672,7 @@ void CompilerStack::compileContract(
compileContract(*dependency, _compiledContracts);
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.name());
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
string onChainMetadata = createOnChainMetadata(compiledContract);
bytes cborEncodedMetadata =
// CBOR-encoding of {"bzzr0": dev::swarmHash(onChainMetadata)}
@ -694,7 +718,7 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa
for (auto const& it: m_sources)
for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contractName = contract->name();
contractName = contract->fullyQualifiedName();
auto it = m_contracts.find(contractName);
if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));

View File

@ -148,6 +148,10 @@ public:
/// @returns the string that provides a mapping between runtime bytecode and sourcecode.
/// if the contract does not (yet) have bytecode.
std::string const* runtimeSourceMapping(std::string const& _contractName = "") const;
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
std::string const filesystemFriendlyName(std::string const& _contractName) const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is
/// returned by the constructor or the zero-h256 if the contract still needs to be linked or
/// does not have runtime code.

View File

@ -185,8 +185,10 @@ void CommandLineInterface::handleBinary(string const& _contract)
{
if (m_args.count(g_argBinary))
{
if (m_args.count(g_argOutputDir))
createFile(_contract + ".bin", m_compiler->object(_contract).toHex());
if (m_args.count("output-dir"))
{
createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", m_compiler->object(_contract).toHex());
}
else
{
cout << "Binary: " << endl;