mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #70 from chriseth/sol_libraries2
Commandline interface for linker.
This commit is contained in:
commit
9309b6aa82
@ -181,6 +181,16 @@ eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool
|
|||||||
return object();
|
return object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerStack::link(const std::map<string, h160>& _libraries)
|
||||||
|
{
|
||||||
|
for (auto& contract: m_contracts)
|
||||||
|
{
|
||||||
|
contract.second.object.link(_libraries);
|
||||||
|
contract.second.runtimeObject.link(_libraries);
|
||||||
|
contract.second.cloneObject.link(_libraries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
||||||
{
|
{
|
||||||
Contract const& currentContract = contract(_contractName);
|
Contract const& currentContract = contract(_contractName);
|
||||||
|
@ -96,9 +96,12 @@ public:
|
|||||||
/// @returns the compiled linker object
|
/// @returns the compiled linker object
|
||||||
eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false);
|
eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false);
|
||||||
|
|
||||||
/// @returns the assembled bytecode for a contract (empty if it has to be linked or lacks implementation).
|
/// Inserts the given addresses into the linker objects of all compiled contracts.
|
||||||
|
void link(std::map<std::string, h160> const& _libraries);
|
||||||
|
|
||||||
|
/// @returns the assembled object for a contract.
|
||||||
eth::LinkerObject const& object(std::string const& _contractName = "") const;
|
eth::LinkerObject const& object(std::string const& _contractName = "") const;
|
||||||
/// @returns the runtime bytecode for the contract (empty if it has to be linked or lacks implementation).
|
/// @returns the runtime object for the contract.
|
||||||
eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const;
|
eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const;
|
||||||
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
|
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
|
||||||
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
|
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
|
||||||
|
@ -925,7 +925,10 @@ bool ContractType::operator==(Type const& _other) const
|
|||||||
|
|
||||||
string ContractType::toString(bool) const
|
string ContractType::toString(bool) const
|
||||||
{
|
{
|
||||||
return "contract " + string(m_super ? "super " : "") + m_contract.name();
|
return
|
||||||
|
string(m_contract.isLibrary() ? "library " : "contract ") +
|
||||||
|
string(m_super ? "super " : "") +
|
||||||
|
m_contract.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
MemberList const& ContractType::members() const
|
MemberList const& ContractType::members() const
|
||||||
@ -971,7 +974,7 @@ MemberList const& ContractType::members() const
|
|||||||
for (auto const& it: m_contract.interfaceFunctions())
|
for (auto const& it: m_contract.interfaceFunctions())
|
||||||
members.push_back(MemberList::Member(
|
members.push_back(MemberList::Member(
|
||||||
it.second->declaration().name(),
|
it.second->declaration().name(),
|
||||||
it.second->asMemberFunction(false),
|
it.second->asMemberFunction(m_contract.isLibrary()),
|
||||||
&it.second->declaration()
|
&it.second->declaration()
|
||||||
));
|
));
|
||||||
m_members.reset(new MemberList(members));
|
m_members.reset(new MemberList(members));
|
||||||
|
@ -69,6 +69,7 @@ static string const g_argOpcodesStr = "opcodes";
|
|||||||
static string const g_argNatspecDevStr = "devdoc";
|
static string const g_argNatspecDevStr = "devdoc";
|
||||||
static string const g_argNatspecUserStr = "userdoc";
|
static string const g_argNatspecUserStr = "userdoc";
|
||||||
static string const g_argAddStandard = "add-std";
|
static string const g_argAddStandard = "add-std";
|
||||||
|
static string const g_stdinFileName = "<stdin>";
|
||||||
|
|
||||||
/// Possible arguments to for --combined-json
|
/// Possible arguments to for --combined-json
|
||||||
static set<string> const g_combinedJsonArgs{
|
static set<string> const g_combinedJsonArgs{
|
||||||
@ -282,6 +283,39 @@ void CommandLineInterface::handleGasEstimation(string const& _contract)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommandLineInterface::parseLibraryOption(string const& _input)
|
||||||
|
{
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
string data = fs::is_regular_file(_input) ? contentsString(_input) : _input;
|
||||||
|
|
||||||
|
vector<string> libraries;
|
||||||
|
boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on);
|
||||||
|
for (string const& lib: libraries)
|
||||||
|
if (!lib.empty())
|
||||||
|
{
|
||||||
|
auto colon = lib.find(':');
|
||||||
|
if (colon == string::npos)
|
||||||
|
{
|
||||||
|
cerr << "Colon separator missing in library address specifier \"" << lib << "\"" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
string libName(lib.begin(), lib.begin() + colon);
|
||||||
|
string addrString(lib.begin() + colon + 1, lib.end());
|
||||||
|
boost::trim(libName);
|
||||||
|
boost::trim(addrString);
|
||||||
|
bytes binAddr = fromHex(addrString);
|
||||||
|
h160 address(binAddr, h160::AlignRight);
|
||||||
|
if (binAddr.size() > 20 || address == h160())
|
||||||
|
{
|
||||||
|
cerr << "Invalid address for library \"" << libName << "\": " << addrString << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_libraries[libName] = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CommandLineInterface::createFile(string const& _fileName, string const& _data)
|
void CommandLineInterface::createFile(string const& _fileName, string const& _data)
|
||||||
{
|
{
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
@ -319,6 +353,13 @@ Allowed options)",
|
|||||||
"Estimated number of contract runs for optimizer tuning."
|
"Estimated number of contract runs for optimizer tuning."
|
||||||
)
|
)
|
||||||
(g_argAddStandard.c_str(), "Add standard contracts.")
|
(g_argAddStandard.c_str(), "Add standard contracts.")
|
||||||
|
(
|
||||||
|
"libraries",
|
||||||
|
po::value<vector<string>>()->value_name("libs"),
|
||||||
|
"Direct string or file containing library addresses. Syntax: "
|
||||||
|
"<libraryName>: <address> [, or whitespace] ...\n"
|
||||||
|
"Address is interpreted as a hex string optionally prefixed by 0x."
|
||||||
|
)
|
||||||
(
|
(
|
||||||
"output-dir,o",
|
"output-dir,o",
|
||||||
po::value<string>()->value_name("path"),
|
po::value<string>()->value_name("path"),
|
||||||
@ -329,7 +370,12 @@ Allowed options)",
|
|||||||
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
|
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
|
||||||
"Output a single json document containing the specified information."
|
"Output a single json document containing the specified information."
|
||||||
)
|
)
|
||||||
(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.");
|
(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
|
||||||
|
(
|
||||||
|
"link",
|
||||||
|
"Switch to linker mode, ignoring all options apart from --libraries "
|
||||||
|
"and modify binaries in place."
|
||||||
|
);
|
||||||
po::options_description outputComponents("Output Components");
|
po::options_description outputComponents("Output Components");
|
||||||
outputComponents.add_options()
|
outputComponents.add_options()
|
||||||
(g_argAstStr.c_str(), "AST of all source files.")
|
(g_argAstStr.c_str(), "AST of all source files.")
|
||||||
@ -402,7 +448,7 @@ bool CommandLineInterface::processInput()
|
|||||||
while (!cin.eof())
|
while (!cin.eof())
|
||||||
{
|
{
|
||||||
getline(cin, s);
|
getline(cin, s);
|
||||||
m_sourceCodes["<stdin>"].append(s + '\n');
|
m_sourceCodes[g_stdinFileName].append(s + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -424,6 +470,18 @@ bool CommandLineInterface::processInput()
|
|||||||
m_sourceCodes[infile] = dev::contentsString(infile);
|
m_sourceCodes[infile] = dev::contentsString(infile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_args.count("libraries"))
|
||||||
|
for (string const& library: m_args["libraries"].as<vector<string>>())
|
||||||
|
if (!parseLibraryOption(library))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_args.count("link"))
|
||||||
|
{
|
||||||
|
// switch to linker mode
|
||||||
|
m_onlyLink = true;
|
||||||
|
return link();
|
||||||
|
}
|
||||||
|
|
||||||
m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0));
|
m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -433,6 +491,7 @@ bool CommandLineInterface::processInput()
|
|||||||
bool optimize = m_args.count("optimize") > 0;
|
bool optimize = m_args.count("optimize") > 0;
|
||||||
unsigned runs = m_args["optimize-runs"].as<unsigned>();
|
unsigned runs = m_args["optimize-runs"].as<unsigned>();
|
||||||
m_compiler->compile(optimize, runs);
|
m_compiler->compile(optimize, runs);
|
||||||
|
m_compiler->link(m_libraries);
|
||||||
}
|
}
|
||||||
catch (ParserError const& _exception)
|
catch (ParserError const& _exception)
|
||||||
{
|
{
|
||||||
@ -601,6 +660,61 @@ void CommandLineInterface::handleAst(string const& _argStr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CommandLineInterface::actOnInput()
|
void CommandLineInterface::actOnInput()
|
||||||
|
{
|
||||||
|
if (m_onlyLink)
|
||||||
|
writeLinkedFiles();
|
||||||
|
else
|
||||||
|
outputCompilationResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineInterface::link()
|
||||||
|
{
|
||||||
|
for (auto& src: m_sourceCodes)
|
||||||
|
{
|
||||||
|
auto end = src.second.end();
|
||||||
|
for (auto it = src.second.begin(); it != end;)
|
||||||
|
{
|
||||||
|
while (it != end && *it != '_') ++it;
|
||||||
|
auto insertStart = it;
|
||||||
|
while (it != end && *it == '_') ++it;
|
||||||
|
auto nameStart = it;
|
||||||
|
while (it != end && *it != '_') ++it;
|
||||||
|
auto nameEnd = it;
|
||||||
|
while (it != end && *it == '_') ++it;
|
||||||
|
auto insertEnd = it;
|
||||||
|
|
||||||
|
if (insertStart == end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (insertEnd - insertStart != 40)
|
||||||
|
{
|
||||||
|
cerr << "Error in binary object file " << src.first << " at position " << (insertStart - src.second.begin()) << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name(nameStart, nameEnd);
|
||||||
|
if (m_libraries.count(name))
|
||||||
|
{
|
||||||
|
string hexStr(toHex(m_libraries.at(name).asBytes()));
|
||||||
|
copy(hexStr.begin(), hexStr.end(), insertStart);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineInterface::writeLinkedFiles()
|
||||||
|
{
|
||||||
|
for (auto const& src: m_sourceCodes)
|
||||||
|
if (src.first == g_stdinFileName)
|
||||||
|
cout << src.second << endl;
|
||||||
|
else
|
||||||
|
writeFile(src.first, src.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineInterface::outputCompilationResults()
|
||||||
{
|
{
|
||||||
handleCombinedJSON();
|
handleCombinedJSON();
|
||||||
|
|
||||||
|
@ -46,6 +46,11 @@ public:
|
|||||||
void actOnInput();
|
void actOnInput();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool link();
|
||||||
|
void writeLinkedFiles();
|
||||||
|
|
||||||
|
void outputCompilationResults();
|
||||||
|
|
||||||
void handleCombinedJSON();
|
void handleCombinedJSON();
|
||||||
void handleAst(std::string const& _argStr);
|
void handleAst(std::string const& _argStr);
|
||||||
void handleBinary(std::string const& _contract);
|
void handleBinary(std::string const& _contract);
|
||||||
@ -56,16 +61,23 @@ private:
|
|||||||
std::string const& _contract);
|
std::string const& _contract);
|
||||||
void handleGasEstimation(std::string const& _contract);
|
void handleGasEstimation(std::string const& _contract);
|
||||||
|
|
||||||
|
/// Tries to read from the file @a _input or interprets _input literally if that fails.
|
||||||
|
/// It then tries to parse the contents and appends to m_libraries.
|
||||||
|
bool parseLibraryOption(std::string const& _input);
|
||||||
|
|
||||||
/// Create a file in the given directory
|
/// Create a file in the given directory
|
||||||
/// @arg _fileName the name of the file
|
/// @arg _fileName the name of the file
|
||||||
/// @arg _data to be written
|
/// @arg _data to be written
|
||||||
void createFile(std::string const& _fileName, std::string const& _data);
|
void createFile(std::string const& _fileName, std::string const& _data);
|
||||||
|
|
||||||
|
bool m_onlyLink = false;
|
||||||
|
|
||||||
/// Compiler arguments variable map
|
/// Compiler arguments variable map
|
||||||
boost::program_options::variables_map m_args;
|
boost::program_options::variables_map m_args;
|
||||||
/// map of input files to source code strings
|
/// map of input files to source code strings
|
||||||
std::map<std::string, std::string> m_sourceCodes;
|
std::map<std::string, std::string> m_sourceCodes;
|
||||||
|
/// map of library names to addresses
|
||||||
|
std::map<std::string, h160> m_libraries;
|
||||||
/// Solidity compiler stack
|
/// Solidity compiler stack
|
||||||
std::unique_ptr<dev::solidity::CompilerStack> m_compiler;
|
std::unique_ptr<dev::solidity::CompilerStack> m_compiler;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user