diff options
-rw-r--r-- | libsolidity/CompilerStack.cpp | 10 | ||||
-rw-r--r-- | libsolidity/CompilerStack.h | 7 | ||||
-rw-r--r-- | libsolidity/Types.cpp | 7 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 118 | ||||
-rw-r--r-- | solc/CommandLineInterface.h | 12 |
5 files changed, 148 insertions, 6 deletions
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 3da982a4..70bb1b4c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -181,6 +181,16 @@ eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool 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 { Contract const& currentContract = contract(_contractName); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 8db8aff4..99e8af1a 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -96,9 +96,12 @@ public: /// @returns the compiled linker object 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; - /// @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; /// @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 diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 5bc7cd43..beb5becd 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -925,7 +925,10 @@ bool ContractType::operator==(Type const& _other) 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 @@ -971,7 +974,7 @@ MemberList const& ContractType::members() const for (auto const& it: m_contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(false), + it.second->asMemberFunction(m_contract.isLibrary()), &it.second->declaration() )); m_members.reset(new MemberList(members)); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index c34ffee0..3d9c3f08 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -69,6 +69,7 @@ static string const g_argOpcodesStr = "opcodes"; static string const g_argNatspecDevStr = "devdoc"; static string const g_argNatspecUserStr = "userdoc"; static string const g_argAddStandard = "add-std"; +static string const g_stdinFileName = "<stdin>"; /// Possible arguments to for --combined-json 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) { namespace fs = boost::filesystem; @@ -320,6 +354,13 @@ Allowed options)", ) (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", po::value<string>()->value_name("path"), "If given, creates one file per component and contract/file at the specified directory." @@ -329,7 +370,12 @@ Allowed options)", po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")), "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"); outputComponents.add_options() (g_argAstStr.c_str(), "AST of all source files.") @@ -402,7 +448,7 @@ bool CommandLineInterface::processInput() while (!cin.eof()) { getline(cin, s); - m_sourceCodes["<stdin>"].append(s + '\n'); + m_sourceCodes[g_stdinFileName].append(s + '\n'); } } else @@ -424,6 +470,18 @@ bool CommandLineInterface::processInput() 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)); try { @@ -433,6 +491,7 @@ bool CommandLineInterface::processInput() bool optimize = m_args.count("optimize") > 0; unsigned runs = m_args["optimize-runs"].as<unsigned>(); m_compiler->compile(optimize, runs); + m_compiler->link(m_libraries); } catch (ParserError const& _exception) { @@ -602,6 +661,61 @@ void CommandLineInterface::handleAst(string const& _argStr) 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(); // do we need AST output? diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index f79bc0be..2af5a5e9 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -46,6 +46,11 @@ public: void actOnInput(); private: + bool link(); + void writeLinkedFiles(); + + void outputCompilationResults(); + void handleCombinedJSON(); void handleAst(std::string const& _argStr); void handleBinary(std::string const& _contract); @@ -56,16 +61,23 @@ private: 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 /// @arg _fileName the name of the file /// @arg _data to be written void createFile(std::string const& _fileName, std::string const& _data); + bool m_onlyLink = false; + /// Compiler arguments variable map boost::program_options::variables_map m_args; /// map of input files to source code strings std::map<std::string, std::string> m_sourceCodes; + /// map of library names to addresses + std::map<std::string, h160> m_libraries; /// Solidity compiler stack std::unique_ptr<dev::solidity::CompilerStack> m_compiler; }; |