From 2d2efc070a6239a76e80e2d452f1f4fad4b20985 Mon Sep 17 00:00:00 2001 From: yann300 <yann.levreau@gmail.com> Date: Tue, 12 May 2015 17:52:03 +0200 Subject: [PATCH] Gas estimation --- mix/CodeModel.cpp | 49 +++++++++++++++++++++++++++++++++++ mix/CodeModel.h | 25 ++++++++++++++++++ mix/qml/CodeEditorView.qml | 4 +++ mix/qml/WebCodeEditor.qml | 8 ++++++ mix/qml/html/cm/solarized.css | 4 +++ mix/qml/html/codeeditor.js | 23 +++++++++++++++- 6 files changed, 112 insertions(+), 1 deletion(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 636a665e6..ecdb46be3 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -33,6 +33,8 @@ #include <libsolidity/CompilerStack.h> #include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/InterfaceHandler.h> +#include <libsolidity/StructuralGasEstimator.h> +#include <libsolidity/SourceReferenceFormatter.h> #include <libevmcore/Instruction.h> #include <libethcore/CommonJS.h> #include "QContractDefinition.h" @@ -41,6 +43,7 @@ #include "CodeHighlighter.h" #include "FileIo.h" #include "CodeModel.h" +#include "QBigInt.h" using namespace dev::mix; @@ -50,6 +53,7 @@ const std::set<std::string> c_predefinedContracts = namespace { +using namespace dev::eth; using namespace dev::solidity; class CollectLocalsVisitor: public ASTConstVisitor @@ -185,6 +189,7 @@ CodeModel::CodeModel(): qRegisterMetaType<QContractDefinition*>("QContractDefinition*"); qRegisterMetaType<QFunctionDefinition*>("QFunctionDefinition*"); qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*"); + //qRegisterMetaType<GasMap>("GasMap"); qmlRegisterType<QFunctionDefinition>("org.ethereum.qml", 1, 0, "QFunctionDefinition"); qmlRegisterType<QVariableDeclaration>("org.ethereum.qml", 1, 0, "QVariableDeclaration"); } @@ -292,6 +297,7 @@ void CodeModel::runCompilationJob(int _jobId) } } cs.compile(false); + gasEstimation(cs); collectContracts(cs, sourceNames); } catch (dev::Exception const& _exception) @@ -314,6 +320,49 @@ void CodeModel::runCompilationJob(int _jobId) emit stateChanged(); } +void CodeModel::gasEstimation(solidity::CompilerStack const& _cs) +{ + m_gasCostsMaps.clear(); + for (std::string n: _cs.getContractNames()) + { + + ContractDefinition const& contractDefinition = _cs.getContractDefinition(n); + QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName); + + if (!m_gasCostsMaps.contains(sourceName)) + m_gasCostsMaps.insert(sourceName, QVariantList()); + + if (!contractDefinition.isFullyImplemented()) + continue; + dev::solidity::SourceUnit const& sourceUnit = _cs.getAST(*contractDefinition.getLocation().sourceName); + AssemblyItems const* items = _cs.getRuntimeAssemblyItems(n); + + StructuralGasEstimator estimator; + std::map<ASTNode const*, GasMeter::GasConsumption> gasCosts = estimator.breakToStatementLevel(estimator.performEstimation(*items, std::vector<ASTNode const*>({&sourceUnit})), {&sourceUnit}); + + for (auto gasIte = gasCosts.begin(); gasIte != gasCosts.end(); ++gasIte) + { + SourceLocation location = gasIte->first->getLocation(); + GasMeter::GasConsumption cost = gasIte->second; + GasMap* gas = new GasMap(location.start, location.end, (new QBigInt(cost.value))->value()); + m_gasCostsMaps.find(sourceName).value().push_back(QVariant::fromValue(gas)); + } + } +} + +QVariantList CodeModel::gasCostByDocumentId(QString const& _documentId) const +{ + if (m_gasCostsMaps.contains(_documentId)) + { + auto sourceMapIter = m_gasCostsMaps.find(_documentId); + int gg = sourceMapIter.value().size(); + Q_UNUSED(gg); + return sourceMapIter.value(); + } + else + return QVariantList(); +} + void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames) { Guard pl(x_pendingContracts); diff --git a/mix/CodeModel.h b/mix/CodeModel.h index 3f713a17b..a1a2c29ec 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -32,6 +32,7 @@ #include <libdevcore/Guards.h> #include <libevmasm/Assembly.h> #include "SolidityType.h" +#include "QBigInt.h" class QTextDocument; @@ -126,7 +127,24 @@ struct SourceMap LocationMap functions; }; +class GasMap: public QObject +{ + Q_OBJECT + + Q_PROPERTY(int start MEMBER m_start CONSTANT) + Q_PROPERTY(int end MEMBER m_end CONSTANT) + Q_PROPERTY(QString gas MEMBER m_gas CONSTANT) + +public: + GasMap(int _start, int _end, QString _gas): m_start(_start), m_end(_end), m_gas(_gas) {} + + int m_start; + int m_end; + QString m_gas; +}; + using SourceMaps = QMap<QString, SourceMap>; //by source id +using GasCostsMaps = QMap<QString, QVariantList/* QList<GasMap>*/>; //gas cost by contract name /// Code compilation model. Compiles contracts in background an provides compiled contract data class CodeModel: public QObject @@ -166,6 +184,10 @@ public: bool isContractOrFunctionLocation(dev::SourceLocation const& _location); /// Get funciton name by location QString resolveFunctionName(dev::SourceLocation const& _location); + /// Gas estimation for compiled sources + void gasEstimation(solidity::CompilerStack const& _cs); + /// Gas cost by doc id + Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const; signals: /// Emited on compilation state change @@ -201,6 +223,7 @@ private: mutable dev::Mutex x_contractMap; ContractMap m_contractMap; SourceMaps m_sourceMaps; + GasCostsMaps m_gasCostsMaps; std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings; QThread m_backgroundThread; BackgroundWorker m_backgroundWorker; @@ -214,3 +237,5 @@ private: } } + +//Q_DECLARE_METATYPE(dev::mix::GasMap) diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index e4d62ed81..08bb072f5 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -177,6 +177,10 @@ Item { } onCompilationComplete: { sourceInError = ""; + var gasCosts = codeModel.gasCostByDocumentId(currentDocumentId); + var editor = getEditor(currentDocumentId); + if (editor) + editor.displayGasCosts(gasCosts); } } diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 38f2327b1..a2602304a 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -83,6 +83,14 @@ Item { editorBrowser.runJavaScript("setFontSize(" + size + ")", function(result) {}); } + function displayGasCosts(gasCosts) { + + //console.log(gasCosts); + //console.log(JSON.stringify(gasCosts)); + if (initialized && editorBrowser) + editorBrowser.runJavaScript("displayGasCosts('" + JSON.stringify(gasCosts) + "')", function(result) {}); + } + Clipboard { id: clipboard diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index b8cede806..046042450 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -189,3 +189,7 @@ view-port } span.CodeMirror-selectedtext { color: #586e75 !important; } + +.gasCost { + background: #b58900 !important; +} diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index d25fbd091..cc5e9a28c 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -3,7 +3,7 @@ var editor = CodeMirror(document.body, { //styleActiveLine: true, matchBrackets: true, autofocus: true, - gutters: ["CodeMirror-linenumbers", "breakpoints"], + gutters: ["CodeMirror-linenumbers", "breakpoints", "gasCost"], autoCloseBrackets: true, styleSelectedText: true }); @@ -203,5 +203,26 @@ setFontSize = function(size) editor.refresh(); } +makeGasCostMarker = function(value) { + var marker = document.createElement("div"); + marker.style.color = "#822"; + marker.innerHTML = value; + return marker; +}; + +displayGasCosts = function(gasCosts) +{ + gasCosts = JSON.parse(gasCosts); + for (var i in gasCosts) + { + var line = editor.posFromIndex(gasCosts[i].start); + console.log("___________") + console.log(line.line); + console.log(gasCosts[i].start); + //editor.setGutterMarker(line.line, "gasCost", makeGasCostMarker(gasCosts[i].gas)); + editor.markText(editor.posFromIndex(gasCosts[i].start), editor.posFromIndex(gasCosts[i].end), { className: "gasCost" }); + } +} + editor.setOption("extraKeys", extraKeys);