aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/interface
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/interface')
-rw-r--r--libsolidity/interface/CompilerStack.cpp435
-rw-r--r--libsolidity/interface/CompilerStack.h221
-rw-r--r--libsolidity/interface/Exceptions.cpp52
-rw-r--r--libsolidity/interface/Exceptions.h101
-rw-r--r--libsolidity/interface/GasEstimator.cpp191
-rw-r--r--libsolidity/interface/GasEstimator.h85
-rw-r--r--libsolidity/interface/InterfaceHandler.cpp478
-rw-r--r--libsolidity/interface/InterfaceHandler.h132
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp126
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.h54
-rw-r--r--libsolidity/interface/Utils.h39
-rw-r--r--libsolidity/interface/Version.cpp73
-rw-r--r--libsolidity/interface/Version.h42
13 files changed, 2029 insertions, 0 deletions
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
new file mode 100644
index 00000000..775c7eb6
--- /dev/null
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -0,0 +1,435 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Full-stack compiler that converts a source code string to bytecode.
+ */
+
+#include <boost/algorithm/string.hpp>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/GlobalContext.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/InterfaceHandler.h>
+
+#include <libdevcore/SHA3.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+const map<string, string> StandardSources = map<string, string>{
+ {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
+ {"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
+ {"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,bytes3 name,uint256 denom){}function register(bytes3 name,uint256 denom){}function unregister(){}})"},
+ {"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
+ {"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
+ {"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
+ {"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(bytes32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
+ {"NameReg", R"(contract NameReg{function register(bytes32 name){}function addressOf(bytes32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(bytes32 name){}})"},
+ {"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
+ {"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
+ {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
+};
+
+CompilerStack::CompilerStack(bool _addStandardSources):
+ m_parseSuccessful(false)
+{
+ if (_addStandardSources)
+ addSources(StandardSources, true); // add them as libraries
+}
+
+void CompilerStack::reset(bool _keepSources, bool _addStandardSources)
+{
+ m_parseSuccessful = false;
+ if (_keepSources)
+ for (auto sourcePair: m_sources)
+ sourcePair.second.reset();
+ else
+ {
+ m_sources.clear();
+ if (_addStandardSources)
+ addSources(StandardSources, true);
+ }
+ m_globalContext.reset();
+ m_sourceOrder.clear();
+ m_contracts.clear();
+ m_errors.clear();
+}
+
+bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
+{
+ bool existed = m_sources.count(_name) != 0;
+ reset(true);
+ m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
+ m_sources[_name].isLibrary = _isLibrary;
+ return existed;
+}
+
+void CompilerStack::setSource(string const& _sourceCode)
+{
+ reset();
+ addSource("", _sourceCode);
+}
+
+bool CompilerStack::parse()
+{
+ //reset
+ m_errors.clear();
+ m_parseSuccessful = false;
+
+ for (auto& sourcePair: m_sources)
+ {
+ sourcePair.second.scanner->reset();
+ sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
+ if (!sourcePair.second.ast)
+ solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
+ }
+ if (!Error::containsOnlyWarnings(m_errors))
+ // errors while parsing. sould stop before type checking
+ return false;
+
+ resolveImports();
+
+ m_globalContext = make_shared<GlobalContext>();
+ NameAndTypeResolver resolver(m_globalContext->declarations(), m_errors);
+ for (Source const* source: m_sourceOrder)
+ if (!resolver.registerDeclarations(*source->ast))
+ return false;
+
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ m_globalContext->setCurrentContract(*contract);
+ if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false;
+ if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false;
+ if (!resolver.resolveNamesAndTypes(*contract)) return false;
+ m_contracts[contract->name()].contract = contract;
+ }
+
+ InterfaceHandler interfaceHandler;
+ bool typesFine = true;
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ m_globalContext->setCurrentContract(*contract);
+ resolver.updateDeclaration(*m_globalContext->currentThis());
+ TypeChecker typeChecker(m_errors);
+ if (typeChecker.checkTypeRequirements(*contract))
+ {
+ contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
+ contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
+ }
+ else
+ typesFine = false;
+
+ m_contracts[contract->name()].contract = contract;
+ }
+ m_parseSuccessful = typesFine;
+ return m_parseSuccessful;
+}
+
+bool CompilerStack::parse(string const& _sourceCode)
+{
+ setSource(_sourceCode);
+ return parse();
+}
+
+vector<string> CompilerStack::contractNames() const
+{
+ if (!m_parseSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ vector<string> contractNames;
+ for (auto const& contract: m_contracts)
+ contractNames.push_back(contract.first);
+ return contractNames;
+}
+
+
+bool CompilerStack::compile(bool _optimize, unsigned _runs)
+{
+ if (!m_parseSuccessful)
+ if (!parse())
+ return false;
+
+ map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
+ compileContract(_optimize, _runs, *contract, compiledContracts);
+ return true;
+}
+
+bool CompilerStack::compile(string const& _sourceCode, bool _optimize)
+{
+ return parse(_sourceCode) && compile(_optimize);
+}
+
+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);
+ return currentContract.compiler ? &contract(_contractName).compiler->assemblyItems() : nullptr;
+}
+
+eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
+{
+ Contract const& currentContract = contract(_contractName);
+ return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
+}
+
+eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
+{
+ return contract(_contractName).object;
+}
+
+eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
+{
+ return contract(_contractName).runtimeObject;
+}
+
+eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) const
+{
+ return contract(_contractName).cloneObject;
+}
+
+dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
+{
+ auto const& obj = runtimeObject(_contractName);
+ if (obj.bytecode.empty() || !obj.linkReferences.empty())
+ return dev::h256();
+ else
+ return dev::sha3(obj.bytecode);
+}
+
+Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
+{
+ Contract const& currentContract = contract(_contractName);
+ if (currentContract.compiler)
+ return currentContract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat);
+ else
+ {
+ _outStream << "Contract not fully implemented" << endl;
+ return Json::Value();
+ }
+}
+
+string const& CompilerStack::interface(string const& _contractName) const
+{
+ return metadata(_contractName, DocumentationType::ABIInterface);
+}
+
+string const& CompilerStack::solidityInterface(string const& _contractName) const
+{
+ return metadata(_contractName, DocumentationType::ABISolidityInterface);
+}
+
+string const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
+{
+ if (!m_parseSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+
+ std::unique_ptr<string const>* doc;
+ Contract const& currentContract = contract(_contractName);
+
+ // checks wheather we already have the documentation
+ switch (_type)
+ {
+ case DocumentationType::NatspecUser:
+ doc = &currentContract.userDocumentation;
+ break;
+ case DocumentationType::NatspecDev:
+ doc = &currentContract.devDocumentation;
+ break;
+ case DocumentationType::ABIInterface:
+ doc = &currentContract.interface;
+ break;
+ case DocumentationType::ABISolidityInterface:
+ doc = &currentContract.solidityInterface;
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
+ }
+
+ // caches the result
+ if (!*doc)
+ doc->reset(new string(currentContract.interfaceHandler->documentation(*currentContract.contract, _type)));
+
+ return *(*doc);
+}
+
+Scanner const& CompilerStack::scanner(string const& _sourceName) const
+{
+ return *source(_sourceName).scanner;
+}
+
+SourceUnit const& CompilerStack::ast(string const& _sourceName) const
+{
+ return *source(_sourceName).ast;
+}
+
+ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
+{
+ return *contract(_contractName).contract;
+}
+
+size_t CompilerStack::functionEntryPoint(
+ std::string const& _contractName,
+ FunctionDefinition const& _function
+) const
+{
+ shared_ptr<Compiler> const& compiler = contract(_contractName).compiler;
+ if (!compiler)
+ return 0;
+ eth::AssemblyItem tag = compiler->functionEntryLabel(_function);
+ if (tag.type() == eth::UndefinedItem)
+ return 0;
+ eth::AssemblyItems const& items = compiler->runtimeAssemblyItems();
+ for (size_t i = 0; i < items.size(); ++i)
+ if (items.at(i).type() == eth::Tag && items.at(i).data() == tag.data())
+ return i;
+ return 0;
+}
+
+tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const
+{
+ int startLine;
+ int startColumn;
+ int endLine;
+ int endColumn;
+ tie(startLine, startColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.start);
+ tie(endLine, endColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.end);
+
+ return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
+}
+
+void CompilerStack::resolveImports()
+{
+ // topological sorting (depth first search) of the import graph, cutting potential cycles
+ vector<Source const*> sourceOrder;
+ set<Source const*> sourcesSeen;
+
+ function<void(Source const*)> toposort = [&](Source const* _source)
+ {
+ if (sourcesSeen.count(_source))
+ return;
+ sourcesSeen.insert(_source);
+ for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
+ if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
+ {
+ string const& id = import->identifier();
+ if (!m_sources.count(id))
+ BOOST_THROW_EXCEPTION(
+ Error(Error::Type::ParserError)
+ << errinfo_sourceLocation(import->location())
+ << errinfo_comment("Source not found.")
+ );
+
+ toposort(&m_sources[id]);
+ }
+ sourceOrder.push_back(_source);
+ };
+
+ for (auto const& sourcePair: m_sources)
+ if (!sourcePair.second.isLibrary)
+ toposort(&sourcePair.second);
+
+ swap(m_sourceOrder, sourceOrder);
+}
+
+void CompilerStack::compileContract(
+ bool _optimize,
+ unsigned _runs,
+ ContractDefinition const& _contract,
+ map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+)
+{
+ if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented)
+ return;
+ for (auto const* dependency: _contract.annotation().contractDependencies)
+ compileContract(_optimize, _runs, *dependency, _compiledContracts);
+
+ shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
+ compiler->compileContract(_contract, _compiledContracts);
+ Contract& compiledContract = m_contracts.at(_contract.name());
+ compiledContract.compiler = compiler;
+ compiledContract.object = compiler->assembledObject();
+ compiledContract.runtimeObject = compiler->runtimeObject();
+ _compiledContracts[compiledContract.contract] = &compiler->assembly();
+
+ Compiler cloneCompiler(_optimize, _runs);
+ cloneCompiler.compileClone(_contract, _compiledContracts);
+ compiledContract.cloneObject = cloneCompiler.assembledObject();
+}
+
+std::string CompilerStack::defaultContractName() const
+{
+ return contract("").contract->name();
+}
+
+CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
+{
+ if (m_contracts.empty())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
+ string contractName = _contractName;
+ if (_contractName.empty())
+ // try to find some user-supplied contract
+ for (auto const& it: m_sources)
+ if (!StandardSources.count(it.first))
+ for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
+ if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
+ contractName = contract->name();
+ auto it = m_contracts.find(contractName);
+ if (it == m_contracts.end())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));
+ return it->second;
+}
+
+CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const
+{
+ auto it = m_sources.find(_sourceName);
+ if (it == m_sources.end())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
+
+ return it->second;
+}
+
+CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {}
+
+
+}
+}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
new file mode 100644
index 00000000..1f1b74f5
--- /dev/null
+++ b/libsolidity/interface/CompilerStack.h
@@ -0,0 +1,221 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Full-stack compiler that converts a source code string to bytecode.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <string>
+#include <memory>
+#include <vector>
+#include <boost/noncopyable.hpp>
+#include <json/json.h>
+#include <libdevcore/Common.h>
+#include <libdevcore/FixedHash.h>
+#include <libevmasm/SourceLocation.h>
+#include <libevmasm/LinkerObject.h>
+#include <libsolidity/interface/Exceptions.h>
+
+namespace dev
+{
+
+namespace eth
+{
+class Assembly;
+class AssemblyItem;
+using AssemblyItems = std::vector<AssemblyItem>;
+}
+
+namespace solidity
+{
+
+// forward declarations
+class Scanner;
+class ContractDefinition;
+class FunctionDefinition;
+class SourceUnit;
+class Compiler;
+class GlobalContext;
+class InterfaceHandler;
+class Error;
+
+enum class DocumentationType: uint8_t
+{
+ NatspecUser = 1,
+ NatspecDev,
+ ABIInterface,
+ ABISolidityInterface
+};
+
+/**
+ * Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
+ * It holds state and can be used to either step through the compilation stages (and abort e.g.
+ * before compilation to bytecode) or run the whole compilation in one call.
+ */
+class CompilerStack: boost::noncopyable
+{
+public:
+ /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
+ explicit CompilerStack(bool _addStandardSources = true);
+
+ /// Resets the compiler to a state where the sources are not parsed or even removed.
+ void reset(bool _keepSources = false, bool _addStandardSources = true);
+
+ /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
+ /// @returns true if a source object by the name already existed and was replaced.
+ void addSources(StringMap const& _nameContents, bool _isLibrary = false)
+ {
+ for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary);
+ }
+ bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
+ void setSource(std::string const& _sourceCode);
+ /// Parses all source units that were added
+ /// @returns false on error.
+ bool parse();
+ /// Sets the given source code as the only source unit apart from standard sources and parses it.
+ /// @returns false on error.
+ bool parse(std::string const& _sourceCode);
+ /// Returns a list of the contract names in the sources.
+ std::vector<std::string> contractNames() const;
+ std::string defaultContractName() const;
+
+ /// Compiles the source units that were previously added and parsed.
+ /// @returns false on error.
+ bool compile(bool _optimize = false, unsigned _runs = 200);
+ /// Parses and compiles the given source code.
+ /// @returns false on error.
+ bool compile(std::string const& _sourceCode, bool _optimize = false);
+
+ /// 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 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
+ /// substituted by the actual address. Note that this sequence starts end ends in three X
+ /// characters but can contain anything in between.
+ eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const;
+ /// @returns normal contract assembly items
+ eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const;
+ /// @returns runtime contract assembly items
+ eth::AssemblyItems const* runtimeAssemblyItems(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.
+ dev::h256 contractCodeHash(std::string const& _contractName = "") const;
+
+ /// Streams a verbose version of the assembly to @a _outStream.
+ /// @arg _sourceCodes is the map of input files to source code strings
+ /// @arg _inJsonFromat shows whether the out should be in Json format
+ /// Prerequisite: Successful compilation.
+ Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const;
+
+ /// Returns a string representing the contract interface in JSON.
+ /// Prerequisite: Successful call to parse or compile.
+ std::string const& interface(std::string const& _contractName = "") const;
+ /// Returns a string representing the contract interface in Solidity.
+ /// Prerequisite: Successful call to parse or compile.
+ std::string const& solidityInterface(std::string const& _contractName = "") const;
+ /// Returns a string representing the contract's documentation in JSON.
+ /// Prerequisite: Successful call to parse or compile.
+ /// @param type The type of the documentation to get.
+ /// Can be one of 4 types defined at @c DocumentationType
+ std::string const& metadata(std::string const& _contractName, DocumentationType _type) const;
+
+ /// @returns the previously used scanner, useful for counting lines during error reporting.
+ Scanner const& scanner(std::string const& _sourceName = "") const;
+ /// @returns the parsed source unit with the supplied name.
+ SourceUnit const& ast(std::string const& _sourceName = "") const;
+ /// @returns the parsed contract with the supplied name. Throws an exception if the contract
+ /// does not exist.
+ ContractDefinition const& contractDefinition(std::string const& _contractName) const;
+
+ /// @returns the offset of the entry point of the given function into the list of assembly items
+ /// or zero if it is not found or does not exist.
+ size_t functionEntryPoint(
+ std::string const& _contractName,
+ FunctionDefinition const& _function
+ ) const;
+
+ /// Helper function for logs printing. Do only use in error cases, it's quite expensive.
+ /// line and columns are numbered starting from 1 with following order:
+ /// start line, start column, end line, end column
+ std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
+
+ /// @returns the list of errors that occured during parsing and type checking.
+ ErrorList const& errors() const { return m_errors; }
+
+
+private:
+ /**
+ * Information pertaining to one source unit, filled gradually during parsing and compilation.
+ */
+ struct Source
+ {
+ std::shared_ptr<Scanner> scanner;
+ std::shared_ptr<SourceUnit> ast;
+ std::string interface;
+ bool isLibrary = false;
+ void reset() { scanner.reset(); ast.reset(); interface.clear(); }
+ };
+
+ struct Contract
+ {
+ ContractDefinition const* contract = nullptr;
+ std::shared_ptr<Compiler> compiler;
+ eth::LinkerObject object;
+ eth::LinkerObject runtimeObject;
+ eth::LinkerObject cloneObject;
+ std::shared_ptr<InterfaceHandler> interfaceHandler;
+ mutable std::unique_ptr<std::string const> interface;
+ mutable std::unique_ptr<std::string const> solidityInterface;
+ mutable std::unique_ptr<std::string const> userDocumentation;
+ mutable std::unique_ptr<std::string const> devDocumentation;
+
+ Contract();
+ };
+
+ void resolveImports();
+ /// Compile a single contract and put the result in @a _compiledContracts.
+ void compileContract(
+ bool _optimize,
+ unsigned _runs,
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+ );
+
+ Contract const& contract(std::string const& _contractName = "") const;
+ Source const& source(std::string const& _sourceName = "") const;
+
+ bool m_parseSuccessful;
+ std::map<std::string const, Source> m_sources;
+ std::shared_ptr<GlobalContext> m_globalContext;
+ std::vector<Source const*> m_sourceOrder;
+ std::map<std::string const, Contract> m_contracts;
+ ErrorList m_errors;
+};
+
+}
+}
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
new file mode 100644
index 00000000..2e79ab39
--- /dev/null
+++ b/libsolidity/interface/Exceptions.cpp
@@ -0,0 +1,52 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Liana <liana@ethdev.com>
+ * @date 2015
+ * Solidity exception hierarchy.
+ */
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/Utils.h>
+
+using namespace dev;
+using namespace dev::solidity;
+
+Error::Error(Type _type): m_type(_type)
+{
+ switch(m_type)
+ {
+ case Type::DeclarationError:
+ m_typeName = "Declaration Error";
+ break;
+ case Type::DocstringParsingError:
+ m_typeName = "Docstring Parsing Error";
+ break;
+ case Type::ParserError:
+ m_typeName = "Parser Error";
+ break;
+ case Type::TypeError:
+ m_typeName = "Type Error";
+ break;
+ case Type::Warning:
+ m_typeName = "Warning";
+ break;
+ default:
+ solAssert(false, "");
+ break;
+ }
+}
diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h
new file mode 100644
index 00000000..cda6b97e
--- /dev/null
+++ b/libsolidity/interface/Exceptions.h
@@ -0,0 +1,101 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity exception hierarchy.
+ */
+
+#pragma once
+
+#include <string>
+#include <utility>
+#include <libdevcore/Exceptions.h>
+#include <libevmasm/SourceLocation.h>
+
+namespace dev
+{
+namespace solidity
+{
+class Error;
+using ErrorList = std::vector<std::shared_ptr<Error const>>;
+
+struct CompilerError: virtual Exception {};
+struct InternalCompilerError: virtual Exception {};
+struct FatalError: virtual Exception {};
+
+class Error: virtual public Exception
+{
+public:
+ enum class Type
+ {
+ DeclarationError,
+ DocstringParsingError,
+ ParserError,
+ TypeError,
+ Warning
+ };
+
+ explicit Error(Type _type);
+
+ Type type() const { return m_type; }
+ std::string const& typeName() const { return m_typeName; }
+
+ /// helper functions
+ static Error const* containsErrorOfType(ErrorList const& _list, Error::Type _type)
+ {
+ for (auto e: _list)
+ {
+ if (e->type() == _type)
+ return e.get();
+ }
+ return nullptr;
+ }
+ static bool containsOnlyWarnings(ErrorList const& _list)
+ {
+ for (auto e: _list)
+ {
+ if (e->type() != Type::Warning)
+ return false;
+ }
+ return true;
+ }
+private:
+ Type m_type;
+ std::string m_typeName;
+};
+
+
+using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
+
+class SecondarySourceLocation
+{
+public:
+ SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
+ {
+ infos.push_back(std::make_pair(_errMsg, _sourceLocation));
+ return *this;
+ }
+ std::vector<errorSourceLocationInfo> infos;
+};
+
+
+using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, SourceLocation>;
+using errinfo_secondarySourceLocation = boost::error_info<struct tag_secondarySourceLocation, SecondarySourceLocation>;
+
+}
+}
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
new file mode 100644
index 00000000..d460ba76
--- /dev/null
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -0,0 +1,191 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Gas consumption estimator working alongside the AST.
+ */
+
+#include "GasEstimator.h"
+#include <map>
+#include <functional>
+#include <memory>
+#include <libdevcore/SHA3.h>
+#include <libevmasm/ControlFlowGraph.h>
+#include <libevmasm/KnownState.h>
+#include <libevmasm/PathGasMeter.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+using namespace dev::solidity;
+
+GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
+ AssemblyItems const& _items,
+ vector<ASTNode const*> const& _ast
+)
+{
+ solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
+ map<SourceLocation, GasConsumption> particularCosts;
+
+ ControlFlowGraph cfg(_items);
+ for (BasicBlock const& block: cfg.optimisedBlocks())
+ {
+ assertThrow(!!block.startState, OptimizerException, "");
+ GasMeter meter(block.startState->copy());
+ auto const end = _items.begin() + block.end;
+ for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
+ particularCosts[iter->location()] += meter.estimateMax(*iter);
+ }
+
+ set<ASTNode const*> finestNodes = finestNodesAtLocation(_ast);
+ ASTGasConsumptionSelfAccumulated gasCosts;
+ auto onNode = [&](ASTNode const& _node)
+ {
+ if (!finestNodes.count(&_node))
+ return true;
+ gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.location()];
+ return true;
+ };
+ auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child)
+ {
+ gasCosts[&_parent][1] += gasCosts[&_child][1];
+ };
+ ASTReduce folder(onNode, onEdge);
+ for (ASTNode const* ast: _ast)
+ ast->accept(folder);
+
+ return gasCosts;
+}
+
+map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLevel(
+ ASTGasConsumptionSelfAccumulated const& _gasCosts,
+ vector<ASTNode const*> const& _roots
+)
+{
+ solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, "");
+ // first pass: statementDepth[node] is the distance from the deepend statement to node
+ // in direction of the tree root (or undefined if not possible)
+ map<ASTNode const*, int> statementDepth;
+ auto onNodeFirstPass = [&](ASTNode const& _node)
+ {
+ if (dynamic_cast<Statement const*>(&_node))
+ statementDepth[&_node] = 0;
+ return true;
+ };
+ auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child)
+ {
+ if (statementDepth.count(&_child))
+ statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1);
+ };
+ ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass);
+ for (ASTNode const* node: _roots)
+ node->accept(firstPass);
+
+ // we use the location of a node if
+ // - its statement depth is 0 or
+ // - its statement depth is undefined but the parent's statement depth is at least 1
+ map<ASTNode const*, GasConsumption> gasCosts;
+ auto onNodeSecondPass = [&](ASTNode const& _node)
+ {
+ return statementDepth.count(&_node);
+ };
+ auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child)
+ {
+ bool useNode = false;
+ if (statementDepth.count(&_child))
+ useNode = statementDepth[&_child] == 0;
+ else
+ useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0;
+ if (useNode)
+ gasCosts[&_child] = _gasCosts.at(&_child)[1];
+ };
+ ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass);
+ for (ASTNode const* node: _roots)
+ node->accept(secondPass);
+ // gasCosts should only contain non-overlapping locations
+ return gasCosts;
+}
+
+GasEstimator::GasConsumption GasEstimator::functionalEstimation(
+ AssemblyItems const& _items,
+ string const& _signature
+)
+{
+ auto state = make_shared<KnownState>();
+
+ if (!_signature.empty())
+ {
+ ExpressionClasses& classes = state->expressionClasses();
+ using Id = ExpressionClasses::Id;
+ using Ids = vector<Id>;
+ Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature)))));
+ Id calldata = classes.find(eth::Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
+ classes.forceEqual(hashValue, eth::Instruction::DIV, Ids{
+ calldata,
+ classes.find(u256(1) << (8 * 28))
+ });
+ }
+
+ PathGasMeter meter(_items);
+ return meter.estimateMax(0, state);
+}
+
+GasEstimator::GasConsumption GasEstimator::functionalEstimation(
+ AssemblyItems const& _items,
+ size_t const& _offset,
+ FunctionDefinition const& _function
+)
+{
+ auto state = make_shared<KnownState>();
+
+ unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters());
+ if (parametersSize > 16)
+ return GasConsumption::infinite();
+
+ // Store an invalid return value on the stack, so that the path estimator breaks upon reaching
+ // the return jump.
+ AssemblyItem invalidTag(PushTag, u256(-0x10));
+ state->feedItem(invalidTag, true);
+ if (parametersSize > 0)
+ state->feedItem(eth::swapInstruction(parametersSize));
+
+ return PathGasMeter(_items).estimateMax(_offset, state);
+}
+
+set<ASTNode const*> GasEstimator::finestNodesAtLocation(
+ vector<ASTNode const*> const& _roots
+)
+{
+ map<SourceLocation, ASTNode const*> locations;
+ set<ASTNode const*> nodes;
+ SimpleASTVisitor visitor(function<bool(ASTNode const&)>(), [&](ASTNode const& _n)
+ {
+ if (!locations.count(_n.location()))
+ {
+ locations[_n.location()] = &_n;
+ nodes.insert(&_n);
+ }
+ });
+
+ for (ASTNode const* root: _roots)
+ root->accept(visitor);
+ return nodes;
+}
diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h
new file mode 100644
index 00000000..518e58e4
--- /dev/null
+++ b/libsolidity/interface/GasEstimator.h
@@ -0,0 +1,85 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Gas consumption estimator working alongside the AST.
+ */
+
+#pragma once
+
+#include <vector>
+#include <map>
+#include <array>
+#include <libevmasm/GasMeter.h>
+#include <libevmasm/Assembly.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class ASTNode;
+class FunctionDefinition;
+
+struct GasEstimator
+{
+public:
+ using GasConsumption = eth::GasMeter::GasConsumption;
+ using ASTGasConsumption = std::map<ASTNode const*, GasConsumption>;
+ using ASTGasConsumptionSelfAccumulated =
+ std::map<ASTNode const*, std::array<GasConsumption, 2>>;
+
+ /// Estimates the gas consumption for every assembly item in the given assembly and stores
+ /// it by source location.
+ /// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
+ static ASTGasConsumptionSelfAccumulated structuralEstimation(
+ eth::AssemblyItems const& _items,
+ std::vector<ASTNode const*> const& _ast
+ );
+ /// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
+ /// the following source locations are part of the mapping:
+ /// 1. source locations of statements that do not contain other statements
+ /// 2. maximal source locations that do not overlap locations coming from the first rule
+ static ASTGasConsumption breakToStatementLevel(
+ ASTGasConsumptionSelfAccumulated const& _gasCosts,
+ std::vector<ASTNode const*> const& _roots
+ );
+
+ /// @returns the estimated gas consumption by the (public or external) function with the
+ /// given signature. If no signature is given, estimates the maximum gas usage.
+ static GasConsumption functionalEstimation(
+ eth::AssemblyItems const& _items,
+ std::string const& _signature = ""
+ );
+
+ /// @returns the estimated gas consumption by the given function which starts at the given
+ /// offset into the list of assembly items.
+ /// @note this does not work correctly for recursive functions.
+ static GasConsumption functionalEstimation(
+ eth::AssemblyItems const& _items,
+ size_t const& _offset,
+ FunctionDefinition const& _function
+ );
+
+private:
+ /// @returns the set of AST nodes which are the finest nodes at their location.
+ static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
+};
+
+}
+}
diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp
new file mode 100644
index 00000000..136136e6
--- /dev/null
+++ b/libsolidity/interface/InterfaceHandler.cpp
@@ -0,0 +1,478 @@
+
+#include <libsolidity/interface/InterfaceHandler.h>
+#include <boost/range/irange.hpp>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/CompilerStack.h>
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+/* -- public -- */
+
+InterfaceHandler::InterfaceHandler()
+{
+ m_lastTag = DocTagType::None;
+}
+
+string InterfaceHandler::documentation(
+ ContractDefinition const& _contractDef,
+ DocumentationType _type
+)
+{
+ switch(_type)
+ {
+ case DocumentationType::NatspecUser:
+ return userDocumentation(_contractDef);
+ case DocumentationType::NatspecDev:
+ return devDocumentation(_contractDef);
+ case DocumentationType::ABIInterface:
+ return abiInterface(_contractDef);
+ case DocumentationType::ABISolidityInterface:
+ return ABISolidityInterface(_contractDef);
+ }
+
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
+ return "";
+}
+
+string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
+{
+ Json::Value abi(Json::arrayValue);
+
+ auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
+ {
+ Json::Value params(Json::arrayValue);
+ solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
+ for (unsigned i = 0; i < _paramNames.size(); ++i)
+ {
+ Json::Value param;
+ param["name"] = _paramNames[i];
+ param["type"] = _paramTypes[i];
+ params.append(param);
+ }
+ return params;
+ };
+
+ for (auto it: _contractDef.interfaceFunctions())
+ {
+ auto externalFunctionType = it.second->interfaceFunctionType();
+ Json::Value method;
+ method["type"] = "function";
+ method["name"] = it.second->declaration().name();
+ method["constant"] = it.second->isConstant();
+ method["inputs"] = populateParameters(
+ externalFunctionType->parameterNames(),
+ externalFunctionType->parameterTypeNames(_contractDef.isLibrary())
+ );
+ method["outputs"] = populateParameters(
+ externalFunctionType->returnParameterNames(),
+ externalFunctionType->returnParameterTypeNames(_contractDef.isLibrary())
+ );
+ abi.append(method);
+ }
+ if (_contractDef.constructor())
+ {
+ Json::Value method;
+ method["type"] = "constructor";
+ auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
+ solAssert(!!externalFunction, "");
+ method["inputs"] = populateParameters(
+ externalFunction->parameterNames(),
+ externalFunction->parameterTypeNames(_contractDef.isLibrary())
+ );
+ abi.append(method);
+ }
+
+ for (auto const& it: _contractDef.interfaceEvents())
+ {
+ Json::Value event;
+ event["type"] = "event";
+ event["name"] = it->name();
+ event["anonymous"] = it->isAnonymous();
+ Json::Value params(Json::arrayValue);
+ for (auto const& p: it->parameters())
+ {
+ Json::Value input;
+ input["name"] = p->name();
+ input["type"] = p->annotation().type->canonicalName(false);
+ input["indexed"] = p->isIndexed();
+ params.append(input);
+ }
+ event["inputs"] = params;
+ abi.append(event);
+ }
+ return Json::FastWriter().write(abi);
+}
+
+string InterfaceHandler::ABISolidityInterface(ContractDefinition const& _contractDef)
+{
+ string ret = (_contractDef.isLibrary() ? "library " : "contract ") + _contractDef.name() + "{";
+
+ auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
+ {
+ string ret = "(";
+ for (size_t i = 0; i < _paramNames.size(); ++i)
+ ret += _paramTypes[i] + " " + _paramNames[i] + ",";
+ if (ret.size() != 1)
+ ret.pop_back();
+ return ret + ")";
+ };
+ // If this is a library, include all its enum and struct types. Should be more intelligent
+ // in the future and check what is actually used (it might even use types from other libraries
+ // or contracts or in the global scope).
+ if (_contractDef.isLibrary())
+ {
+ for (auto const& stru: _contractDef.definedStructs())
+ {
+ ret += "struct " + stru->name() + "{";
+ for (ASTPointer<VariableDeclaration> const& _member: stru->members())
+ ret += _member->type(nullptr)->canonicalName(false) + " " + _member->name() + ";";
+ ret += "}";
+ }
+ for (auto const& enu: _contractDef.definedEnums())
+ {
+ ret += "enum " + enu->name() + "{";
+ for (ASTPointer<EnumValue> const& val: enu->members())
+ ret += val->name() + ",";
+ if (ret.back() == ',')
+ ret.pop_back();
+ ret += "}";
+ }
+ }
+ if (_contractDef.constructor())
+ {
+ auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
+ solAssert(!!externalFunction, "");
+ ret +=
+ "function " +
+ _contractDef.name() +
+ populateParameters(
+ externalFunction->parameterNames(),
+ externalFunction->parameterTypeNames(_contractDef.isLibrary())
+ ) +
+ ";";
+ }
+ for (auto const& it: _contractDef.interfaceFunctions())
+ {
+ ret += "function " + it.second->declaration().name() +
+ populateParameters(
+ it.second->parameterNames(),
+ it.second->parameterTypeNames(_contractDef.isLibrary())
+ ) + (it.second->isConstant() ? "constant " : "");
+ if (it.second->returnParameterTypes().size())
+ ret += "returns" + populateParameters(
+ it.second->returnParameterNames(),
+ it.second->returnParameterTypeNames(_contractDef.isLibrary())
+ );
+ else if (ret.back() == ' ')
+ ret.pop_back();
+ ret += ";";
+ }
+
+ return ret + "}";
+}
+
+string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
+{
+ Json::Value doc;
+ Json::Value methods(Json::objectValue);
+
+ for (auto const& it: _contractDef.interfaceFunctions())
+ {
+ Json::Value user;
+ auto strPtr = it.second->documentation();
+ if (strPtr)
+ {
+ resetUser();
+ parseDocString(*strPtr, CommentOwner::Function);
+ if (!m_notice.empty())
+ {// since @notice is the only user tag if missing function should not appear
+ user["notice"] = Json::Value(m_notice);
+ methods[it.second->externalSignature()] = user;
+ }
+ }
+ }
+ doc["methods"] = methods;
+
+ return Json::StyledWriter().write(doc);
+}
+
+string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
+{
+ // LTODO: Somewhere in this function warnings for mismatch of param names
+ // should be thrown
+ Json::Value doc;
+ Json::Value methods(Json::objectValue);
+
+ auto contractDoc = _contractDef.documentation();
+ if (contractDoc)
+ {
+ m_contractAuthor.clear();
+ m_title.clear();
+ parseDocString(*contractDoc, CommentOwner::Contract);
+
+ if (!m_contractAuthor.empty())
+ doc["author"] = m_contractAuthor;
+
+ if (!m_title.empty())
+ doc["title"] = m_title;
+ }
+
+ for (auto const& it: _contractDef.interfaceFunctions())
+ {
+ Json::Value method;
+ auto strPtr = it.second->documentation();
+ if (strPtr)
+ {
+ resetDev();
+ parseDocString(*strPtr, CommentOwner::Function);
+
+ if (!m_dev.empty())
+ method["details"] = Json::Value(m_dev);
+
+ if (!m_author.empty())
+ method["author"] = m_author;
+
+ Json::Value params(Json::objectValue);
+ vector<string> paramNames = it.second->parameterNames();
+ for (auto const& pair: m_params)
+ {
+ if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end())
+ // LTODO: mismatching parameter name, throw some form of warning and not just an exception
+ BOOST_THROW_EXCEPTION(
+ Error(Error::Type::DocstringParsingError) <<
+ errinfo_comment("documented parameter \"" + pair.first + "\" not found in the parameter list of the function.")
+ );
+ params[pair.first] = pair.second;
+ }
+
+ if (!m_params.empty())
+ method["params"] = params;
+
+ if (!m_return.empty())
+ method["return"] = m_return;
+
+ if (!method.empty()) // add the function, only if we have any documentation to add
+ methods[it.second->externalSignature()] = method;
+ }
+ }
+ doc["methods"] = methods;
+
+ return Json::StyledWriter().write(doc);
+}
+
+/* -- private -- */
+void InterfaceHandler::resetUser()
+{
+ m_notice.clear();
+}
+
+void InterfaceHandler::resetDev()
+{
+ m_dev.clear();
+ m_author.clear();
+ m_return.clear();
+ m_params.clear();
+}
+
+static inline string::const_iterator skipLineOrEOS(
+ string::const_iterator _nlPos,
+ string::const_iterator _end
+)
+{
+ return (_nlPos == _end) ? _end : ++_nlPos;
+}
+
+string::const_iterator InterfaceHandler::parseDocTagLine(
+ string::const_iterator _pos,
+ string::const_iterator _end,
+ string& _tagString,
+ DocTagType _tagType,
+ bool _appending
+)
+{
+ auto nlPos = find(_pos, _end, '\n');
+ if (_appending && _pos < _end && *_pos != ' ')
+ _tagString += " ";
+ copy(_pos, nlPos, back_inserter(_tagString));
+ m_lastTag = _tagType;
+ return skipLineOrEOS(nlPos, _end);
+}
+
+string::const_iterator InterfaceHandler::parseDocTagParam(
+ string::const_iterator _pos,
+ string::const_iterator _end
+)
+{
+ // find param name
+ auto currPos = find(_pos, _end, ' ');
+ if (currPos == _end)
+ BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("End of param name not found" + string(_pos, _end)));
+
+
+ auto paramName = string(_pos, currPos);
+
+ currPos += 1;
+ auto nlPos = find(currPos, _end, '\n');
+ auto paramDesc = string(currPos, nlPos);
+ m_params.push_back(make_pair(paramName, paramDesc));
+
+ m_lastTag = DocTagType::Param;
+ return skipLineOrEOS(nlPos, _end);
+}
+
+string::const_iterator InterfaceHandler::appendDocTagParam(
+ string::const_iterator _pos,
+ string::const_iterator _end
+)
+{
+ // Should never be called with an empty vector
+ solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter");
+
+ auto pair = m_params.back();
+ if (_pos < _end && *_pos != ' ')
+ pair.second += " ";
+ auto nlPos = find(_pos, _end, '\n');
+ copy(_pos, nlPos, back_inserter(pair.second));
+
+ m_params.at(m_params.size() - 1) = pair;
+
+ return skipLineOrEOS(nlPos, _end);
+}
+
+string::const_iterator InterfaceHandler::parseDocTag(
+ string::const_iterator _pos,
+ string::const_iterator _end,
+ string const& _tag,
+ CommentOwner _owner
+)
+{
+ // LTODO: need to check for @(start of a tag) between here and the end of line
+ // for all cases. Also somehow automate list of acceptable tags for each
+ // language construct since current way does not scale well.
+ if (m_lastTag == DocTagType::None || _tag != "")
+ {
+ if (_tag == "dev")
+ return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false);
+ else if (_tag == "notice")
+ return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false);
+ else if (_tag == "return")
+ return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false);
+ else if (_tag == "author")
+ {
+ if (_owner == CommentOwner::Contract)
+ return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false);
+ else if (_owner == CommentOwner::Function)
+ return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false);
+ else
+ // LTODO: for now this else makes no sense but later comments will go to more language constructs
+ BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("@author tag is legal only for contracts"));
+ }
+ else if (_tag == "title")
+ {
+ if (_owner == CommentOwner::Contract)
+ return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false);
+ else
+ // LTODO: Unknown tag, throw some form of warning and not just an exception
+ BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("@title tag is legal only for contracts"));
+ }
+ else if (_tag == "param")
+ return parseDocTagParam(_pos, _end);
+ else
+ // LTODO: Unknown tag, throw some form of warning and not just an exception
+ BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("Unknown tag " + _tag + " encountered"));
+ }
+ else
+ return appendDocTag(_pos, _end, _owner);
+}
+
+string::const_iterator InterfaceHandler::appendDocTag(
+ string::const_iterator _pos,
+ string::const_iterator _end,
+ CommentOwner _owner
+)
+{
+ switch (m_lastTag)
+ {
+ case DocTagType::Dev:
+ return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true);
+ case DocTagType::Notice:
+ return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true);
+ case DocTagType::Return:
+ return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true);
+ case DocTagType::Author:
+ if (_owner == CommentOwner::Contract)
+ return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true);
+ else if (_owner == CommentOwner::Function)
+ return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true);
+ else
+ // LTODO: Unknown tag, throw some form of warning and not just an exception
+ BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("@author tag in illegal comment"));
+ case DocTagType::Title:
+ if (_owner == CommentOwner::Contract)
+ return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true);
+ else
+ // LTODO: Unknown tag, throw some form of warning and not just an exception
+ BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("@title tag in illegal comment"));
+ case DocTagType::Param:
+ return appendDocTagParam(_pos, _end);
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
+ break;
+ }
+}
+
+static inline string::const_iterator firstSpaceOrNl(
+ string::const_iterator _pos,
+ string::const_iterator _end
+)
+{
+ auto spacePos = find(_pos, _end, ' ');
+ auto nlPos = find(_pos, _end, '\n');
+ return (spacePos < nlPos) ? spacePos : nlPos;
+}
+
+void InterfaceHandler::parseDocString(string const& _string, CommentOwner _owner)
+{
+ auto currPos = _string.begin();
+ auto end = _string.end();
+
+ while (currPos != end)
+ {
+ auto tagPos = find(currPos, end, '@');
+ auto nlPos = find(currPos, end, '\n');
+
+ if (tagPos != end && tagPos < nlPos)
+ {
+ // we found a tag
+ auto tagNameEndPos = firstSpaceOrNl(tagPos, end);
+ if (tagNameEndPos == end)
+ BOOST_THROW_EXCEPTION(
+ Error(Error::Type::DocstringParsingError) <<
+ errinfo_comment("End of tag " + string(tagPos, tagNameEndPos) + "not found"));
+
+ currPos = parseDocTag(tagNameEndPos + 1, end, string(tagPos + 1, tagNameEndPos), _owner);
+ }
+ else if (m_lastTag != DocTagType::None) // continuation of the previous tag
+ currPos = appendDocTag(currPos, end, _owner);
+ else if (currPos != end)
+ {
+ // if it begins without a tag then consider it as @notice
+ if (currPos == _string.begin())
+ {
+ currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function);
+ continue;
+ }
+ else if (nlPos == end) //end of text
+ return;
+ // else skip the line if a newline was found and we get here
+ currPos = nlPos + 1;
+ }
+ }
+}
+
+} //solidity NS
+} // dev NS
diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h
new file mode 100644
index 00000000..62164517
--- /dev/null
+++ b/libsolidity/interface/InterfaceHandler.h
@@ -0,0 +1,132 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2014
+ * Takes the parsed AST and produces the Natspec
+ * documentation and the ABI interface
+ * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
+ *
+ * Can generally deal with JSON files
+ */
+
+#pragma once
+
+#include <string>
+#include <memory>
+#include <json/json.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+// Forward declarations
+class ContractDefinition;
+enum class DocumentationType: uint8_t;
+
+enum class DocTagType: uint8_t
+{
+ None = 0,
+ Dev,
+ Notice,
+ Param,
+ Return,
+ Author,
+ Title
+};
+
+enum class CommentOwner
+{
+ Contract,
+ Function
+};
+
+class InterfaceHandler
+{
+public:
+ InterfaceHandler();
+
+ /// Get the given type of documentation
+ /// @param _contractDef The contract definition
+ /// @param _type The type of the documentation. Can be one of the
+ /// types provided by @c DocumentationType
+ /// @return A string with the json representation of provided type
+ std::string documentation(
+ ContractDefinition const& _contractDef,
+ DocumentationType _type
+ );
+ /// Get the ABI Interface of the contract
+ /// @param _contractDef The contract definition
+ /// @return A string with the json representation of the contract's ABI Interface
+ std::string abiInterface(ContractDefinition const& _contractDef);
+ std::string ABISolidityInterface(ContractDefinition const& _contractDef);
+ /// Get the User documentation of the contract
+ /// @param _contractDef The contract definition
+ /// @return A string with the json representation of the contract's user documentation
+ std::string userDocumentation(ContractDefinition const& _contractDef);
+ /// Genereates the Developer's documentation of the contract
+ /// @param _contractDef The contract definition
+ /// @return A string with the json representation
+ /// of the contract's developer documentation
+ std::string devDocumentation(ContractDefinition const& _contractDef);
+
+private:
+ void resetUser();
+ void resetDev();
+
+ std::string::const_iterator parseDocTagLine(
+ std::string::const_iterator _pos,
+ std::string::const_iterator _end,
+ std::string& _tagString,
+ DocTagType _tagType,
+ bool _appending
+ );
+ std::string::const_iterator parseDocTagParam(
+ std::string::const_iterator _pos,
+ std::string::const_iterator _end
+ );
+ std::string::const_iterator appendDocTagParam(
+ std::string::const_iterator _pos,
+ std::string::const_iterator _end
+ );
+ void parseDocString(std::string const& _string, CommentOwner _owner);
+ std::string::const_iterator appendDocTag(
+ std::string::const_iterator _pos,
+ std::string::const_iterator _end,
+ CommentOwner _owner
+ );
+ std::string::const_iterator parseDocTag(
+ std::string::const_iterator _pos,
+ std::string::const_iterator _end,
+ std::string const& _tag,
+ CommentOwner _owner
+ );
+
+ // internal state
+ DocTagType m_lastTag;
+ std::string m_notice;
+ std::string m_dev;
+ std::string m_return;
+ std::string m_contractAuthor;
+ std::string m_author;
+ std::string m_title;
+ std::vector<std::pair<std::string, std::string>> m_params;
+};
+
+} //solidity NS
+} // dev NS
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
new file mode 100644
index 00000000..169e5c18
--- /dev/null
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -0,0 +1,126 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Formatting functions for errors referencing positions and locations in the source.
+ */
+
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/Exceptions.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+void SourceReferenceFormatter::printSourceLocation(
+ ostream& _stream,
+ SourceLocation const& _location,
+ Scanner const& _scanner
+)
+{
+ int startLine;
+ int startColumn;
+ tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
+ int endLine;
+ int endColumn;
+ tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
+ if (startLine == endLine)
+ {
+ string line = _scanner.lineAtPosition(_location.start);
+ _stream << line << endl;
+ for_each(
+ line.cbegin(),
+ line.cbegin() + startColumn,
+ [&_stream](char const& ch) { _stream << (ch == '\t' ? '\t' : ' '); }
+ );
+ _stream << "^";
+ if (endColumn > startColumn + 2)
+ _stream << string(endColumn - startColumn - 2, '-');
+ if (endColumn > startColumn + 1)
+ _stream << "^";
+ _stream << endl;
+ }
+ else
+ _stream <<
+ _scanner.lineAtPosition(_location.start) <<
+ endl <<
+ string(startColumn, ' ') <<
+ "^\n" <<
+ "Spanning multiple lines.\n";
+}
+
+void SourceReferenceFormatter::printSourceName(
+ ostream& _stream,
+ SourceLocation const& _location,
+ Scanner const& _scanner
+)
+{
+ int startLine;
+ int startColumn;
+ tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
+ _stream << *_location.sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
+}
+
+void SourceReferenceFormatter::printExceptionInformation(
+ ostream& _stream,
+ Exception const& _exception,
+ string const& _name,
+ CompilerStack const& _compiler
+)
+{
+ SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
+ auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
+ Scanner const* scannerPtr = nullptr;
+
+ if (location)
+ {
+ scannerPtr = &_compiler.scanner(*location->sourceName);
+ printSourceName(_stream, *location, *scannerPtr);
+ }
+
+ _stream << _name;
+ if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
+ _stream << ": " << *description << endl;
+
+ if (location)
+ {
+ scannerPtr = &_compiler.scanner(*location->sourceName);
+ printSourceLocation(_stream, *location, *scannerPtr);
+ }
+
+ if (secondarylocation && !secondarylocation->infos.empty())
+ {
+ for (auto info: secondarylocation->infos)
+ {
+ scannerPtr = &_compiler.scanner(*info.second.sourceName);
+ _stream << info.first << " ";
+ printSourceName(_stream, info.second, *scannerPtr);
+ _stream << endl;
+ printSourceLocation(_stream, info.second, *scannerPtr);
+ }
+ _stream << endl;
+ }
+}
+
+}
+}
diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h
new file mode 100644
index 00000000..dd258c27
--- /dev/null
+++ b/libsolidity/interface/SourceReferenceFormatter.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Formatting functions for errors referencing positions and locations in the source.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <libevmasm/SourceLocation.h>
+
+namespace dev
+{
+
+struct Exception; // forward
+
+namespace solidity
+{
+
+class Scanner; // forward
+class CompilerStack; // forward
+
+struct SourceReferenceFormatter
+{
+public:
+ static void printSourceLocation(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner);
+ static void printExceptionInformation(
+ std::ostream& _stream,
+ Exception const& _exception,
+ std::string const& _name,
+ CompilerStack const& _compiler
+ );
+private:
+ static void printSourceName(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner);
+};
+
+}
+}
diff --git a/libsolidity/interface/Utils.h b/libsolidity/interface/Utils.h
new file mode 100644
index 00000000..738669ac
--- /dev/null
+++ b/libsolidity/interface/Utils.h
@@ -0,0 +1,39 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity Utilities.
+ */
+
+#pragma once
+
+#include <libdevcore/Assertions.h>
+#include <libsolidity/interface/Exceptions.h>
+
+namespace dev
+{
+namespace solidity
+{
+struct InternalCompilerError;
+}
+}
+
+/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
+#define solAssert(CONDITION, DESCRIPTION) \
+ assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
+
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
new file mode 100644
index 00000000..d2a53440
--- /dev/null
+++ b/libsolidity/interface/Version.cpp
@@ -0,0 +1,73 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Versioning.
+ */
+
+#include <libsolidity/interface/Version.h>
+#include <string>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/Common.h>
+#include <libevmasm/Version.h>
+#include <libsolidity/interface/Utils.h>
+#include <solidity/BuildInfo.h>
+
+using namespace dev;
+using namespace dev::solidity;
+using namespace std;
+
+char const* dev::solidity::VersionNumber = ETH_PROJECT_VERSION;
+
+string const dev::solidity::VersionString =
+ string(dev::solidity::VersionNumber) +
+ "-" +
+ string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) +
+ (ETH_CLEAN_REPO ? "" : "*") +
+ "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM)
+ " linked to libethereum-" + eth::VersionStringLibEvmAsm;
+
+
+bytes dev::solidity::binaryVersion()
+{
+ bytes ret{0};
+ size_t i = 0;
+ auto parseDecimal = [&]()
+ {
+ size_t ret = 0;
+ solAssert('0' <= VersionString[i] && VersionString[i] <= '9', "");
+ for (; i < VersionString.size() && '0' <= VersionString[i] && VersionString[i] <= '9'; ++i)
+ ret = ret * 10 + (VersionString[i] - '0');
+ return ret;
+ };
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '.', "");
+ ++i;
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '.', "");
+ ++i;
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '-', "");
+ ++i;
+ solAssert(i + 7 < VersionString.size(), "");
+ ret += fromHex(VersionString.substr(i, 8));
+ solAssert(ret.size() == 1 + 3 + 4, "");
+
+ return ret;
+}
+
diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h
new file mode 100644
index 00000000..fea73997
--- /dev/null
+++ b/libsolidity/interface/Version.h
@@ -0,0 +1,42 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Versioning.
+ */
+
+#pragma once
+
+#include <string>
+#include <libdevcore/Common.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+extern char const* VersionNumber;
+extern std::string const VersionString;
+
+/// @returns a binary form of the version string, where A.B.C-HASH is encoded such that
+/// the first byte is zero, the following three bytes encode A B and C (interpreted as decimals)
+/// and HASH is interpreted as 8 hex digits and encoded into the last four bytes.
+bytes binaryVersion();
+
+}
+}