aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/interface/CompilerStack.cpp86
-rw-r--r--libsolidity/interface/CompilerStack.h3
-rw-r--r--solc/CommandLineInterface.cpp57
-rw-r--r--solc/jsonCompiler.cpp76
4 files changed, 143 insertions, 79 deletions
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 9ad28573..92b49cda 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>
@@ -841,3 +842,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 9c37eead..a34a34bb 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -173,6 +173,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.
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index f0b73152..947a2004 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -315,49 +315,40 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
void CommandLineInterface::handleGasEstimation(string const& _contract)
{
- using Gas = GasEstimator::GasConsumption;
- if (!m_compiler->assemblyItems(_contract) && !m_compiler->runtimeAssemblyItems(_contract))
- return;
+ Json::Value estimates = m_compiler->gasEstimates(_contract);
cout << "Gas estimation:" << endl;
- if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
+
+ if (estimates["creation"].isObject())
{
- Gas gas = GasEstimator::functionalEstimation(*items);
- u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size());
+ Json::Value creation = estimates["creation"];
cout << "construction:" << endl;
- cout << " " << gas << " + " << (bytecodeSize * eth::GasCosts::createDataGas) << " = ";
- gas += bytecodeSize * eth::GasCosts::createDataGas;
- cout << gas << endl;
+ cout << " " << creation["executionCost"].asString();
+ cout << " + " << creation["codeDepositCost"].asString();
+ cout << " = " << creation["totalCost"].asString() << endl;
}
- if (eth::AssemblyItems const* items = m_compiler->runtimeAssemblyItems(_contract))
+
+ if (estimates["external"].isObject())
{
- ContractDefinition const& contract = m_compiler->contractDefinition(_contract);
+ Json::Value externalFunctions = estimates["external"];
cout << "external:" << endl;
- for (auto it: contract.interfaceFunctions())
- {
- string sig = it.second->externalSignature();
- GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, sig);
- cout << " " << sig << ":\t" << gas << endl;
- }
- if (contract.fallbackFunction())
+ for (auto const& name: externalFunctions.getMemberNames())
{
- GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, "INVALID");
- cout << " fallback:\t" << gas << endl;
+ if (name.empty())
+ cout << " fallback:\t";
+ else
+ cout << " " << name << ":\t";
+ cout << externalFunctions[name].asString() << endl;
}
+ }
+
+ if (estimates["internal"].isObject())
+ {
+ Json::Value internalFunctions = estimates["internal"];
cout << "internal:" << endl;
- for (auto const& it: contract.definedFunctions())
+ for (auto const& name: internalFunctions.getMemberNames())
{
- if (it->isPartOfExternalInterface() || it->isConstructor())
- continue;
- size_t entry = m_compiler->functionEntryPoint(_contract, *it);
- GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
- if (entry > 0)
- gas = GasEstimator::functionalEstimation(*items, entry, *it);
- FunctionType type(*it);
- cout << " " << it->name() << "(";
- auto paramTypes = type.parameterTypes();
- for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
- cout << (*it)->toString() << (it + 1 == paramTypes.end() ? "" : ",");
- cout << "):\t" << gas << endl;
+ cout << " " << name << ":\t";
+ cout << internalFunctions[name].asString() << endl;
}
}
}
diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
index 065716ea..fd375ce3 100644
--- a/solc/jsonCompiler.cpp
+++ b/solc/jsonCompiler.cpp
@@ -58,61 +58,45 @@ Json::Value functionHashes(ContractDefinition const& _contract)
return functionHashes;
}
-Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
+/// Translates a gas value as a string to a JSON number or null
+Json::Value gasToJson(Json::Value const& _value)
{
- if (_gas.isInfinite || _gas.value > std::numeric_limits<Json::LargestUInt>::max())
+ if (_value.isObject())
+ {
+ Json::Value ret = Json::objectValue;
+ for (auto const& sig: _value.getMemberNames())
+ ret[sig] = gasToJson(_value[sig]);
+ return ret;
+ }
+
+ if (_value == "infinite")
+ return Json::Value(Json::nullValue);
+
+ u256 value(_value.asString());
+ if (value > std::numeric_limits<Json::LargestUInt>::max())
return Json::Value(Json::nullValue);
else
- return Json::Value(Json::LargestUInt(_gas.value));
+ return Json::Value(Json::LargestUInt(value));
}
Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
{
- Json::Value gasEstimates(Json::objectValue);
- using Gas = GasEstimator::GasConsumption;
- if (!_compiler.assemblyItems(_contract) && !_compiler.runtimeAssemblyItems(_contract))
- return gasEstimates;
- if (eth::AssemblyItems const* items = _compiler.assemblyItems(_contract))
- {
- Gas gas = GasEstimator::functionalEstimation(*items);
- u256 bytecodeSize(_compiler.runtimeObject(_contract).bytecode.size());
- Json::Value creationGas(Json::arrayValue);
- creationGas[0] = gasToJson(gas);
- creationGas[1] = gasToJson(bytecodeSize * eth::GasCosts::createDataGas);
- gasEstimates["creation"] = creationGas;
- }
- if (eth::AssemblyItems const* items = _compiler.runtimeAssemblyItems(_contract))
+ Json::Value estimates = _compiler.gasEstimates(_contract);
+ Json::Value output(Json::objectValue);
+
+ if (estimates["creation"].isObject())
{
- ContractDefinition const& contract = _compiler.contractDefinition(_contract);
- 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())
- externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
- gasEstimates["external"] = externalFunctions;
- Json::Value internalFunctions(Json::objectValue);
- for (auto const& it: contract.definedFunctions())
- {
- if (it->isPartOfExternalInterface() || it->isConstructor())
- continue;
- size_t entry = _compiler.functionEntryPoint(_contract, *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);
- }
- gasEstimates["internal"] = internalFunctions;
+ Json::Value creation(Json::arrayValue);
+ creation[0] = gasToJson(estimates["creation"]["executionCost"]);
+ creation[1] = gasToJson(estimates["creation"]["codeDepositCost"]);
+ output["creation"] = creation;
}
- return gasEstimates;
+ else
+ output["creation"] = Json::objectValue;
+ output["external"] = gasToJson(estimates.get("external", Json::objectValue));
+ output["internal"] = gasToJson(estimates.get("internal", Json::objectValue));
+
+ return output;
}
string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback)