Browse Source

Merge pull request #2023 from yann300/gasEstimation

Mix - Functional gas estimation
cl-refactor
Arkadiy Paronyan 10 years ago
parent
commit
01a2289c85
  1. 2
      mix/ClientModel.cpp
  2. 30
      mix/CodeModel.cpp
  3. 65
      mix/CodeModel.h
  4. 17
      mix/qml/Application.qml
  5. 8
      mix/qml/html/cm/inkpot.css
  6. 31
      mix/qml/html/codeeditor.js

2
mix/ClientModel.cpp

@ -331,7 +331,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
{ {
QSolidityType const* type = p->type(); QSolidityType const* type = p->type();
QVariant value = transaction.parameterValues.value(p->name()); QVariant value = transaction.parameterValues.value(p->name());
if (type->type().type == SolidityType::Type::Address) if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<"))
{ {
std::pair<QString, int> ctrParamInstance = resolvePair(value.toString()); std::pair<QString, int> ctrParamInstance = resolvePair(value.toString());
value = QVariant(resolveToken(ctrParamInstance, deployedContracts)); value = QVariant(resolveToken(ctrParamInstance, deployedContracts));

30
mix/CodeModel.cpp

@ -309,7 +309,7 @@ void CodeModel::runCompilationJob(int _jobId)
sourceNames.push_back(c.first.toStdString()); sourceNames.push_back(c.first.toStdString());
} }
} }
cs.compile(false); cs.compile(m_optimizeCode);
gasEstimation(cs); gasEstimation(cs);
collectContracts(cs, sourceNames); collectContracts(cs, sourceNames);
} }
@ -380,7 +380,23 @@ void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
GasMeter::GasConsumption cost = gasItem->second; GasMeter::GasConsumption cost = gasItem->second;
std::stringstream v; std::stringstream v;
v << cost.value; v << cost.value;
m_gasCostsMaps->push(sourceName, location.start, location.end, QString::fromStdString(v.str()), cost.isInfinite); m_gasCostsMaps->push(sourceName, location.start, location.end, QString::fromStdString(v.str()), cost.isInfinite, GasMap::type::Statement);
}
if (contractDefinition.getConstructor() != nullptr)
{
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(*_cs.getRuntimeAssemblyItems(n), contractDefinition.getConstructor()->externalSignature());
std::stringstream v;
v << cost.value;
m_gasCostsMaps->push(sourceName, contractDefinition.getConstructor()->getLocation().start, contractDefinition.getConstructor()->getLocation().end, QString::fromStdString(v.str()), cost.isInfinite, GasMap::type::Constructor);
}
for (auto func: contractDefinition.getDefinedFunctions())
{
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(*_cs.getRuntimeAssemblyItems(n), func->externalSignature());
std::stringstream v;
v << cost.value;
m_gasCostsMaps->push(sourceName, func->getLocation().start, func->getLocation().end, QString::fromStdString(v.str()), cost.isInfinite, GasMap::type::Function);
} }
} }
} }
@ -583,9 +599,15 @@ QString CodeModel::resolveFunctionName(dev::SourceLocation const& _location)
return QString(); return QString();
} }
void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite) void CodeModel::setOptimizeCode(bool _value)
{
m_optimizeCode = _value;
emit scheduleCompilationJob(++m_backgroundJobId);
}
void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type)
{ {
GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, this); GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, _type, this);
m_gasMaps.find(_source).value().push_back(QVariant::fromValue(gas)); m_gasMaps.find(_source).value().push_back(QVariant::fromValue(gas));
} }

65
mix/CodeModel.h

@ -28,6 +28,7 @@
#include <QObject> #include <QObject>
#include <QThread> #include <QThread>
#include <QHash> #include <QHash>
#include <QMetaEnum>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
@ -130,39 +131,60 @@ struct SourceMap
using SourceMaps = QMap<QString, SourceMap>; //by source id using SourceMaps = QMap<QString, SourceMap>; //by source id
using GasCostsMaps = QMap<QString, QVariantList>; //gas cost by contract name using GasCostsMaps = QMap<QString, QVariantList>; //gas cost by contract name
class GasMapWrapper: public QObject
{
Q_OBJECT
Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT)
public:
GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){}
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite);
bool contains(QString _key);
void insert(QString _source, QVariantList _variantList);
QVariantList gasCostsByDocId(QString _source);
private:
GasCostsMaps m_gasMaps;
};
class GasMap: public QObject class GasMap: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_ENUMS(type)
Q_PROPERTY(int start MEMBER m_start CONSTANT) Q_PROPERTY(int start MEMBER m_start CONSTANT)
Q_PROPERTY(int end MEMBER m_end CONSTANT) Q_PROPERTY(int end MEMBER m_end CONSTANT)
Q_PROPERTY(QString gas MEMBER m_gas CONSTANT) Q_PROPERTY(QString gas MEMBER m_gas CONSTANT)
Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT) Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT)
Q_PROPERTY(QString codeBlockType READ codeBlockType CONSTANT)
public: public:
GasMap(int _start, int _end, QString _gas, bool _isInfinite, QObject* _parent): QObject(_parent), m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite) {}
enum type
{
Statement,
Function,
Constructor
};
GasMap(int _start, int _end, QString _gas, bool _isInfinite, type _type, QObject* _parent): QObject(_parent), m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite), m_type(_type) {}
int m_start; int m_start;
int m_end; int m_end;
QString m_gas; QString m_gas;
bool m_isInfinite; bool m_isInfinite;
type m_type;
QString codeBlockType() const
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("type"));
if (m_type)
{
const char* key = units.valueToKey(m_type);
return QString(key).toLower();
}
return QString("");
}
};
class GasMapWrapper: public QObject
{
Q_OBJECT
Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT)
public:
GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){}
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type);
bool contains(QString _key);
void insert(QString _source, QVariantList _variantList);
QVariantList gasCostsByDocId(QString _source);
private:
GasCostsMaps m_gasMaps;
}; };
/// Code compilation model. Compiles contracts in background an provides compiled contract data /// Code compilation model. Compiles contracts in background an provides compiled contract data
@ -177,6 +199,7 @@ public:
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged) Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged)
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged) Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged)
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged) Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged)
Q_PROPERTY(bool optimizeCode MEMBER m_optimizeCode WRITE setOptimizeCode)
/// @returns latest compilation results for contracts /// @returns latest compilation results for contracts
QVariantMap contracts() const; QVariantMap contracts() const;
@ -209,6 +232,7 @@ public:
void gasEstimation(solidity::CompilerStack const& _cs); void gasEstimation(solidity::CompilerStack const& _cs);
/// Gas cost by doc id /// Gas cost by doc id
Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const; Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const;
Q_INVOKABLE void setOptimizeCode(bool _value);
signals: signals:
/// Emited on compilation state change /// Emited on compilation state change
@ -253,11 +277,10 @@ private:
std::map<QString, dev::bytes> m_compiledContracts; //by name std::map<QString, dev::bytes> m_compiledContracts; //by name
dev::Mutex x_pendingContracts; dev::Mutex x_pendingContracts;
std::map<QString, QString> m_pendingContracts; //name to source std::map<QString, QString> m_pendingContracts; //name to source
bool m_optimizeCode = false;
friend class BackgroundWorker; friend class BackgroundWorker;
}; };
} }
} }
//Q_DECLARE_METATYPE(dev::mix::GasMap)

17
mix/qml/Application.qml

@ -119,6 +119,7 @@ ApplicationWindow {
Menu { Menu {
title: qsTr("Tools") title: qsTr("Tools")
MenuItem { action: gasEstimationAction } MenuItem { action: gasEstimationAction }
MenuItem { action: optimizeCodeAction }
} }
Menu { Menu {
title: qsTr("Windows") title: qsTr("Windows")
@ -419,9 +420,19 @@ ApplicationWindow {
text: qsTr("Display gas estimation") text: qsTr("Display gas estimation")
shortcut: "Ctrl+G" shortcut: "Ctrl+G"
checkable: true checkable: true
onTriggered: onTriggered: mainContent.codeEditor.displayGasEstimation(checked);
{ }
mainContent.codeEditor.displayGasEstimation(checked);
Action {
id: optimizeCodeAction
text: qsTr("Enable optimized compilation")
shortcut: "Ctrl+Shift+O"
checkable: true
onTriggered: codeModel.setOptimizeCode(checked);
} }
Settings {
property alias gasEstimation: gasEstimationAction.checked
property alias optimizeCode: optimizeCodeAction.checked
} }
} }

8
mix/qml/html/cm/inkpot.css

@ -68,3 +68,11 @@ span.CodeMirror-selectedtext { color: #ffffff !important; }
font-size: 12px; font-size: 12px;
} }
.CodeMirror-gasCost
{
font-family: monospace;
font-size: 14px;
color: #409090;
text-shadow: none !important;
margin-left: 5px;
}

31
mix/qml/html/codeeditor.js

@ -210,9 +210,8 @@ setFontSize = function(size)
makeGasCostMarker = function(value) { makeGasCostMarker = function(value) {
var marker = document.createElement("div"); var marker = document.createElement("div");
marker.style.color = "#822";
marker.innerHTML = value; marker.innerHTML = value;
marker.className = "CodeMirror-errorannotation-context"; marker.className = "CodeMirror-gasCost";
return marker; return marker;
}; };
@ -252,8 +251,23 @@ displayGasEstimation = function(show)
else else
color = colorGradient[colorIndex]; color = colorGradient[colorIndex];
var className = "CodeMirror-gasCosts" + i; var className = "CodeMirror-gasCosts" + i;
var line = editor.posFromIndex(gasCosts[i].start) var line = editor.posFromIndex(gasCosts[i].start);
gasMarkText.push(editor.markText(line, editor.posFromIndex(gasCosts[i].end), { inclusiveLeft: true, inclusiveRight: true, handleMouseEvents: true, className: className, css: "background-color:" + color })); var endChar;
if (gasCosts[i].codeBlockType === "statement" || gasCosts[i].codeBlockType === "")
{
endChar = editor.posFromIndex(gasCosts[i].end);
gasMarkText.push({ line: line, markText: editor.markText(line, endChar, { inclusiveLeft: true, inclusiveRight: true, handleMouseEvents: true, className: className, css: "background-color:" + color })});
}
else if (gasCosts[i].codeBlockType === "function" || gasCosts[i].codeBlockType === "constructor")
{
var l = editor.getLine(line.line);
endChar = { line: line.line, ch: line.ch + l.length };
var marker = document.createElement("div");
marker.innerHTML = " max execution cost: " + gasCosts[i].gas + " gas";
marker.className = "CodeMirror-gasCost";
editor.addWidget(endChar, marker, false, "over");
gasMarkText.push({ line: line.line, widget: marker });
}
gasMarkRef[className] = { line: line.line, value: gasCosts[i] }; gasMarkRef[className] = { line: line.line, value: gasCosts[i] };
} }
} }
@ -275,7 +289,12 @@ function clearGasMark()
{ {
if (gasMarkText) if (gasMarkText)
for (var k in gasMarkText) for (var k in gasMarkText)
gasMarkText[k].clear(); {
if (gasMarkText[k] && gasMarkText[k].markText)
gasMarkText[k].markText.clear();
if (gasMarkText[k] && gasMarkText[k].widget)
gasMarkText[k].widget.remove();
}
} }
var gasAnnotation; var gasAnnotation;
@ -290,7 +309,7 @@ function listenMouseOver(e)
gasAnnotation.clear(); gasAnnotation.clear();
var cl = getGasCostClass(node); var cl = getGasCostClass(node);
var gasTitle = gasMarkRef[cl].value.isInfinite ? "infinite" : gasMarkRef[cl].value.gas; var gasTitle = gasMarkRef[cl].value.isInfinite ? "infinite" : gasMarkRef[cl].value.gas;
gasTitle = gasTitle + " gas"; gasTitle = " execution cost: " + gasTitle + " gas";
gasAnnotation = editor.addLineWidget(gasMarkRef[cl].line + 1, makeGasCostMarker(gasTitle), { coverGutter: false, above: true }); gasAnnotation = editor.addLineWidget(gasMarkRef[cl].line + 1, makeGasCostMarker(gasTitle), { coverGutter: false, above: true });
} }
else if (gasAnnotation) else if (gasAnnotation)

Loading…
Cancel
Save