diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 238fa5372..cad210f1b 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -331,7 +331,7 @@ void ClientModel::executeSequence(vector const& _sequence, { QSolidityType const* type = p->type(); 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 ctrParamInstance = resolvePair(value.toString()); value = QVariant(resolveToken(ctrParamInstance, deployedContracts)); diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 2b4e332c0..46fc572b3 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -309,7 +309,7 @@ void CodeModel::runCompilationJob(int _jobId) sourceNames.push_back(c.first.toStdString()); } } - cs.compile(false); + cs.compile(m_optimizeCode); gasEstimation(cs); collectContracts(cs, sourceNames); } @@ -380,7 +380,23 @@ void CodeModel::gasEstimation(solidity::CompilerStack const& _cs) GasMeter::GasConsumption cost = gasItem->second; std::stringstream v; 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(); } -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)); } diff --git a/mix/CodeModel.h b/mix/CodeModel.h index dcf3d0c1e..b9d06341d 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -130,39 +131,60 @@ struct SourceMap using SourceMaps = QMap; //by source id using GasCostsMaps = QMap; //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 { Q_OBJECT - + Q_ENUMS(type) Q_PROPERTY(int start MEMBER m_start CONSTANT) Q_PROPERTY(int end MEMBER m_end CONSTANT) Q_PROPERTY(QString gas MEMBER m_gas CONSTANT) Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT) + Q_PROPERTY(QString codeBlockType READ codeBlockType CONSTANT) 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_end; QString m_gas; 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 @@ -177,6 +199,7 @@ public: Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged) Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged) Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged) + Q_PROPERTY(bool optimizeCode MEMBER m_optimizeCode WRITE setOptimizeCode) /// @returns latest compilation results for contracts QVariantMap contracts() const; @@ -209,6 +232,7 @@ public: void gasEstimation(solidity::CompilerStack const& _cs); /// Gas cost by doc id Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const; + Q_INVOKABLE void setOptimizeCode(bool _value); signals: /// Emited on compilation state change @@ -253,11 +277,10 @@ private: std::map m_compiledContracts; //by name dev::Mutex x_pendingContracts; std::map m_pendingContracts; //name to source + bool m_optimizeCode = false; friend class BackgroundWorker; }; } } - -//Q_DECLARE_METATYPE(dev::mix::GasMap) diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index 161f7141a..d041f421e 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -119,6 +119,7 @@ ApplicationWindow { Menu { title: qsTr("Tools") MenuItem { action: gasEstimationAction } + MenuItem { action: optimizeCodeAction } } Menu { title: qsTr("Windows") @@ -419,9 +420,19 @@ ApplicationWindow { text: qsTr("Display gas estimation") shortcut: "Ctrl+G" checkable: true - onTriggered: - { - mainContent.codeEditor.displayGasEstimation(checked); - } + onTriggered: 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 } } diff --git a/mix/qml/html/cm/inkpot.css b/mix/qml/html/cm/inkpot.css index 6a2d8d63a..b31e20ad8 100644 --- a/mix/qml/html/cm/inkpot.css +++ b/mix/qml/html/cm/inkpot.css @@ -68,3 +68,11 @@ span.CodeMirror-selectedtext { color: #ffffff !important; } font-size: 12px; } +.CodeMirror-gasCost +{ + font-family: monospace; + font-size: 14px; + color: #409090; + text-shadow: none !important; + margin-left: 5px; +} diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 6af8ff131..108df5952 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -210,9 +210,8 @@ setFontSize = function(size) makeGasCostMarker = function(value) { var marker = document.createElement("div"); - marker.style.color = "#822"; marker.innerHTML = value; - marker.className = "CodeMirror-errorannotation-context"; + marker.className = "CodeMirror-gasCost"; return marker; }; @@ -252,8 +251,23 @@ displayGasEstimation = function(show) else color = colorGradient[colorIndex]; var className = "CodeMirror-gasCosts" + i; - 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 line = editor.posFromIndex(gasCosts[i].start); + 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] }; } } @@ -275,7 +289,12 @@ function clearGasMark() { if (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; @@ -290,7 +309,7 @@ function listenMouseOver(e) gasAnnotation.clear(); var cl = getGasCostClass(node); 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 }); } else if (gasAnnotation)