aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/interface
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/interface')
-rw-r--r--libsolidity/interface/CompilerStack.cpp190
-rw-r--r--libsolidity/interface/CompilerStack.h39
-rw-r--r--libsolidity/interface/Exceptions.cpp12
-rw-r--r--libsolidity/interface/ReadFile.h45
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.h11
-rw-r--r--libsolidity/interface/StandardCompiler.cpp482
-rw-r--r--libsolidity/interface/StandardCompiler.h63
7 files changed, 800 insertions, 42 deletions
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 6b0024ad..9c9c9614 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -38,6 +38,7 @@
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/InterfaceHandler.h>
+#include <libsolidity/interface/GasEstimator.h>
#include <libsolidity/formal/Why3Translator.h>
#include <libevmasm/Exceptions.h>
@@ -55,8 +56,8 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-CompilerStack::CompilerStack(ReadFileCallback const& _readFile):
- m_readFile(_readFile), m_parseSuccessful(false) {}
+CompilerStack::CompilerStack(ReadFile::Callback const& _readFile):
+ m_readFile(_readFile) {}
void CompilerStack::setRemappings(vector<string> const& _remappings)
{
@@ -78,10 +79,12 @@ void CompilerStack::setRemappings(vector<string> const& _remappings)
void CompilerStack::reset(bool _keepSources)
{
- m_parseSuccessful = false;
if (_keepSources)
+ {
+ m_stackState = SourcesSet;
for (auto sourcePair: m_sources)
sourcePair.second.reset();
+ }
else
{
m_sources.clear();
@@ -93,6 +96,7 @@ void CompilerStack::reset(bool _keepSources)
m_sourceOrder.clear();
m_contracts.clear();
m_errors.clear();
+ m_stackState = Empty;
}
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
@@ -101,6 +105,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
m_sources[_name].isLibrary = _isLibrary;
+ m_stackState = SourcesSet;
return existed;
}
@@ -113,9 +118,10 @@ void CompilerStack::setSource(string const& _sourceCode)
bool CompilerStack::parse()
{
//reset
+ if(m_stackState != SourcesSet)
+ return false;
m_errors.clear();
ASTNode::resetID();
- m_parseSuccessful = false;
if (SemVerVersion{string(VersionString)}.isPrerelease())
{
@@ -127,14 +133,12 @@ bool CompilerStack::parse()
vector<string> sourcesToParse;
for (auto const& s: m_sources)
sourcesToParse.push_back(s.first);
- map<string, SourceUnit const*> sourceUnitsByName;
for (size_t i = 0; i < sourcesToParse.size(); ++i)
{
string const& path = sourcesToParse[i];
Source& source = m_sources[path];
source.scanner->reset();
source.ast = Parser(m_errors).parse(source.scanner);
- sourceUnitsByName[path] = source.ast.get();
if (!source.ast)
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
else
@@ -149,10 +153,19 @@ bool CompilerStack::parse()
}
}
}
- if (!Error::containsOnlyWarnings(m_errors))
- // errors while parsing. should stop before type checking
+ if (Error::containsOnlyWarnings(m_errors))
+ {
+ m_stackState = ParsingSuccessful;
+ return true;
+ }
+ else
return false;
+}
+bool CompilerStack::analyze()
+{
+ if (m_stackState != ParsingSuccessful)
+ return false;
resolveImports();
bool noErrors = true;
@@ -172,6 +185,9 @@ bool CompilerStack::parse()
if (!resolver.registerDeclarations(*source->ast))
return false;
+ map<string, SourceUnit const*> sourceUnitsByName;
+ for (auto& source: m_sources)
+ sourceUnitsByName[source.first] = source.second.ast.get();
for (Source const* source: m_sourceOrder)
if (!resolver.performImports(*source->ast, sourceUnitsByName))
return false;
@@ -234,8 +250,13 @@ bool CompilerStack::parse()
noErrors = false;
}
- m_parseSuccessful = noErrors;
- return m_parseSuccessful;
+ if (noErrors)
+ {
+ m_stackState = AnalysisSuccessful;
+ return true;
+ }
+ else
+ return false;
}
bool CompilerStack::parse(string const& _sourceCode)
@@ -244,9 +265,20 @@ bool CompilerStack::parse(string const& _sourceCode)
return parse();
}
+bool CompilerStack::parseAndAnalyze()
+{
+ return parse() && analyze();
+}
+
+bool CompilerStack::parseAndAnalyze(std::string const& _sourceCode)
+{
+ setSource(_sourceCode);
+ return parseAndAnalyze();
+}
+
vector<string> CompilerStack::contractNames() const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
vector<string> contractNames;
for (auto const& contract: m_contracts)
@@ -257,8 +289,8 @@ vector<string> CompilerStack::contractNames() const
bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> const& _libraries)
{
- if (!m_parseSuccessful)
- if (!parse())
+ if (m_stackState < AnalysisSuccessful)
+ if (!parseAndAnalyze())
return false;
m_optimize = _optimize;
@@ -271,12 +303,13 @@ bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> co
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
compileContract(*contract, compiledContracts);
this->link();
+ m_stackState = CompilationSuccessful;
return true;
}
bool CompilerStack::compile(string const& _sourceCode, bool _optimize, unsigned _runs)
{
- return parse(_sourceCode) && compile(_optimize, _runs);
+ return parseAndAnalyze(_sourceCode) && compile(_optimize, _runs);
}
void CompilerStack::link()
@@ -405,8 +438,9 @@ vector<string> CompilerStack::sourceNames() const
map<string, unsigned> CompilerStack::sourceIndices() const
{
map<string, unsigned> indices;
+ unsigned index = 0;
for (auto const& s: m_sources)
- indices[s.first] = indices.size();
+ indices[s.first] = index++;
return indices;
}
@@ -417,7 +451,7 @@ Json::Value const& CompilerStack::interface(string const& _contractName) const
Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
return metadata(contract(_contractName), _type);
@@ -425,7 +459,7 @@ Json::Value const& CompilerStack::metadata(string const& _contractName, Document
Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
solAssert(_contract.contract, "");
@@ -456,7 +490,7 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat
string const& CompilerStack::onChainMetadata(string const& _contractName) const
{
- if (!m_parseSuccessful)
+ if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
return contract(_contractName).onChainMetadata;
@@ -522,18 +556,18 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
if (m_sources.count(importPath) || newSources.count(importPath))
continue;
- ReadFileResult result{false, string("File not supplied initially.")};
+ ReadFile::Result result{false, string("File not supplied initially.")};
if (m_readFile)
result = m_readFile(importPath);
if (result.success)
- newSources[importPath] = result.contentsOrErrorMesage;
+ newSources[importPath] = result.contentsOrErrorMessage;
else
{
auto err = make_shared<Error>(Error::Type::ParserError);
*err <<
errinfo_sourceLocation(import->location()) <<
- errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMesage);
+ errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage);
m_errors.push_back(std::move(err));
continue;
}
@@ -655,8 +689,33 @@ void CompilerStack::compileContract(
cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
compiledContract.compiler = compiler;
- compiledContract.object = compiler->assembledObject();
- compiledContract.runtimeObject = compiler->runtimeObject();
+
+ try
+ {
+ compiledContract.object = compiler->assembledObject();
+ }
+ catch(eth::OptimizerException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly optimizer exception for bytecode"));
+ }
+ catch(eth::AssemblyException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for bytecode"));
+ }
+
+ try
+ {
+ compiledContract.runtimeObject = compiler->runtimeObject();
+ }
+ catch(eth::OptimizerException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly optimizer exception for deployed bytecode"));
+ }
+ catch(eth::AssemblyException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for deployed bytecode"));
+ }
+
compiledContract.onChainMetadata = onChainMetadata;
_compiledContracts[compiledContract.contract] = &compiler->assembly();
@@ -841,3 +900,88 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con
}
return ret;
}
+
+namespace
+{
+
+Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
+{
+ if (_gas.isInfinite)
+ return Json::Value("infinite");
+ else
+ return Json::Value(toString(_gas.value));
+}
+
+}
+
+Json::Value CompilerStack::gasEstimates(string const& _contractName) const
+{
+ if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
+ return Json::Value();
+
+ using Gas = GasEstimator::GasConsumption;
+ Json::Value output(Json::objectValue);
+
+ if (eth::AssemblyItems const* items = assemblyItems(_contractName))
+ {
+ Gas executionGas = GasEstimator::functionalEstimation(*items);
+ u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
+ Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
+
+ Json::Value creation(Json::objectValue);
+ creation["codeDepositCost"] = gasToJson(codeDepositGas);
+ creation["executionCost"] = gasToJson(executionGas);
+ /// TODO: implement + overload to avoid the need of +=
+ executionGas += codeDepositGas;
+ creation["totalCost"] = gasToJson(executionGas);
+ output["creation"] = creation;
+ }
+
+ if (eth::AssemblyItems const* items = runtimeAssemblyItems(_contractName))
+ {
+ /// External functions
+ ContractDefinition const& contract = contractDefinition(_contractName);
+ Json::Value externalFunctions(Json::objectValue);
+ for (auto it: contract.interfaceFunctions())
+ {
+ string sig = it.second->externalSignature();
+ externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
+ }
+
+ if (contract.fallbackFunction())
+ /// This needs to be set to an invalid signature in order to trigger the fallback,
+ /// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
+ /// An empty string ("") would work to trigger the shortcut only.
+ externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
+
+ if (!externalFunctions.empty())
+ output["external"] = externalFunctions;
+
+ /// Internal functions
+ Json::Value internalFunctions(Json::objectValue);
+ for (auto const& it: contract.definedFunctions())
+ {
+ /// Exclude externally visible functions, constructor and the fallback function
+ if (it->isPartOfExternalInterface() || it->isConstructor() || it->name().empty())
+ continue;
+
+ size_t entry = functionEntryPoint(_contractName, *it);
+ GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
+ if (entry > 0)
+ gas = GasEstimator::functionalEstimation(*items, entry, *it);
+
+ FunctionType type(*it);
+ string sig = it->name() + "(";
+ auto paramTypes = type.parameterTypes();
+ for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
+ sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ",");
+ sig += ")";
+ internalFunctions[sig] = gasToJson(gas);
+ }
+
+ if (!internalFunctions.empty())
+ output["internal"] = internalFunctions;
+ }
+
+ return output;
+}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index eddfea68..c1d344ca 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -36,6 +36,7 @@
#include <libevmasm/SourceLocation.h>
#include <libevmasm/LinkerObject.h>
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ReadFile.h>
namespace dev
{
@@ -77,18 +78,10 @@ enum class DocumentationType: uint8_t
class CompilerStack: boost::noncopyable
{
public:
- struct ReadFileResult
- {
- bool success;
- std::string contentsOrErrorMesage;
- };
-
- /// File reading callback.
- using ReadFileCallback = std::function<ReadFileResult(std::string const&)>;
-
/// Creates a new compiler stack.
- /// @param _readFile callback to used to read files for import statements. Should return
- explicit CompilerStack(ReadFileCallback const& _readFile = ReadFileCallback());
+ /// @param _readFile callback to used to read files for import statements. Must return
+ /// and must not emit exceptions.
+ explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback());
/// Sets path remappings in the format "context:prefix=target"
void setRemappings(std::vector<std::string> const& _remappings);
@@ -110,6 +103,16 @@ public:
/// 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);
+ /// performs the analyisis steps (imports, scopesetting, syntaxCheck, referenceResolving,
+ /// typechecking, staticAnalysis) on previously set sources
+ /// @returns false on error.
+ bool analyze();
+ /// Parses and analyzes all source units that were added
+ /// @returns false on error.
+ bool parseAndAnalyze();
+ /// Sets the given source code as the only source unit apart from standard sources and parses and analyzes it.
+ /// @returns false on error.
+ bool parseAndAnalyze(std::string const& _sourceCode);
/// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
std::string defaultContractName() const;
@@ -181,6 +184,9 @@ public:
std::string const& onChainMetadata(std::string const& _contractName) const;
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
+ /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions
+ Json::Value gasEstimates(std::string const& _contractName) 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.
@@ -230,6 +236,13 @@ private:
mutable std::unique_ptr<std::string const> sourceMapping;
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
};
+ enum State {
+ Empty,
+ SourcesSet,
+ ParsingSuccessful,
+ AnalysisSuccessful,
+ CompilationSuccessful
+ };
/// Loads the missing sources from @a _ast (named @a _path) using the callback
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
@@ -263,14 +276,13 @@ private:
std::string target;
};
- ReadFileCallback m_readFile;
+ ReadFile::Callback m_readFile;
bool m_optimize = false;
unsigned m_optimizeRuns = 200;
std::map<std::string, h160> m_libraries;
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
/// "context:prefix=target"
std::vector<Remapping> m_remappings;
- bool m_parseSuccessful;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
@@ -279,6 +291,7 @@ private:
std::string m_formalTranslation;
ErrorList m_errors;
bool m_metadataLiteralSources = false;
+ State m_stackState = Empty;
};
}
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index 968a24ad..c09180de 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -33,22 +33,22 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
switch(m_type)
{
case Type::DeclarationError:
- m_typeName = "Declaration Error";
+ m_typeName = "DeclarationError";
break;
case Type::DocstringParsingError:
- m_typeName = "Docstring Parsing Error";
+ m_typeName = "DocstringParsingError";
break;
case Type::ParserError:
- m_typeName = "Parser Error";
+ m_typeName = "ParserError";
break;
case Type::SyntaxError:
- m_typeName = "Syntax Error";
+ m_typeName = "SyntaxError";
break;
case Type::TypeError:
- m_typeName = "Type Error";
+ m_typeName = "TypeError";
break;
case Type::Why3TranslatorError:
- m_typeName = "Why3 Translator Error";
+ m_typeName = "Why3TranslatorError";
break;
case Type::Warning:
m_typeName = "Warning";
diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h
new file mode 100644
index 00000000..2e8a6bd8
--- /dev/null
+++ b/libsolidity/interface/ReadFile.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of solidity.
+
+ solidity 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.
+
+ solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <string>
+#include <functional>
+#include <boost/noncopyable.hpp>
+
+namespace dev
+{
+
+namespace solidity
+{
+
+class ReadFile: boost::noncopyable
+{
+public:
+ /// File reading result.
+ struct Result
+ {
+ bool success;
+ std::string contentsOrErrorMessage;
+ };
+
+ /// File reading callback.
+ using Callback = std::function<Result(std::string const&)>;
+};
+
+}
+}
diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h
index 7034f4ab..e8676d60 100644
--- a/libsolidity/interface/SourceReferenceFormatter.h
+++ b/libsolidity/interface/SourceReferenceFormatter.h
@@ -23,6 +23,7 @@
#pragma once
#include <ostream>
+#include <sstream>
#include <functional>
#include <libevmasm/SourceLocation.h>
@@ -53,6 +54,16 @@ public:
std::string const& _name,
ScannerFromSourceNameFun const& _scannerFromSourceName
);
+ static std::string formatExceptionInformation(
+ Exception const& _exception,
+ std::string const& _name,
+ ScannerFromSourceNameFun const& _scannerFromSourceName
+ )
+ {
+ std::ostringstream errorOutput;
+ printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName);
+ return errorOutput.str();
+ }
private:
/// Prints source name if location is given.
static void printSourceName(
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
new file mode 100644
index 00000000..223cc15d
--- /dev/null
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -0,0 +1,482 @@
+/*
+ This file is part of solidity.
+
+ solidity 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.
+
+ solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Alex Beregszaszi
+ * @date 2016
+ * Standard JSON compiler interface.
+ */
+
+#include <libsolidity/interface/StandardCompiler.h>
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/ast/ASTJsonConverter.h>
+#include <libevmasm/Instruction.h>
+#include <libdevcore/JSON.h>
+#include <libdevcore/SHA3.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+namespace {
+
+Json::Value formatError(
+ bool _warning,
+ string const& _type,
+ string const& _component,
+ string const& _message,
+ string const& _formattedMessage = "",
+ Json::Value const& _sourceLocation = Json::Value()
+)
+{
+ Json::Value error = Json::objectValue;
+ error["type"] = _type;
+ error["component"] = _component;
+ error["severity"] = _warning ? "warning" : "error";
+ error["message"] = _message;
+ error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message;
+ if (_sourceLocation.isObject())
+ error["sourceLocation"] = _sourceLocation;
+ return error;
+}
+
+Json::Value formatFatalError(string const& _type, string const& _message)
+{
+ Json::Value output = Json::objectValue;
+ output["errors"] = Json::arrayValue;
+ output["errors"].append(formatError(false, _type, "general", _message));
+ return output;
+}
+
+Json::Value formatErrorWithException(
+ Exception const& _exception,
+ bool const& _warning,
+ string const& _type,
+ string const& _component,
+ string const& _message,
+ function<Scanner const&(string const&)> const& _scannerFromSourceName
+)
+{
+ string message;
+ string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _message, _scannerFromSourceName);
+
+ // NOTE: the below is partially a copy from SourceReferenceFormatter
+ SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
+
+ if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
+ message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
+ else
+ message = _message;
+
+ if (location && location->sourceName)
+ {
+ Json::Value sourceLocation = Json::objectValue;
+ sourceLocation["file"] = *location->sourceName;
+ sourceLocation["start"] = location->start;
+ sourceLocation["end"] = location->end;
+ }
+
+ return formatError(_warning, _type, _component, message, formattedMessage, location);
+}
+
+/// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content.
+bool hashMatchesContent(string const& _hash, string const& _content)
+{
+ try
+ {
+ return dev::h256(_hash) == dev::keccak256(_content);
+ }
+ catch (dev::BadHexCharacter)
+ {
+ return false;
+ }
+}
+
+StringMap createSourceList(Json::Value const& _input)
+{
+ StringMap sources;
+ Json::Value const& jsonSources = _input["sources"];
+ if (jsonSources.isObject())
+ for (auto const& sourceName: jsonSources.getMemberNames())
+ sources[sourceName] = jsonSources[sourceName]["content"].asString();
+ return sources;
+}
+
+Json::Value methodIdentifiers(ContractDefinition const& _contract)
+{
+ Json::Value methodIdentifiers(Json::objectValue);
+ for (auto const& it: _contract.interfaceFunctions())
+ methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
+ return methodIdentifiers;
+}
+
+Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
+{
+ Json::Value ret(Json::objectValue);
+
+ for (auto const& ref: linkReferences)
+ {
+ string const& fullname = ref.second;
+ size_t colon = fullname.find(':');
+ solAssert(colon != string::npos, "");
+ string file = fullname.substr(0, colon);
+ string name = fullname.substr(colon + 1);
+
+ Json::Value fileObject = ret.get(file, Json::objectValue);
+ Json::Value libraryArray = fileObject.get(name, Json::arrayValue);
+
+ Json::Value entry = Json::objectValue;
+ entry["start"] = Json::UInt(ref.first);
+ entry["length"] = 20;
+
+ libraryArray.append(entry);
+ fileObject[name] = libraryArray;
+ ret[file] = fileObject;
+ }
+
+ return ret;
+}
+
+Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _sourceMap)
+{
+ Json::Value output = Json::objectValue;
+ output["object"] = _object.toHex();
+ output["opcodes"] = solidity::disassemble(_object.bytecode);
+ output["sourceMap"] = _sourceMap ? *_sourceMap : "";
+ output["linkReferences"] = formatLinkReferences(_object.linkReferences);
+ return output;
+}
+
+}
+
+Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
+{
+ m_compilerStack.reset(false);
+
+ if (!_input.isObject())
+ return formatFatalError("JSONError", "Input is not a JSON object.");
+
+ if (_input["language"] != "Solidity")
+ return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
+
+ Json::Value const& sources = _input["sources"];
+ if (!sources)
+ return formatFatalError("JSONError", "No input sources specified.");
+
+ Json::Value errors = Json::arrayValue;
+
+ for (auto const& sourceName: sources.getMemberNames())
+ {
+ string hash;
+
+ if (!sources[sourceName].isObject())
+ return formatFatalError("JSONError", "Source input is not a JSON object.");
+
+ if (sources[sourceName]["keccak256"].isString())
+ hash = sources[sourceName]["keccak256"].asString();
+
+ if (sources[sourceName]["content"].isString())
+ {
+ string content = sources[sourceName]["content"].asString();
+ if (!hash.empty() && !hashMatchesContent(hash, content))
+ errors.append(formatError(
+ false,
+ "IOError",
+ "general",
+ "Mismatch between content and supplied hash for \"" + sourceName + "\""
+ ));
+ else
+ m_compilerStack.addSource(sourceName, content);
+ }
+ else if (sources[sourceName]["urls"].isArray())
+ {
+ if (!m_readFile)
+ return formatFatalError("JSONError", "No import callback supplied, but URL is requested.");
+
+ bool found = false;
+ vector<string> failures;
+
+ for (auto const& url: sources[sourceName]["urls"])
+ {
+ ReadFile::Result result = m_readFile(url.asString());
+ if (result.success)
+ {
+ if (!hash.empty() && !hashMatchesContent(hash, result.contentsOrErrorMessage))
+ errors.append(formatError(
+ false,
+ "IOError",
+ "general",
+ "Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.asString() + "\""
+ ));
+ else
+ {
+ m_compilerStack.addSource(sourceName, result.contentsOrErrorMessage);
+ found = true;
+ break;
+ }
+ }
+ else
+ failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.contentsOrErrorMessage);
+ }
+
+ for (auto const& failure: failures)
+ {
+ /// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors.
+ errors.append(formatError(
+ found ? true : false,
+ "IOError",
+ "general",
+ failure
+ ));
+ }
+ }
+ else
+ return formatFatalError("JSONError", "Invalid input source specified.");
+ }
+
+ Json::Value const& settings = _input.get("settings", Json::Value());
+
+ vector<string> remappings;
+ for (auto const& remapping: settings.get("remappings", Json::Value()))
+ remappings.push_back(remapping.asString());
+ m_compilerStack.setRemappings(remappings);
+
+ Json::Value optimizerSettings = settings.get("optimizer", Json::Value());
+ bool optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
+ unsigned optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();
+
+ map<string, h160> libraries;
+ Json::Value jsonLibraries = settings.get("libraries", Json::Value());
+ for (auto const& sourceName: jsonLibraries.getMemberNames())
+ {
+ auto const& jsonSourceName = jsonLibraries[sourceName];
+ for (auto const& library: jsonSourceName.getMemberNames())
+ // @TODO use libraries only for the given source
+ libraries[library] = h160(jsonSourceName[library].asString());
+ }
+
+ Json::Value metadataSettings = settings.get("metadata", Json::Value());
+ m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());
+
+ auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); };
+
+ bool success = false;
+
+ try
+ {
+ success = m_compilerStack.compile(optimize, optimizeRuns, libraries);
+
+ for (auto const& error: m_compilerStack.errors())
+ {
+ auto err = dynamic_pointer_cast<Error const>(error);
+
+ errors.append(formatErrorWithException(
+ *error,
+ err->type() == Error::Type::Warning,
+ err->typeName(),
+ "general",
+ "",
+ scannerFromSourceName
+ ));
+ }
+ }
+ catch (Error const& _error)
+ {
+ if (_error.type() == Error::Type::DocstringParsingError)
+ errors.append(formatError(
+ false,
+ "DocstringParsingError",
+ "general",
+ "Documentation parsing error: " + *boost::get_error_info<errinfo_comment>(_error)
+ ));
+ else
+ errors.append(formatErrorWithException(
+ _error,
+ false,
+ _error.typeName(),
+ "general",
+ "",
+ scannerFromSourceName
+ ));
+ }
+ catch (CompilerError const& _exception)
+ {
+ errors.append(formatErrorWithException(
+ _exception,
+ false,
+ "CompilerError",
+ "general",
+ "Compiler error (" + _exception.lineInfo() + ")",
+ scannerFromSourceName
+ ));
+ }
+ catch (InternalCompilerError const& _exception)
+ {
+ errors.append(formatErrorWithException(
+ _exception,
+ false,
+ "InternalCompilerError",
+ "general",
+ "Internal compiler error (" + _exception.lineInfo() + ")", scannerFromSourceName
+ ));
+ }
+ catch (UnimplementedFeatureError const& _exception)
+ {
+ errors.append(formatErrorWithException(
+ _exception,
+ false,
+ "UnimplementedFeatureError",
+ "general",
+ "Unimplemented feature (" + _exception.lineInfo() + ")",
+ scannerFromSourceName));
+ }
+ catch (Exception const& _exception)
+ {
+ errors.append(formatError(
+ false,
+ "Exception",
+ "general",
+ "Exception during compilation: " + boost::diagnostic_information(_exception)
+ ));
+ }
+ catch (...)
+ {
+ errors.append(formatError(
+ false,
+ "Exception",
+ "general",
+ "Unknown exception during compilation."
+ ));
+ }
+
+ Json::Value output = Json::objectValue;
+
+ if (errors.size() > 0)
+ output["errors"] = errors;
+
+ /// Inconsistent state - stop here to receive error reports from users
+ if (!success && (errors.size() == 0))
+ return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
+
+ output["sources"] = Json::objectValue;
+ unsigned sourceIndex = 0;
+ for (auto const& source: m_compilerStack.sourceNames())
+ {
+ Json::Value sourceResult = Json::objectValue;
+ sourceResult["id"] = sourceIndex++;
+ sourceResult["legacyAST"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json();
+ output["sources"][source] = sourceResult;
+ }
+
+ Json::Value contractsOutput = Json::objectValue;
+ for (string const& contractName: success ? m_compilerStack.contractNames() : vector<string>())
+ {
+ size_t colon = contractName.find(':');
+ solAssert(colon != string::npos, "");
+ string file = contractName.substr(0, colon);
+ string name = contractName.substr(colon + 1);
+
+ // ABI, documentation and metadata
+ Json::Value contractData(Json::objectValue);
+ contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface);
+ contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
+ contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser);
+ contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev);
+
+ // EVM
+ Json::Value evmData(Json::objectValue);
+ // @TODO: add ir
+ ostringstream tmp;
+ m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false);
+ evmData["assembly"] = tmp.str();
+ evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true);
+ evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName));
+ evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
+
+ evmData["bytecode"] = collectEVMObject(
+ m_compilerStack.object(contractName),
+ m_compilerStack.sourceMapping(contractName)
+ );
+
+ evmData["deployedBytecode"] = collectEVMObject(
+ m_compilerStack.runtimeObject(contractName),
+ m_compilerStack.runtimeSourceMapping(contractName)
+ );
+
+ contractData["evm"] = evmData;
+
+ if (!contractsOutput.isMember(file))
+ contractsOutput[file] = Json::objectValue;
+
+ contractsOutput[file][name] = contractData;
+ }
+ output["contracts"] = contractsOutput;
+
+ return output;
+}
+
+Json::Value StandardCompiler::compile(Json::Value const& _input)
+{
+ try
+ {
+ return compileInternal(_input);
+ }
+ catch (Json::LogicError const& _exception)
+ {
+ return formatFatalError("InternalCompilerError", string("JSON logic exception: ") + _exception.what());
+ }
+ catch (Json::RuntimeError const& _exception)
+ {
+ return formatFatalError("InternalCompilerError", string("JSON runtime exception: ") + _exception.what());
+ }
+ catch (Exception const& _exception)
+ {
+ return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compileInternal: " + boost::diagnostic_information(_exception));
+ }
+ catch (...)
+ {
+ return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compileInternal");
+ }
+}
+
+string StandardCompiler::compile(string const& _input)
+{
+ Json::Value input;
+ Json::Reader reader;
+
+ try
+ {
+ if (!reader.parse(_input, input, false))
+ return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages()));
+ }
+ catch(...)
+ {
+ return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
+ }
+
+ // cout << "Input: " << input.toStyledString() << endl;
+ Json::Value output = compile(input);
+ // cout << "Output: " << output.toStyledString() << endl;
+
+ try
+ {
+ return jsonCompactPrint(output);
+ }
+ catch(...)
+ {
+ return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
+ }
+}
diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h
new file mode 100644
index 00000000..dfaf88cd
--- /dev/null
+++ b/libsolidity/interface/StandardCompiler.h
@@ -0,0 +1,63 @@
+/*
+ This file is part of solidity.
+
+ solidity 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.
+
+ solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Alex Beregszaszi
+ * @date 2016
+ * Standard JSON compiler interface.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/CompilerStack.h>
+
+namespace dev
+{
+
+namespace solidity
+{
+
+/**
+ * Standard JSON compiler interface, which expects a JSON input and returns a JSON ouput.
+ * See docs/using-the-compiler#compiler-input-and-output-json-description.
+ */
+class StandardCompiler: boost::noncopyable
+{
+public:
+ /// Creates a new StandardCompiler.
+ /// @param _readFile callback to used to read files for import statements. Must return
+ /// and must not emit exceptions.
+ StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback())
+ : m_compilerStack(_readFile), m_readFile(_readFile)
+ {
+ }
+
+ /// Sets all input parameters according to @a _input which conforms to the standardized input
+ /// format, performs compilation and returns a standardized output.
+ Json::Value compile(Json::Value const& _input);
+ /// Parses input as JSON and peforms the above processing steps, returning a serialized JSON
+ /// output. Parsing errors are returned as regular errors.
+ std::string compile(std::string const& _input);
+
+private:
+ Json::Value compileInternal(Json::Value const& _input);
+
+ CompilerStack m_compilerStack;
+ ReadFile::Callback m_readFile;
+};
+
+}
+}