aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/interface
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-11-14 02:33:35 +0800
committerGitHub <noreply@github.com>2018-11-14 02:33:35 +0800
commit1d4f565a64988a3400847d2655ca24f73f234bc6 (patch)
treecaaa6c26e307513505349b50ca4f2a8a9506752b /libsolidity/interface
parent59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb (diff)
parent91b6b8a88e76016e0324036cb7a7f9300a1e2439 (diff)
downloaddexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.gz
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.bz2
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.lz
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.xz
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.zst
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.zip
Merge pull request #5416 from ethereum/develop
Merge develop into release for 0.5.0
Diffstat (limited to 'libsolidity/interface')
-rw-r--r--libsolidity/interface/AssemblyStack.cpp20
-rw-r--r--libsolidity/interface/AssemblyStack.h6
-rw-r--r--libsolidity/interface/CompilerStack.cpp191
-rw-r--r--libsolidity/interface/CompilerStack.h72
-rw-r--r--libsolidity/interface/EVMVersion.h1
-rw-r--r--libsolidity/interface/ErrorReporter.h6
-rw-r--r--libsolidity/interface/Exceptions.cpp3
-rw-r--r--libsolidity/interface/Exceptions.h2
-rw-r--r--libsolidity/interface/GasEstimator.cpp7
-rw-r--r--libsolidity/interface/Natspec.cpp75
-rw-r--r--libsolidity/interface/Natspec.h8
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp11
-rw-r--r--libsolidity/interface/StandardCompiler.cpp64
-rw-r--r--libsolidity/interface/StandardCompiler.h6
-rw-r--r--libsolidity/interface/Version.cpp6
15 files changed, 284 insertions, 194 deletions
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index 7f97336b..26496de7 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -15,7 +15,7 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
- * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
+ * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and
* eWasm as output.
*/
@@ -31,8 +31,8 @@
#include <libevmasm/Assembly.h>
-#include <libjulia/backends/evm/EVMCodeTransform.h>
-#include <libjulia/backends/evm/EVMAssembly.h>
+#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/backends/evm/EVMAssembly.h>
using namespace std;
using namespace dev;
@@ -48,11 +48,11 @@ assembly::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language)
return assembly::AsmFlavour::Loose;
case AssemblyStack::Language::StrictAssembly:
return assembly::AsmFlavour::Strict;
- case AssemblyStack::Language::JULIA:
- return assembly::AsmFlavour::IULIA;
+ case AssemblyStack::Language::Yul:
+ return assembly::AsmFlavour::Yul;
}
solAssert(false, "");
- return assembly::AsmFlavour::IULIA;
+ return assembly::AsmFlavour::Yul;
}
}
@@ -116,10 +116,10 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
case Machine::EVM15:
{
MachineAssemblyObject object;
- julia::EVMAssembly assembly(true);
- julia::CodeTransform(assembly, *m_analysisInfo, m_language == Language::JULIA, true)(*m_parserResult);
+ yul::EVMAssembly assembly(true);
+ yul::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult);
object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
- /// TOOD: fill out text representation
+ /// TODO: fill out text representation
return object;
}
case Machine::eWasm:
@@ -132,5 +132,5 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
string AssemblyStack::print() const
{
solAssert(m_parserResult, "");
- return assembly::AsmPrinter(m_language == Language::JULIA)(*m_parserResult);
+ return assembly::AsmPrinter(m_language == Language::Yul)(*m_parserResult);
}
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index 720220ab..8132ce63 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -15,7 +15,7 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
- * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
+ * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and
* eWasm as output.
*/
@@ -47,13 +47,13 @@ struct MachineAssemblyObject
};
/*
- * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
+ * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and
* eWasm as output.
*/
class AssemblyStack
{
public:
- enum class Language { JULIA, Assembly, StrictAssembly };
+ enum class Language { Yul, Assembly, StrictAssembly };
enum class Machine { EVM, EVM15, eWasm };
explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly):
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 47dc30cf..441c7897 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -47,6 +47,8 @@
#include <libevmasm/Exceptions.h>
+#include <libyul/YulString.h>
+
#include <libdevcore/SwarmHash.h>
#include <libdevcore/JSON.h>
@@ -58,22 +60,31 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-void CompilerStack::setRemappings(vector<string> const& _remappings)
+boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping)
+{
+ auto eq = find(_remapping.begin(), _remapping.end(), '=');
+ if (eq == _remapping.end())
+ return {};
+
+ auto colon = find(_remapping.begin(), eq, ':');
+
+ Remapping r;
+
+ r.context = colon == eq ? string() : string(_remapping.begin(), colon);
+ r.prefix = colon == eq ? string(_remapping.begin(), eq) : string(colon + 1, eq);
+ r.target = string(eq + 1, _remapping.end());
+
+ if (r.prefix.empty())
+ return {};
+
+ return r;
+}
+
+void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
{
- vector<Remapping> remappings;
for (auto const& remapping: _remappings)
- {
- auto eq = find(remapping.begin(), remapping.end(), '=');
- if (eq == remapping.end())
- continue; // ignore
- auto colon = find(remapping.begin(), eq, ':');
- Remapping r;
- r.context = colon == eq ? string() : string(remapping.begin(), colon);
- r.prefix = colon == eq ? string(remapping.begin(), eq) : string(colon + 1, eq);
- r.target = string(eq + 1, remapping.end());
- remappings.push_back(r);
- }
- swap(m_remappings, remappings);
+ solAssert(!remapping.prefix.empty(), "");
+ m_remappings = _remappings;
}
void CompilerStack::setEVMVersion(EVMVersion _version)
@@ -119,7 +130,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
bool CompilerStack::parse()
{
//reset
- if(m_stackState != SourcesSet)
+ if (m_stackState != SourcesSet)
return false;
m_errorReporter.clear();
ASTNode::resetID();
@@ -191,6 +202,8 @@ bool CompilerStack::analyze()
if (!resolver.performImports(*source->ast, sourceUnitsByName))
return false;
+ // This is the main name and type resolution loop. Needs to be run for every contract, because
+ // the special variables "this" and "super" must be set appropriately.
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@@ -204,11 +217,15 @@ bool CompilerStack::analyze()
// thus contracts can only conflict if declared in the same source file. This
// already causes a double-declaration error elsewhere, so we do not report
// an error here and instead silently drop any additional contracts we find.
-
if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end())
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
+ // This cannot be done in the above loop, because cross-contract types couldn't be resolved.
+ // A good example is `LibraryName.TypeName x;`.
+ //
+ // Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed,
+ // which is only done one step later.
TypeChecker typeChecker(m_evmVersion, m_errorReporter);
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
@@ -218,6 +235,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Checks that can only be done when all types of all AST nodes are known.
PostTypeChecker postTypeChecker(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!postTypeChecker.check(*source->ast))
@@ -226,6 +244,8 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Control flow graph generator and analyzer. It can check for issues such as
+ // variable is used before it is assigned to.
CFG cfg(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!cfg.constructFlow(*source->ast))
@@ -242,6 +262,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Checks for common mistakes. Only generates warnings.
StaticAnalyzer staticAnalyzer(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!staticAnalyzer.analyze(*source->ast))
@@ -250,6 +271,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Check for state mutability in every function.
vector<ASTPointer<ASTNode>> ast;
for (Source const* source: m_sourceOrder)
ast.push_back(source->ast);
@@ -262,7 +284,7 @@ bool CompilerStack::analyze()
{
SMTChecker smtChecker(m_errorReporter, m_smtQuery);
for (Source const* source: m_sourceOrder)
- smtChecker.analyze(*source->ast);
+ smtChecker.analyze(*source->ast, source->scanner);
}
}
catch(FatalError const&)
@@ -300,6 +322,7 @@ bool CompilerStack::compile()
if (!parseAndAnalyze())
return false;
+ // Only compile contracts individually which have been requested.
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
@@ -317,7 +340,6 @@ void CompilerStack::link()
{
contract.second.object.link(m_libraries);
contract.second.runtimeObject.link(m_libraries);
- contract.second.cloneObject.link(m_libraries);
}
}
@@ -396,11 +418,6 @@ eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractNam
return contract(_contractName).runtimeObject;
}
-eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) const
-{
- return contract(_contractName).cloneObject;
-}
-
/// FIXME: cache this string
string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const
{
@@ -571,7 +588,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
for (auto const& node: _ast.nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{
- string importPath = absolutePath(import->path(), _sourcePath);
+ string importPath = dev::absolutePath(import->path(), _sourcePath);
// The current value of `path` is the absolute path as seen from this source file.
// We first have to apply remappings before we can store the actual absolute path
// as seen globally.
@@ -614,8 +631,8 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
for (auto const& redir: m_remappings)
{
- string context = sanitizePath(redir.context);
- string prefix = sanitizePath(redir.prefix);
+ string context = dev::sanitizePath(redir.context);
+ string prefix = dev::sanitizePath(redir.prefix);
// Skip if current context is closer
if (context.length() < longestContext)
@@ -632,7 +649,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
longestContext = context.length();
longestPrefix = prefix.length();
- bestMatchTarget = sanitizePath(redir.target);
+ bestMatchTarget = dev::sanitizePath(redir.target);
}
string path = bestMatchTarget;
path.append(_path.begin() + longestPrefix, _path.end());
@@ -669,23 +686,6 @@ void CompilerStack::resolveImports()
swap(m_sourceOrder, sourceOrder);
}
-string CompilerStack::absolutePath(string const& _path, string const& _reference) const
-{
- using path = boost::filesystem::path;
- path p(_path);
- // Anything that does not start with `.` is an absolute path.
- if (p.begin() == p.end() || (*p.begin() != "." && *p.begin() != ".."))
- return _path;
- path result(_reference);
- result.remove_filename();
- for (path::iterator it = p.begin(); it != p.end(); ++it)
- if (*it == "..")
- result = result.parent_path();
- else if (*it != ".")
- result /= *it;
- return result.generic_string();
-}
-
namespace
{
bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features)
@@ -711,39 +711,33 @@ void CompilerStack::compileContract(
for (auto const* dependency: _contract.annotation().contractDependencies)
compileContract(*dependency, _compiledContracts);
- shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
- string metadata = createMetadata(compiledContract);
- bytes cborEncodedHash =
- // CBOR-encoding of the key "bzzr0"
- bytes{0x65, 'b', 'z', 'z', 'r', '0'}+
- // CBOR-encoding of the hash
- bytes{0x58, 0x20} + dev::swarmHash(metadata).asBytes();
- bytes cborEncodedMetadata;
- if (onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures))
- cborEncodedMetadata =
- // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)}
- bytes{0xa1} +
- cborEncodedHash;
- else
- cborEncodedMetadata =
- // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true}
- bytes{0xa2} +
- cborEncodedHash +
- bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5};
- solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large");
- // 16-bit big endian length
- cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
- compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
+
+ shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns);
compiledContract.compiler = compiler;
+ string metadata = createMetadata(compiledContract);
+ compiledContract.metadata = metadata;
+
+ bytes cborEncodedMetadata = createCBORMetadata(
+ metadata,
+ !onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures)
+ );
+
try
{
- compiledContract.object = compiler->assembledObject();
+ // Run optimiser and compile the contract.
+ compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
}
catch(eth::OptimizerException const&)
{
- solAssert(false, "Assembly optimizer exception for bytecode");
+ solAssert(false, "Optimizer exception during compilation");
+ }
+
+ try
+ {
+ // Assemble deployment (incl. runtime) object.
+ compiledContract.object = compiler->assembledObject();
}
catch(eth::AssemblyException const&)
{
@@ -752,36 +746,15 @@ void CompilerStack::compileContract(
try
{
+ // Assemble runtime object.
compiledContract.runtimeObject = compiler->runtimeObject();
}
- catch(eth::OptimizerException const&)
- {
- solAssert(false, "Assembly optimizer exception for deployed bytecode");
- }
catch(eth::AssemblyException const&)
{
solAssert(false, "Assembly exception for deployed bytecode");
}
- compiledContract.metadata = metadata;
_compiledContracts[compiledContract.contract] = &compiler->assembly();
-
- try
- {
- if (!_contract.isLibrary())
- {
- Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns);
- cloneCompiler.compileClone(_contract, _compiledContracts);
- compiledContract.cloneObject = cloneCompiler.assembledObject();
- }
- }
- catch (eth::AssemblyException const&)
- {
- // In some cases (if the constructor requests a runtime function), it is not
- // possible to compile the clone.
-
- // TODO: Report error / warning
- }
}
string const CompilerStack::lastContractName() const
@@ -809,7 +782,7 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa
// To provide a measure of backward-compatibility, if a contract is not located by its
// fully-qualified name, a lookup will be attempted purely on the contract's name to see
// if anything will satisfy.
- if (_contractName.find(":") == string::npos)
+ if (_contractName.find(':') == string::npos)
{
for (auto const& contractEntry: m_contracts)
{
@@ -894,6 +867,31 @@ string CompilerStack::createMetadata(Contract const& _contract) const
return jsonCompactPrint(meta);
}
+bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode)
+{
+ bytes cborEncodedHash =
+ // CBOR-encoding of the key "bzzr0"
+ bytes{0x65, 'b', 'z', 'z', 'r', '0'}+
+ // CBOR-encoding of the hash
+ bytes{0x58, 0x20} + dev::swarmHash(_metadata).asBytes();
+ bytes cborEncodedMetadata;
+ if (_experimentalMode)
+ cborEncodedMetadata =
+ // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true}
+ bytes{0xa2} +
+ cborEncodedHash +
+ bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5};
+ else
+ cborEncodedMetadata =
+ // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)}
+ bytes{0xa1} +
+ cborEncodedHash;
+ solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large");
+ // 16-bit big endian length
+ cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
+ return cborEncodedMetadata;
+}
+
string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const
{
string ret;
@@ -938,17 +936,17 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con
if (components-- > 0)
{
if (location.start != prevStart)
- ret += std::to_string(location.start);
+ ret += to_string(location.start);
if (components-- > 0)
{
ret += ':';
if (length != prevLength)
- ret += std::to_string(length);
+ ret += to_string(length);
if (components-- > 0)
{
ret += ':';
if (sourceIndex != prevSourceIndex)
- ret += std::to_string(sourceIndex);
+ ret += to_string(sourceIndex);
if (components-- > 0)
{
ret += ':';
@@ -992,8 +990,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
{
Gas executionGas = gasEstimator.functionalEstimation(*items);
- u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
- Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
+ Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)};
Json::Value creation(Json::objectValue);
creation["codeDepositCost"] = gasToJson(codeDepositGas);
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 13c9cc7a..9a15fbf0 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -36,7 +36,6 @@
#include <json/json.h>
#include <boost/noncopyable.hpp>
-#include <boost/filesystem.hpp>
#include <ostream>
#include <string>
@@ -85,6 +84,13 @@ public:
CompilationSuccessful
};
+ struct Remapping
+ {
+ std::string context;
+ std::string prefix;
+ std::string target;
+ };
+
/// Creates a new compiler stack.
/// @param _readFile callback to used to read files for import statements. Must return
/// and must not emit exceptions.
@@ -93,7 +99,7 @@ public:
m_errorList(),
m_errorReporter(m_errorList) {}
- /// @returns the list of errors that occured during parsing and type checking.
+ /// @returns the list of errors that occurred during parsing and type checking.
ErrorList const& errors() const { return m_errorReporter.errors(); }
/// @returns the current state.
@@ -104,8 +110,11 @@ public:
/// All settings, with the exception of remappings, are reset.
void reset(bool _keepSources = false);
- /// Sets path remappings in the format "context:prefix=target"
- void setRemappings(std::vector<std::string> const& _remappings);
+ // Parses a remapping of the format "context:prefix=target".
+ static boost::optional<Remapping> parseRemapping(std::string const& _remapping);
+
+ /// Sets path remappings.
+ void setRemappings(std::vector<Remapping> const& _remappings);
/// Sets library addresses. Addresses are cleared iff @a _libraries is missing.
/// Will not take effect before running compile.
@@ -122,6 +131,8 @@ public:
m_optimizeRuns = _runs;
}
+ /// Set the EVM version used before running compile.
+ /// When called without an argument it will revert to the default version.
void setEVMVersion(EVMVersion _version = EVMVersion{});
/// Sets the list of requested contract names. If empty, no filtering is performed and every contract
@@ -188,12 +199,6 @@ public:
/// @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 DELEGATECALL.
- /// 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;
@@ -240,9 +245,7 @@ public:
Json::Value gasEstimates(std::string const& _contractName) const;
private:
- /**
- * Information pertaining to one source unit, filled gradually during parsing and compilation.
- */
+ /// The state per source unit. Filled gradually during parsing.
struct Source
{
std::shared_ptr<Scanner> scanner;
@@ -251,13 +254,13 @@ private:
void reset() { scanner.reset(); ast.reset(); }
};
+ /// The state per contract. Filled gradually during compilation.
struct Contract
{
ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler;
- eth::LinkerObject object;
- eth::LinkerObject runtimeObject;
- eth::LinkerObject cloneObject;
+ eth::LinkerObject object; ///< Deployment object (includes the runtime sub-object).
+ eth::LinkerObject runtimeObject; ///< Runtime object.
std::string metadata; ///< The metadata json that will be hashed into the chain.
mutable std::unique_ptr<Json::Value const> abi;
mutable std::unique_ptr<Json::Value const> userDocumentation;
@@ -272,10 +275,6 @@ private:
StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path);
std::string applyRemapping(std::string const& _path, std::string const& _context);
void resolveImports();
- /// @returns the absolute path corresponding to @a _path relative to @a _reference.
- std::string absolutePath(std::string const& _path, std::string const& _reference) const;
- /// Helper function to return path converted strings.
- std::string sanitizePath(std::string const& _path) const { return boost::filesystem::path(_path).generic_string(); }
/// @returns true if the contract is requested to be compiled.
bool isRequestedContract(ContractDefinition const& _contract) const;
@@ -285,19 +284,42 @@ private:
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
);
+
+ /// Links all the known library addresses in the available objects. Any unknown
+ /// library will still be kept as an unlinked placeholder in the objects.
void link();
+ /// @returns the contract object for the given @a _contractName.
+ /// Can only be called after state is CompilationSuccessful.
Contract const& contract(std::string const& _contractName) const;
+
+ /// @returns the source object for the given @a _sourceName.
+ /// Can only be called after state is SourcesSet.
Source const& source(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 metadata JSON as a compact string for the given contract.
std::string createMetadata(Contract const& _contract) const;
+
+ /// @returns the metadata CBOR for the given serialised metadata JSON.
+ static bytes createCBORMetadata(std::string _metadata, bool _experimentalMode);
+
+ /// @returns the computer source mapping string.
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
+
+ /// @returns the contract ABI as a JSON object.
+ /// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& contractABI(Contract const&) const;
+
+ /// @returns the Natspec User documentation as a JSON object.
+ /// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& natspecUser(Contract const&) const;
+
+ /// @returns the Natspec Developer documentation as a JSON object.
+ /// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& natspecDev(Contract const&) const;
/// @returns the offset of the entry point of the given function into the list of assembly items
@@ -307,13 +329,6 @@ private:
FunctionDefinition const& _function
) const;
- struct Remapping
- {
- std::string context;
- std::string prefix;
- std::string target;
- };
-
ReadCallback::Callback m_readFile;
ReadCallback::Callback m_smtQuery;
bool m_optimize = false;
@@ -326,8 +341,9 @@ private:
std::vector<Remapping> m_remappings;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
- std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
std::vector<Source const*> m_sourceOrder;
+ /// This is updated during compilation.
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
std::map<std::string const, Contract> m_contracts;
ErrorList m_errorList;
ErrorReporter m_errorReporter;
diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h
index b68e1f4e..657727ac 100644
--- a/libsolidity/interface/EVMVersion.h
+++ b/libsolidity/interface/EVMVersion.h
@@ -75,6 +75,7 @@ public:
bool supportsReturndata() const { return *this >= byzantium(); }
bool hasStaticCall() const { return *this >= byzantium(); }
bool hasBitwiseShifting() const { return *this >= constantinople(); }
+ bool hasCreate2() const { return *this >= constantinople(); }
/// Whether we have to retain the costs for the call opcode itself (false),
/// or whether we can just forward easily all remaining gas (true).
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
index d1a0030f..fd53587a 100644
--- a/libsolidity/interface/ErrorReporter.h
+++ b/libsolidity/interface/ErrorReporter.h
@@ -92,6 +92,12 @@ public:
void clear();
+ /// @returns true iff there is any error (ignores warnings).
+ bool hasErrors() const
+ {
+ return m_errorCount > 0;
+ }
+
private:
void error(Error::Type _type,
SourceLocation const& _location,
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index a837dce6..ecadd0b7 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -49,9 +49,6 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
case Type::Warning:
m_typeName = "Warning";
break;
- default:
- solAssert(false, "");
- break;
}
if (!_location.isEmpty())
diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h
index 7c66d572..629b8f3f 100644
--- a/libsolidity/interface/Exceptions.h
+++ b/libsolidity/interface/Exceptions.h
@@ -117,7 +117,7 @@ public:
if (occurrences > 32)
{
infos.resize(32);
- _message += " Truncated from " + boost::lexical_cast<std::string>(occurrences) + " to the first 32 occurrences.";
+ _message += " Truncated from " + std::to_string(occurrences) + " to the first 32 occurrences.";
}
}
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index a532f86e..1f20366e 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -24,7 +24,7 @@
#include <map>
#include <functional>
#include <memory>
-#include <libdevcore/SHA3.h>
+#include <libdevcore/Keccak256.h>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/KnownState.h>
#include <libevmasm/PathGasMeter.h>
@@ -160,8 +160,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
);
}
- PathGasMeter meter(_items, m_evmVersion);
- return meter.estimateMax(0, state);
+ return PathGasMeter::estimateMax(_items, m_evmVersion, 0, state);
}
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
@@ -183,7 +182,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
if (parametersSize > 0)
state->feedItem(swapInstruction(parametersSize));
- return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state);
+ return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state);
}
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
index 7f7084ef..11dde349 100644
--- a/libsolidity/interface/Natspec.cpp
+++ b/libsolidity/interface/Natspec.cpp
@@ -36,6 +36,19 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
Json::Value doc;
Json::Value methods(Json::objectValue);
+ auto constructorDefinition(_contractDef.constructor());
+ if (constructorDefinition)
+ {
+ string value = extractDoc(constructorDefinition->annotation().docTags, "notice");
+ if (!value.empty())
+ // add the constructor, only if we have any documentation to add
+ methods["constructor"] = Json::Value(value);
+ }
+
+ string notice = extractDoc(_contractDef.annotation().docTags, "notice");
+ if (!notice.empty())
+ doc["notice"] = Json::Value(notice);
+
for (auto const& it: _contractDef.interfaceFunctions())
if (it.second->hasDeclaration())
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
@@ -65,34 +78,26 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
auto title = extractDoc(_contractDef.annotation().docTags, "title");
if (!title.empty())
doc["title"] = title;
+ auto dev = extractDoc(_contractDef.annotation().docTags, "dev");
+ if (!dev.empty())
+ doc["details"] = Json::Value(dev);
+
+ auto constructorDefinition(_contractDef.constructor());
+ if (constructorDefinition)
+ {
+ Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags));
+ if (!constructor.empty())
+ // add the constructor, only if we have any documentation to add
+ methods["constructor"] = constructor;
+ }
for (auto const& it: _contractDef.interfaceFunctions())
{
if (!it.second->hasDeclaration())
continue;
- Json::Value method;
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
{
- auto dev = extractDoc(fun->annotation().docTags, "dev");
- if (!dev.empty())
- method["details"] = Json::Value(dev);
-
- auto author = extractDoc(fun->annotation().docTags, "author");
- if (!author.empty())
- method["author"] = author;
-
- auto ret = extractDoc(fun->annotation().docTags, "return");
- if (!ret.empty())
- method["return"] = ret;
-
- Json::Value params(Json::objectValue);
- auto paramRange = fun->annotation().docTags.equal_range("param");
- for (auto i = paramRange.first; i != paramRange.second; ++i)
- params[i->second.paramName] = Json::Value(i->second.content);
-
- if (!params.empty())
- method["params"] = params;
-
+ Json::Value method(devDocumentation(fun->annotation().docTags));
if (!method.empty())
// add the function, only if we have any documentation to add
methods[it.second->externalSignature()] = method;
@@ -111,3 +116,31 @@ string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const&
value += i->second.content;
return value;
}
+
+Json::Value Natspec::devDocumentation(std::multimap<std::string, DocTag> const &_tags)
+{
+ Json::Value json(Json::objectValue);
+ auto dev = extractDoc(_tags, "dev");
+ if (!dev.empty())
+ json["details"] = Json::Value(dev);
+
+ auto author = extractDoc(_tags, "author");
+ if (!author.empty())
+ json["author"] = author;
+
+ // for constructors, the "return" node will never exist. invalid tags
+ // will already generate an error within dev::solidity::DocStringAnalyzer.
+ auto ret = extractDoc(_tags, "return");
+ if (!ret.empty())
+ json["return"] = ret;
+
+ Json::Value params(Json::objectValue);
+ auto paramRange = _tags.equal_range("param");
+ for (auto i = paramRange.first; i != paramRange.second; ++i)
+ params[i->second.paramName] = Json::Value(i->second.content);
+
+ if (!params.empty())
+ json["params"] = params;
+
+ return json;
+}
diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h
index 0701f821..0be4dda2 100644
--- a/libsolidity/interface/Natspec.h
+++ b/libsolidity/interface/Natspec.h
@@ -45,7 +45,7 @@ public:
/// @param _contractDef The contract definition
/// @return A JSON representation of the contract's user documentation
static Json::Value userDocumentation(ContractDefinition const& _contractDef);
- /// Genereates the Developer's documentation of the contract
+ /// Generates the Developer's documentation of the contract
/// @param _contractDef The contract definition
/// @return A JSON representation
/// of the contract's developer documentation
@@ -54,6 +54,12 @@ public:
private:
/// @returns concatenation of all content under the given tag name.
static std::string extractDoc(std::multimap<std::string, DocTag> const& _tags, std::string const& _name);
+
+ /// Helper-function that will create a json object with dev specific annotations, if present.
+ /// @param _tags docTags that are used.
+ /// @return A JSON representation
+ /// of the contract's developer documentation
+ static Json::Value devDocumentation(std::multimap<std::string, DocTag> const &_tags);
};
} //solidity NS
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
index 0f014372..865907e2 100644
--- a/libsolidity/interface/SourceReferenceFormatter.cpp
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -55,8 +55,15 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati
}
if (line.length() > 150)
{
- line = " ... " + line.substr(startColumn, locationLength) + " ... ";
- startColumn = 5;
+ int len = line.length();
+ line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
+ if (startColumn + locationLength + 35 < len)
+ line += " ...";
+ if (startColumn > 35)
+ {
+ line = " ... " + line;
+ startColumn = 40;
+ }
endColumn = startColumn + locationLength;
}
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index ee9b1440..c8b03a94 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -25,7 +25,7 @@
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>
-#include <libdevcore/SHA3.h>
+#include <libdevcore/Keccak256.h>
#include <boost/algorithm/string.hpp>
@@ -117,7 +117,7 @@ bool hashMatchesContent(string const& _hash, string const& _content)
{
return dev::h256(_hash) == dev::keccak256(_content);
}
- catch (dev::BadHexCharacter)
+ catch (dev::BadHexCharacter const&)
{
return false;
}
@@ -280,6 +280,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
for (auto const& url: sources[sourceName]["urls"])
{
+ if (!url.isString())
+ return formatFatalError("JSONError", "URL must be a string.");
ReadCallback::Result result = m_readFile(url.asString());
if (result.success)
{
@@ -320,21 +322,45 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (settings.isMember("evmVersion"))
{
- boost::optional<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString());
+ if (!settings["evmVersion"].isString())
+ return formatFatalError("JSONError", "evmVersion must be a string.");
+ boost::optional<EVMVersion> version = EVMVersion::fromString(settings["evmVersion"].asString());
if (!version)
return formatFatalError("JSONError", "Invalid EVM version requested.");
m_compilerStack.setEVMVersion(*version);
}
- vector<string> remappings;
+ vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
- remappings.push_back(remapping.asString());
+ {
+ if (!remapping.isString())
+ return formatFatalError("JSONError", "Remapping entry must be a string.");
+ if (auto r = CompilerStack::parseRemapping(remapping.asString()))
+ remappings.emplace_back(std::move(*r));
+ else
+ return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\"");
+ }
m_compilerStack.setRemappings(remappings);
- Json::Value optimizerSettings = settings.get("optimizer", Json::Value());
- bool const optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
- unsigned const optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();
- m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
+ if (settings.isMember("optimizer"))
+ {
+ Json::Value optimizerSettings = settings["optimizer"];
+ if (optimizerSettings.isMember("enabled"))
+ {
+ if (!optimizerSettings["enabled"].isBool())
+ return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean.");
+
+ bool const optimize = optimizerSettings["enabled"].asBool();
+ unsigned optimizeRuns = 200;
+ if (optimizerSettings.isMember("runs"))
+ {
+ if (!optimizerSettings["runs"].isUInt())
+ return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
+ optimizeRuns = optimizerSettings["runs"].asUInt();
+ }
+ m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
+ }
+ }
map<string, h160> libraries;
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
@@ -344,9 +370,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
auto const& jsonSourceName = jsonLibraries[sourceName];
if (!jsonSourceName.isObject())
- return formatFatalError("JSONError", "library entry is not a JSON object.");
+ return formatFatalError("JSONError", "Library entry is not a JSON object.");
for (auto const& library: jsonSourceName.getMemberNames())
{
+ if (!jsonSourceName[library].isString())
+ return formatFatalError("JSONError", "Library address must be a string.");
string address = jsonSourceName[library].asString();
if (!boost::starts_with(address, "0x"))
@@ -366,7 +394,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// @TODO use libraries only for the given source
libraries[library] = h160(address);
}
- catch (dev::BadHexCharacter)
+ catch (dev::BadHexCharacter const&)
{
return formatFatalError(
"JSONError",
@@ -481,7 +509,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
/// Inconsistent state - stop here to receive error reports from users
- if (!compilationSuccess && (errors.size() == 0))
+ if (!compilationSuccess && errors.empty())
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
Json::Value output = Json::objectValue;
@@ -567,7 +595,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
return output;
}
-Json::Value StandardCompiler::compile(Json::Value const& _input)
+Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
{
try
{
@@ -591,7 +619,7 @@ Json::Value StandardCompiler::compile(Json::Value const& _input)
}
}
-string StandardCompiler::compile(string const& _input)
+string StandardCompiler::compile(string const& _input) noexcept
{
Json::Value input;
string errors;
@@ -600,9 +628,9 @@ string StandardCompiler::compile(string const& _input)
if (!jsonParseStrict(_input, input, &errors))
return jsonCompactPrint(formatFatalError("JSONError", errors));
}
- catch(...)
+ catch (...)
{
- return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
+ return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
}
// cout << "Input: " << input.toStyledString() << endl;
@@ -613,8 +641,8 @@ string StandardCompiler::compile(string const& _input)
{
return jsonCompactPrint(output);
}
- catch(...)
+ catch (...)
{
- return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
+ 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
index 11a0b4c2..fc9c3a59 100644
--- a/libsolidity/interface/StandardCompiler.h
+++ b/libsolidity/interface/StandardCompiler.h
@@ -31,7 +31,7 @@ namespace solidity
{
/**
- * Standard JSON compiler interface, which expects a JSON input and returns a JSON ouput.
+ * Standard JSON compiler interface, which expects a JSON input and returns a JSON output.
* See docs/using-the-compiler#compiler-input-and-output-json-description.
*/
class StandardCompiler: boost::noncopyable
@@ -47,10 +47,10 @@ public:
/// 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);
+ Json::Value compile(Json::Value const& _input) noexcept;
/// 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);
+ std::string compile(std::string const& _input) noexcept;
private:
Json::Value compileInternal(Json::Value const& _input);
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
index a35bfd29..b5f68ce8 100644
--- a/libsolidity/interface/Version.cpp
+++ b/libsolidity/interface/Version.cpp
@@ -55,13 +55,13 @@ bytes dev::solidity::binaryVersion()
ret = ret * 10 + (VersionString[i] - '0');
return ret;
};
- ret.push_back(byte(parseDecimal()));
+ ret.push_back(uint8_t(parseDecimal()));
solAssert(i < VersionString.size() && VersionString[i] == '.', "");
++i;
- ret.push_back(byte(parseDecimal()));
+ ret.push_back(uint8_t(parseDecimal()));
solAssert(i < VersionString.size() && VersionString[i] == '.', "");
++i;
- ret.push_back(byte(parseDecimal()));
+ ret.push_back(uint8_t(parseDecimal()));
solAssert(i < VersionString.size() && (VersionString[i] == '-' || VersionString[i] == '+'), "");
++i;
size_t commitpos = VersionString.find("commit.");