Gav Wood
10 years ago
46 changed files with 1531 additions and 1168 deletions
@ -0,0 +1,170 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
cpp-ethereum is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file CodeHighlighter.cpp
|
||||
|
* @author Arkadiy Paronyan arkadiy@ethdev.com |
||||
|
* @date 2015 |
||||
|
* Ethereum IDE client. |
||||
|
*/ |
||||
|
|
||||
|
#include <algorithm> |
||||
|
#include <QRegularExpression> |
||||
|
#include <QTextDocument> |
||||
|
#include <QTextBlock> |
||||
|
#include <QTextLayout> |
||||
|
#include <libsolidity/ASTVisitor.h> |
||||
|
#include <libsolidity/AST.h> |
||||
|
#include <libsolidity/Scanner.h> |
||||
|
#include <libsolidity/Exceptions.h> |
||||
|
#include "CodeHighlighter.h" |
||||
|
|
||||
|
using namespace dev::mix; |
||||
|
|
||||
|
CodeHighlighterSettings::CodeHighlighterSettings() |
||||
|
{ |
||||
|
backgroundColor = QColor(0x00, 0x2b, 0x36); |
||||
|
foregroundColor = QColor(0xee, 0xe8, 0xd5); |
||||
|
formats[Keyword].setForeground(QColor(0x93, 0xa1, 0xa1)); |
||||
|
formats[Comment].setForeground(QColor(0x85, 0x99, 0x00)); |
||||
|
formats[StringLiteral].setForeground(QColor(0xdc, 0x32, 0x2f)); |
||||
|
formats[NumLiteral].setForeground(foregroundColor); |
||||
|
formats[Import].setForeground(QColor(0x6c, 0x71, 0xc4)); |
||||
|
formats[CompilationError].setUnderlineColor(Qt::red); |
||||
|
formats[CompilationError].setUnderlineStyle(QTextCharFormat::SingleUnderline); |
||||
|
} |
||||
|
|
||||
|
namespace |
||||
|
{ |
||||
|
using namespace dev::solidity; |
||||
|
class HighlightVisitor: public ASTConstVisitor |
||||
|
{ |
||||
|
public: |
||||
|
HighlightVisitor(CodeHighlighter::Formats* _formats) { m_formats = _formats; } |
||||
|
private: |
||||
|
CodeHighlighter::Formats* m_formats; |
||||
|
|
||||
|
virtual bool visit(ImportDirective const& _node) |
||||
|
{ |
||||
|
m_formats->push_back(CodeHighlighter::FormatRange(CodeHighlighterSettings::Import, _node.getLocation())); |
||||
|
return true; |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
CodeHighlighter::FormatRange::FormatRange(CodeHighlighterSettings::Token _t, solidity::Location const& _location): |
||||
|
token(_t), start(_location.start), length(_location.end - _location.start) |
||||
|
{} |
||||
|
|
||||
|
void CodeHighlighter::processSource(std::string const& _source) |
||||
|
{ |
||||
|
processComments(_source); |
||||
|
solidity::CharStream stream(_source); |
||||
|
solidity::Scanner scanner(stream); |
||||
|
solidity::Token::Value token = scanner.getCurrentToken(); |
||||
|
while (token != Token::EOS) |
||||
|
{ |
||||
|
if ((token >= Token::BREAK && token < Token::TYPES_END) || |
||||
|
token == Token::IN || token == Token::DELETE || token == Token::NULL_LITERAL || token == Token::TRUE_LITERAL || token == Token::FALSE_LITERAL) |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::Keyword, scanner.getCurrentLocation())); |
||||
|
else if (token == Token::STRING_LITERAL) |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::StringLiteral, scanner.getCurrentLocation())); |
||||
|
else if (token == Token::COMMENT_LITERAL) |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, scanner.getCurrentLocation())); |
||||
|
else if (token == Token::NUMBER) |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::NumLiteral, scanner.getCurrentLocation())); |
||||
|
|
||||
|
token = scanner.next(); |
||||
|
} |
||||
|
std::sort(m_formats.begin(), m_formats.end()); |
||||
|
} |
||||
|
|
||||
|
void CodeHighlighter::processAST(solidity::ASTNode const& _ast) |
||||
|
{ |
||||
|
HighlightVisitor visitor(&m_formats); |
||||
|
_ast.accept(visitor); |
||||
|
|
||||
|
std::sort(m_formats.begin(), m_formats.end()); |
||||
|
} |
||||
|
|
||||
|
void CodeHighlighter::processError(dev::Exception const& _exception) |
||||
|
{ |
||||
|
Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::CompilationError, *location)); |
||||
|
} |
||||
|
|
||||
|
void CodeHighlighter::processComments(std::string const& _source) |
||||
|
{ |
||||
|
unsigned i = 0; |
||||
|
unsigned size = _source.size(); |
||||
|
if (size == 0) |
||||
|
return; |
||||
|
while (i < size - 1) |
||||
|
{ |
||||
|
if (_source[i] == '/' && _source[i + 1] == '/') |
||||
|
{ |
||||
|
//add single line comment
|
||||
|
int start = i; |
||||
|
i += 2; |
||||
|
while (_source[i] != '\n' && i < size) |
||||
|
++i; |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, start, i - start)); |
||||
|
} |
||||
|
else if (_source[i] == '/' && _source[i + 1] == '*') |
||||
|
{ |
||||
|
//add multiline comment
|
||||
|
int start = i; |
||||
|
i += 2; |
||||
|
while ((_source[i] != '/' || _source[i - 1] != '*') && i < size) |
||||
|
++i; |
||||
|
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, start, i - start + 1)); |
||||
|
} |
||||
|
++i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void CodeHighlighter::updateFormatting(QTextDocument* _document, CodeHighlighterSettings const& _settings) |
||||
|
{ |
||||
|
QTextBlock block = _document->firstBlock(); |
||||
|
QList<QTextLayout::FormatRange> ranges; |
||||
|
|
||||
|
Formats::const_iterator format = m_formats.begin(); |
||||
|
while (true) |
||||
|
{ |
||||
|
while ((format == m_formats.end() || (block.position() + block.length() <= format->start)) && block.isValid()) |
||||
|
{ |
||||
|
auto layout = block.layout(); |
||||
|
layout->clearAdditionalFormats(); |
||||
|
layout->setAdditionalFormats(ranges); |
||||
|
_document->markContentsDirty(block.position(), block.length()); |
||||
|
block = block.next(); |
||||
|
ranges.clear(); |
||||
|
} |
||||
|
if (!block.isValid()) |
||||
|
break; |
||||
|
|
||||
|
int intersectionStart = std::max(format->start, block.position()); |
||||
|
int intersectionLength = std::min(format->start + format->length, block.position() + block.length()) - intersectionStart; |
||||
|
if (intersectionLength > 0) |
||||
|
{ |
||||
|
QTextLayout::FormatRange range; |
||||
|
range.format = _settings.formats[format->token]; |
||||
|
range.start = format->start - block.position(); |
||||
|
range.length = format->length; |
||||
|
ranges.append(range); |
||||
|
} |
||||
|
++format; |
||||
|
} |
||||
|
} |
@ -0,0 +1,109 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
cpp-ethereum is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file CodeHighlighter.h
|
||||
|
* @author Arkadiy Paronyan arkadiy@ethdev.com |
||||
|
* @date 2015 |
||||
|
* Ethereum IDE client. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <vector> |
||||
|
#include <QString> |
||||
|
#include <QTextCharFormat> |
||||
|
|
||||
|
class QTextDocument; |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
|
||||
|
struct Exception; |
||||
|
|
||||
|
namespace solidity |
||||
|
{ |
||||
|
class ASTNode; |
||||
|
struct Location; |
||||
|
} |
||||
|
|
||||
|
namespace mix |
||||
|
{ |
||||
|
|
||||
|
/// Code highligting settings
|
||||
|
class CodeHighlighterSettings |
||||
|
{ |
||||
|
public: |
||||
|
enum Token |
||||
|
{ |
||||
|
Import, |
||||
|
Keyword, |
||||
|
Comment, |
||||
|
StringLiteral, |
||||
|
NumLiteral, |
||||
|
CompilationError, |
||||
|
Size, //this must be kept last
|
||||
|
}; |
||||
|
|
||||
|
CodeHighlighterSettings(); |
||||
|
///Format for each token
|
||||
|
QTextCharFormat formats[Size]; |
||||
|
///Background color
|
||||
|
QColor backgroundColor; |
||||
|
///Foreground color
|
||||
|
QColor foregroundColor; |
||||
|
}; |
||||
|
|
||||
|
/// Code highlighting engine class
|
||||
|
class CodeHighlighter |
||||
|
{ |
||||
|
public: |
||||
|
/// Formatting range
|
||||
|
struct FormatRange |
||||
|
{ |
||||
|
FormatRange(CodeHighlighterSettings::Token _t, int _start, int _length): token(_t), start(_start), length(_length) {} |
||||
|
FormatRange(CodeHighlighterSettings::Token _t, solidity::Location const& _location); |
||||
|
bool operator<(FormatRange const& _other) const { return start < _other.start || (start == _other.start && length < _other.length); } |
||||
|
|
||||
|
CodeHighlighterSettings::Token token; |
||||
|
int start; |
||||
|
int length; |
||||
|
}; |
||||
|
typedef std::vector<FormatRange> Formats; // Sorted by start position
|
||||
|
|
||||
|
public: |
||||
|
/// Collect highligting information by lexing the source
|
||||
|
void processSource(std::string const& _source); |
||||
|
/// Collect additional highligting information from AST
|
||||
|
void processAST(solidity::ASTNode const& _ast); |
||||
|
/// Collect highlighting information from compilation exception
|
||||
|
void processError(dev::Exception const& _exception); |
||||
|
|
||||
|
/// Apply formatting for a text document
|
||||
|
/// @todo Remove this once editor is reworked
|
||||
|
void updateFormatting(QTextDocument* _document, CodeHighlighterSettings const& _settings); |
||||
|
|
||||
|
private: |
||||
|
/// Collect highligting information by paring for comments
|
||||
|
/// @todo Support this in solidity?
|
||||
|
void processComments(std::string const& _source); |
||||
|
|
||||
|
private: |
||||
|
Formats m_formats; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,177 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
cpp-ethereum is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file CodeModel.cpp
|
||||
|
* @author Arkadiy Paronyan arkadiy@ethdev.com |
||||
|
* @date 2014 |
||||
|
* Ethereum IDE client. |
||||
|
*/ |
||||
|
|
||||
|
#include <sstream> |
||||
|
#include <QDebug> |
||||
|
#include <QApplication> |
||||
|
#include <QtQml> |
||||
|
#include <libsolidity/CompilerStack.h> |
||||
|
#include <libsolidity/SourceReferenceFormatter.h> |
||||
|
#include <libevmcore/Instruction.h> |
||||
|
#include "QContractDefinition.h" |
||||
|
#include "QFunctionDefinition.h" |
||||
|
#include "QVariableDeclaration.h" |
||||
|
#include "CodeHighlighter.h" |
||||
|
#include "CodeModel.h" |
||||
|
|
||||
|
using namespace dev::mix; |
||||
|
|
||||
|
void BackgroundWorker::queueCodeChange(int _jobId, QString const& _content) |
||||
|
{ |
||||
|
m_model->runCompilationJob(_jobId, _content); |
||||
|
} |
||||
|
|
||||
|
CompilationResult::CompilationResult(): |
||||
|
QObject(nullptr), |
||||
|
m_successful(false), |
||||
|
m_codeHash(qHash(QString())), |
||||
|
m_contract(new QContractDefinition()), |
||||
|
m_codeHighlighter(new CodeHighlighter()) |
||||
|
{} |
||||
|
|
||||
|
CompilationResult::CompilationResult(const solidity::CompilerStack& _compiler): |
||||
|
QObject(nullptr), |
||||
|
m_successful(true), |
||||
|
m_codeHash(qHash(QString())) |
||||
|
{ |
||||
|
if (!_compiler.getContractNames().empty()) |
||||
|
{ |
||||
|
m_contract.reset(new QContractDefinition(&_compiler.getContractDefinition(std::string()))); |
||||
|
m_bytes = _compiler.getBytecode(); |
||||
|
m_assemblyCode = QString::fromStdString(dev::eth::disassemble(m_bytes)); |
||||
|
} |
||||
|
else |
||||
|
m_contract.reset(new QContractDefinition()); |
||||
|
} |
||||
|
|
||||
|
CompilationResult::CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage): |
||||
|
QObject(nullptr), |
||||
|
m_successful(false), |
||||
|
m_codeHash(qHash(QString())), |
||||
|
m_contract(_prev.m_contract), |
||||
|
m_compilerMessage(_compilerMessage), |
||||
|
m_bytes(_prev.m_bytes), |
||||
|
m_assemblyCode(_prev.m_assemblyCode), |
||||
|
m_codeHighlighter(_prev.m_codeHighlighter) |
||||
|
{} |
||||
|
|
||||
|
CodeModel::CodeModel(QObject* _parent): |
||||
|
QObject(_parent), |
||||
|
m_compiling(false), |
||||
|
m_result(new CompilationResult()), |
||||
|
m_codeHighlighterSettings(new CodeHighlighterSettings()), |
||||
|
m_backgroundWorker(this), |
||||
|
m_backgroundJobId(0) |
||||
|
{ |
||||
|
m_backgroundWorker.moveToThread(&m_backgroundThread); |
||||
|
connect(this, &CodeModel::scheduleCompilationJob, &m_backgroundWorker, &BackgroundWorker::queueCodeChange, Qt::QueuedConnection); |
||||
|
connect(this, &CodeModel::compilationCompleteInternal, this, &CodeModel::onCompilationComplete, Qt::QueuedConnection); |
||||
|
qRegisterMetaType<CompilationResult*>("CompilationResult*"); |
||||
|
qRegisterMetaType<QContractDefinition*>("QContractDefinition*"); |
||||
|
qRegisterMetaType<QFunctionDefinition*>("QFunctionDefinition*"); |
||||
|
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*"); |
||||
|
qmlRegisterType<QFunctionDefinition>("org.ethereum.qml", 1, 0, "QFunctionDefinition"); |
||||
|
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml", 1, 0, "QVariableDeclaration"); |
||||
|
m_backgroundThread.start(); |
||||
|
} |
||||
|
|
||||
|
CodeModel::~CodeModel() |
||||
|
{ |
||||
|
stop(); |
||||
|
disconnect(this); |
||||
|
} |
||||
|
|
||||
|
void CodeModel::stop() |
||||
|
{ |
||||
|
///@todo: cancel bg job
|
||||
|
m_backgroundThread.exit(); |
||||
|
m_backgroundThread.wait(); |
||||
|
} |
||||
|
|
||||
|
void CodeModel::registerCodeChange(QString const& _code) |
||||
|
{ |
||||
|
// launch the background thread
|
||||
|
uint hash = qHash(_code); |
||||
|
if (m_result->m_codeHash == hash) |
||||
|
return; |
||||
|
m_backgroundJobId++; |
||||
|
m_compiling = true; |
||||
|
emit stateChanged(); |
||||
|
emit scheduleCompilationJob(m_backgroundJobId, _code); |
||||
|
} |
||||
|
|
||||
|
void CodeModel::runCompilationJob(int _jobId, QString const& _code) |
||||
|
{ |
||||
|
if (_jobId != m_backgroundJobId) |
||||
|
return; //obsolete job
|
||||
|
|
||||
|
solidity::CompilerStack cs; |
||||
|
std::unique_ptr<CompilationResult> result; |
||||
|
|
||||
|
std::string source = _code.toStdString(); |
||||
|
// run syntax highlighting first
|
||||
|
// @todo combine this with compilation step
|
||||
|
auto codeHighlighter = std::make_shared<CodeHighlighter>(); |
||||
|
codeHighlighter->processSource(source); |
||||
|
|
||||
|
// run compilation
|
||||
|
try |
||||
|
{ |
||||
|
cs.setSource(source); |
||||
|
cs.compile(false); |
||||
|
codeHighlighter->processAST(cs.getAST()); |
||||
|
result.reset(new CompilationResult(cs)); |
||||
|
qDebug() << QString(QApplication::tr("compilation succeeded")); |
||||
|
} |
||||
|
catch (dev::Exception const& _exception) |
||||
|
{ |
||||
|
std::ostringstream error; |
||||
|
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs); |
||||
|
result.reset(new CompilationResult(*m_result, QString::fromStdString(error.str()))); |
||||
|
codeHighlighter->processError(_exception); |
||||
|
qDebug() << QString(QApplication::tr("compilation failed:") + " " + result->compilerMessage()); |
||||
|
} |
||||
|
result->m_codeHighlighter = codeHighlighter; |
||||
|
result->m_codeHash = qHash(_code); |
||||
|
|
||||
|
emit compilationCompleteInternal(result.release()); |
||||
|
} |
||||
|
|
||||
|
void CodeModel::onCompilationComplete(CompilationResult*_newResult) |
||||
|
{ |
||||
|
m_compiling = false; |
||||
|
m_result.reset(_newResult); |
||||
|
emit compilationComplete(); |
||||
|
emit stateChanged(); |
||||
|
if (m_result->successfull()) |
||||
|
emit codeChanged(); |
||||
|
} |
||||
|
|
||||
|
bool CodeModel::hasContract() const |
||||
|
{ |
||||
|
return m_result->contract()->functionsList().size() > 0; |
||||
|
} |
||||
|
|
||||
|
void CodeModel::updateFormatting(QTextDocument* _document) |
||||
|
{ |
||||
|
m_result->codeHighlighter()->updateFormatting(_document, *m_codeHighlighterSettings); |
||||
|
} |
@ -0,0 +1,163 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
cpp-ethereum is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file CodeModel.h
|
||||
|
* @author Arkadiy Paronyan arkadiy@ethdev.com |
||||
|
* @date 2014 |
||||
|
* Ethereum IDE client. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <atomic> |
||||
|
#include <QObject> |
||||
|
#include <QThread> |
||||
|
#include <libdevcore/Common.h> |
||||
|
|
||||
|
class QTextDocument; |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
|
||||
|
namespace solidity |
||||
|
{ |
||||
|
class CompilerStack; |
||||
|
} |
||||
|
|
||||
|
namespace mix |
||||
|
{ |
||||
|
|
||||
|
class CodeModel; |
||||
|
class CodeHighlighter; |
||||
|
class CodeHighlighterSettings; |
||||
|
class QContractDefinition; |
||||
|
|
||||
|
//utility class to perform tasks in background thread
|
||||
|
class BackgroundWorker: public QObject |
||||
|
{ |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
BackgroundWorker(CodeModel* _model): QObject(), m_model(_model) {} |
||||
|
|
||||
|
public slots: |
||||
|
void queueCodeChange(int _jobId, QString const& _content); |
||||
|
private: |
||||
|
CodeModel* m_model; |
||||
|
}; |
||||
|
|
||||
|
///Compilation result model. Contains all the compiled contract data required by UI
|
||||
|
class CompilationResult: public QObject |
||||
|
{ |
||||
|
Q_OBJECT |
||||
|
Q_PROPERTY(QContractDefinition* contract READ contract) |
||||
|
|
||||
|
public: |
||||
|
/// Empty compilation result constructor
|
||||
|
CompilationResult(); |
||||
|
/// Successfull compilation result constructor
|
||||
|
CompilationResult(solidity::CompilerStack const& _compiler); |
||||
|
/// Failed compilation result constructor
|
||||
|
CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage); |
||||
|
|
||||
|
/// @returns contract definition for QML property
|
||||
|
QContractDefinition* contract() { return m_contract.get(); } |
||||
|
/// @returns contract definition
|
||||
|
std::shared_ptr<QContractDefinition> sharedContract() { return m_contract; } |
||||
|
/// Indicates if the compilation was successfull
|
||||
|
bool successfull() const { return m_successful; } |
||||
|
/// @returns compiler error message in case of unsuccessfull compilation
|
||||
|
QString compilerMessage() const { return m_compilerMessage; } |
||||
|
/// @returns contract bytecode
|
||||
|
dev::bytes const& bytes() const { return m_bytes; } |
||||
|
/// @returns contract bytecode in human-readable form
|
||||
|
QString assemblyCode() const { return m_assemblyCode; } |
||||
|
/// Get code highlighter
|
||||
|
std::shared_ptr<CodeHighlighter> codeHighlighter() { return m_codeHighlighter; } |
||||
|
|
||||
|
private: |
||||
|
bool m_successful; |
||||
|
uint m_codeHash; |
||||
|
std::shared_ptr<QContractDefinition> m_contract; |
||||
|
QString m_compilerMessage; ///< @todo: use some structure here
|
||||
|
dev::bytes m_bytes; |
||||
|
QString m_assemblyCode; |
||||
|
std::shared_ptr<CodeHighlighter> m_codeHighlighter; |
||||
|
|
||||
|
friend class CodeModel; |
||||
|
}; |
||||
|
|
||||
|
/// Background code compiler
|
||||
|
class CodeModel: public QObject |
||||
|
{ |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
CodeModel(QObject* _parent); |
||||
|
~CodeModel(); |
||||
|
|
||||
|
/// @returns latest compilation result
|
||||
|
CompilationResult* code() { return m_result.get(); } |
||||
|
/// @returns latest compilation resul
|
||||
|
CompilationResult const* code() const { return m_result.get(); } |
||||
|
|
||||
|
Q_PROPERTY(CompilationResult* code READ code NOTIFY codeChanged) |
||||
|
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged) |
||||
|
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged) |
||||
|
|
||||
|
/// @returns compilation status
|
||||
|
bool isCompiling() const { return m_compiling; } |
||||
|
/// @returns true if contract has at least one function
|
||||
|
bool hasContract() const; |
||||
|
/// Apply text document formatting. @todo Move this to editor module
|
||||
|
void updateFormatting(QTextDocument* _document); |
||||
|
|
||||
|
signals: |
||||
|
/// Emited on compilation state change
|
||||
|
void stateChanged(); |
||||
|
/// Emitted on compilation complete
|
||||
|
void compilationComplete(); |
||||
|
/// Internal signal used to transfer compilation job to background thread
|
||||
|
void scheduleCompilationJob(int _jobId, QString const& _content); |
||||
|
/// Emitted if there are any changes in the code model
|
||||
|
void codeChanged(); |
||||
|
/// Emitted on compilation complete. Internal
|
||||
|
void compilationCompleteInternal(CompilationResult* _newResult); |
||||
|
|
||||
|
private slots: |
||||
|
void onCompilationComplete(CompilationResult* _newResult); |
||||
|
|
||||
|
public slots: |
||||
|
/// Update code model on source code change
|
||||
|
void registerCodeChange(QString const& _code); |
||||
|
|
||||
|
private: |
||||
|
void runCompilationJob(int _jobId, QString const& _content); |
||||
|
void stop(); |
||||
|
|
||||
|
std::atomic<bool> m_compiling; |
||||
|
std::unique_ptr<CompilationResult> m_result; |
||||
|
std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings; |
||||
|
QThread m_backgroundThread; |
||||
|
BackgroundWorker m_backgroundWorker; |
||||
|
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
|
||||
|
friend class BackgroundWorker; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
@ -1,66 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file ConstantCompilationModel.cpp
|
|
||||
* @author Yann yann@ethdev.com |
|
||||
* @date 2014 |
|
||||
* Ethereum IDE client. |
|
||||
*/ |
|
||||
|
|
||||
#include <QApplication> |
|
||||
#include <QObject> |
|
||||
#include <libevm/VM.h> |
|
||||
#include <libsolidity/Scanner.h> |
|
||||
#include <libsolidity/Parser.h> |
|
||||
#include <libsolidity/CompilerStack.h> |
|
||||
#include <libsolidity/SourceReferenceFormatter.h> |
|
||||
#include <libsolidity/NameAndTypeResolver.h> |
|
||||
#include "ConstantCompilationModel.h" |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
using namespace dev::eth; |
|
||||
using namespace dev::mix; |
|
||||
using namespace dev::solidity; |
|
||||
|
|
||||
CompilerResult ConstantCompilationModel::compile(QString _code) |
|
||||
{ |
|
||||
dev::solidity::CompilerStack compiler; |
|
||||
dev::bytes m_data; |
|
||||
CompilerResult res; |
|
||||
try |
|
||||
{ |
|
||||
m_data = compiler.compile(_code.toStdString(), true); |
|
||||
res.success = true; |
|
||||
res.comment = "ok"; |
|
||||
res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data)); |
|
||||
res.bytes = m_data; |
|
||||
} |
|
||||
catch (dev::Exception const& _exception) |
|
||||
{ |
|
||||
ostringstream error; |
|
||||
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler); |
|
||||
res.success = false; |
|
||||
res.comment = QString::fromStdString(error.str()); |
|
||||
res.hexCode = ""; |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
res.success = false; |
|
||||
res.comment = QApplication::tr("Uncaught exception."); |
|
||||
res.hexCode = ""; |
|
||||
} |
|
||||
return res; |
|
||||
} |
|
@ -1,60 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file ConstantCompilationModel.h
|
|
||||
* @author Yann yann@ethdev.com |
|
||||
* @date 2014 |
|
||||
* Ethereum IDE client. |
|
||||
*/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <QObject> |
|
||||
#include <libevm/VM.h> |
|
||||
#include <libsolidity/AST.h> |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace mix |
|
||||
{ |
|
||||
|
|
||||
/**
|
|
||||
* @brief Provides compiler result information. |
|
||||
*/ |
|
||||
struct CompilerResult |
|
||||
{ |
|
||||
QString hexCode; |
|
||||
QString comment; |
|
||||
dev::bytes bytes; |
|
||||
bool success; |
|
||||
}; |
|
||||
|
|
||||
/**
|
|
||||
* @brief Compile source code using the solidity library. |
|
||||
*/ |
|
||||
class ConstantCompilationModel |
|
||||
{ |
|
||||
|
|
||||
public: |
|
||||
ConstantCompilationModel() {} |
|
||||
~ConstantCompilationModel() {} |
|
||||
/// Compile code.
|
|
||||
CompilerResult compile(QString _code); |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,41 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file KeyEventManager.cpp
|
|
||||
* @author Yann yann@ethdev.com |
|
||||
* @date 2014 |
|
||||
* Used as an event handler for all classes which need keyboard interactions. |
|
||||
* Can be improve by adding the possibility to register to a specific key. |
|
||||
*/ |
|
||||
|
|
||||
#include <QDebug> |
|
||||
#include <QKeySequence> |
|
||||
#include "KeyEventManager.h" |
|
||||
|
|
||||
void KeyEventManager::registerEvent(const QObject* _receiver, const char* _slot) |
|
||||
{ |
|
||||
QObject::connect(this, SIGNAL(onKeyPressed(int)), _receiver, _slot); |
|
||||
} |
|
||||
|
|
||||
void KeyEventManager::unRegisterEvent(QObject* _receiver) |
|
||||
{ |
|
||||
QObject::disconnect(_receiver); |
|
||||
} |
|
||||
|
|
||||
void KeyEventManager::keyPressed(QVariant _event) |
|
||||
{ |
|
||||
emit onKeyPressed(_event.toInt()); |
|
||||
} |
|
@ -1,47 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file KeyEventManager.h
|
|
||||
* @author Yann yann@ethdev.com |
|
||||
* @date 2014 |
|
||||
* Used as an event handler for all classes which need keyboard interactions |
|
||||
*/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <QObject> |
|
||||
#include <QVariant> |
|
||||
|
|
||||
class KeyEventManager: public QObject |
|
||||
{ |
|
||||
Q_OBJECT |
|
||||
|
|
||||
public: |
|
||||
KeyEventManager() {} |
|
||||
/// Allows _receiver to handle key pressed event.
|
|
||||
void registerEvent(const QObject* _receiver, const char* _slot); |
|
||||
/// Unregister _receiver.
|
|
||||
void unRegisterEvent(QObject* _receiver); |
|
||||
|
|
||||
signals: |
|
||||
/// Emited when a key is pressed.
|
|
||||
void onKeyPressed(int _event); |
|
||||
|
|
||||
public slots: |
|
||||
/// Called when a key is pressed.
|
|
||||
void keyPressed(QVariant _event); |
|
||||
}; |
|
||||
|
|
@ -1,221 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file TransactionListModel.cpp
|
|
||||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|
||||
* @date 2014 |
|
||||
* Ethereum IDE client. |
|
||||
*/ |
|
||||
|
|
||||
#include <QObject> |
|
||||
#include <QQmlEngine> |
|
||||
#include <QTextDocument> |
|
||||
#include <QAbstractListModel> |
|
||||
#include <libdevcore/CommonJS.h> |
|
||||
#include "TransactionListModel.h" |
|
||||
#include "QContractDefinition.h" |
|
||||
#include "QFunctionDefinition.h" |
|
||||
#include "QVariableDeclaration.h" |
|
||||
|
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace mix |
|
||||
{ |
|
||||
|
|
||||
/// @todo Move this to QML
|
|
||||
u256 fromQString(QString const& _s) |
|
||||
{ |
|
||||
return dev::jsToU256(_s.toStdString()); |
|
||||
} |
|
||||
|
|
||||
/// @todo Move this to QML
|
|
||||
QString toQString(u256 _value) |
|
||||
{ |
|
||||
std::ostringstream s; |
|
||||
s << _value; |
|
||||
return QString::fromStdString(s.str()); |
|
||||
} |
|
||||
|
|
||||
TransactionListItem::TransactionListItem(int _index, TransactionSettings const& _t, QObject* _parent): |
|
||||
QObject(_parent), m_index(_index), m_title(_t.title), m_functionId(_t.functionId), m_value(toQString(_t.value)), |
|
||||
m_gas(toQString(_t.gas)), m_gasPrice(toQString(_t.gasPrice)) |
|
||||
{} |
|
||||
|
|
||||
TransactionListModel::TransactionListModel(QObject* _parent, QTextDocument* _document): |
|
||||
QAbstractListModel(_parent), m_document(_document) |
|
||||
{ |
|
||||
qRegisterMetaType<TransactionListItem*>("TransactionListItem*"); |
|
||||
} |
|
||||
|
|
||||
QHash<int, QByteArray> TransactionListModel::roleNames() const |
|
||||
{ |
|
||||
QHash<int, QByteArray> roles; |
|
||||
roles[TitleRole] = "title"; |
|
||||
roles[IdRole] = "transactionIndex"; |
|
||||
return roles; |
|
||||
} |
|
||||
|
|
||||
int TransactionListModel::rowCount(QModelIndex const& _parent) const |
|
||||
{ |
|
||||
Q_UNUSED(_parent); |
|
||||
return m_transactions.size(); |
|
||||
} |
|
||||
|
|
||||
QVariant TransactionListModel::data(QModelIndex const& _index, int _role) const |
|
||||
{ |
|
||||
if (_index.row() < 0 || _index.row() >= (int)m_transactions.size()) |
|
||||
return QVariant(); |
|
||||
auto const& transaction = m_transactions.at(_index.row()); |
|
||||
switch (_role) |
|
||||
{ |
|
||||
case TitleRole: |
|
||||
return QVariant(transaction.title); |
|
||||
case IdRole: |
|
||||
return QVariant(_index.row()); |
|
||||
default: |
|
||||
return QVariant(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
///@todo: get parameters from code model
|
|
||||
QList<TransactionParameterItem*> buildParameters(QTextDocument* _document, TransactionSettings const& _transaction, QString const& _functionId) |
|
||||
{ |
|
||||
QList<TransactionParameterItem*> params; |
|
||||
try |
|
||||
{ |
|
||||
std::shared_ptr<QContractDefinition> contract = QContractDefinition::Contract(_document->toPlainText()); |
|
||||
auto functions = contract->functions(); |
|
||||
for (auto f : functions) |
|
||||
{ |
|
||||
if (f->name() != _functionId) |
|
||||
continue; |
|
||||
|
|
||||
auto parameters = f->parameters(); |
|
||||
//build a list of parameters for a function. If the function is selected as current, add parameter values as well
|
|
||||
for (auto p : parameters) |
|
||||
{ |
|
||||
QString paramValue; |
|
||||
if (f->name() == _transaction.functionId) |
|
||||
{ |
|
||||
auto paramValueIter = _transaction.parameterValues.find(p->name()); |
|
||||
if (paramValueIter != _transaction.parameterValues.cend()) |
|
||||
paramValue = toQString(paramValueIter->second); |
|
||||
} |
|
||||
|
|
||||
TransactionParameterItem* item = new TransactionParameterItem(p->name(), p->type(), paramValue); |
|
||||
QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); |
|
||||
params.append(item); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
catch (boost::exception const&) |
|
||||
{ |
|
||||
//TODO:
|
|
||||
} |
|
||||
|
|
||||
return params; |
|
||||
} |
|
||||
|
|
||||
///@todo: get fnctions from code model
|
|
||||
QList<QString> TransactionListModel::getFunctions() |
|
||||
{ |
|
||||
QList<QString> functionNames; |
|
||||
try |
|
||||
{ |
|
||||
QString code = m_document->toPlainText(); |
|
||||
std::shared_ptr<QContractDefinition> contract(QContractDefinition::Contract(code)); |
|
||||
auto functions = contract->functions(); |
|
||||
for (auto f : functions) |
|
||||
{ |
|
||||
functionNames.append(f->name()); |
|
||||
} |
|
||||
} |
|
||||
catch (boost::exception const&) |
|
||||
{ |
|
||||
} |
|
||||
return functionNames; |
|
||||
} |
|
||||
|
|
||||
QVariantList TransactionListModel::getParameters(int _index, QString const& _functionId) |
|
||||
{ |
|
||||
TransactionSettings const& transaction = (_index >= 0 && _index < (int)m_transactions.size()) ? m_transactions[_index] : TransactionSettings(); |
|
||||
auto plist = buildParameters(m_document, transaction, _functionId); |
|
||||
QVariantList vl; |
|
||||
for (QObject* p : plist) |
|
||||
vl.append(QVariant::fromValue(p)); |
|
||||
return vl; |
|
||||
} |
|
||||
|
|
||||
TransactionListItem* TransactionListModel::getItem(int _index) |
|
||||
{ |
|
||||
TransactionSettings const& transaction = (_index >= 0 && _index < (int)m_transactions.size()) ? m_transactions[_index] : TransactionSettings(); |
|
||||
TransactionListItem* item = new TransactionListItem(_index, transaction, nullptr); |
|
||||
QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); |
|
||||
return item; |
|
||||
} |
|
||||
|
|
||||
void TransactionListModel::edit(QObject* _data) |
|
||||
{ |
|
||||
//these properties come from TransactionDialog QML object
|
|
||||
///@todo change the model to a qml component
|
|
||||
int index = _data->property("transactionIndex").toInt(); |
|
||||
QString title = _data->property("transactionTitle").toString(); |
|
||||
QString gas = _data->property("gas").toString(); |
|
||||
QString gasPrice = _data->property("gasPrice").toString(); |
|
||||
QString value = _data->property("transactionValue").toString(); |
|
||||
QString functionId = _data->property("functionId").toString(); |
|
||||
QAbstractListModel* paramsModel = qvariant_cast<QAbstractListModel*>(_data->property("transactionParams")); |
|
||||
TransactionSettings transaction(title, functionId, fromQString(value), fromQString(gas), fromQString(gasPrice)); |
|
||||
int paramCount = paramsModel->rowCount(QModelIndex()); |
|
||||
for (int p = 0; p < paramCount; ++p) |
|
||||
{ |
|
||||
QString paramName = paramsModel->data(paramsModel->index(p, 0), Qt::DisplayRole).toString(); |
|
||||
QString paramValue = paramsModel->data(paramsModel->index(p, 0), Qt::DisplayRole + 2).toString(); |
|
||||
if (!paramValue.isEmpty() && !paramName.isEmpty()) |
|
||||
transaction.parameterValues[paramName] = fromQString(paramValue); |
|
||||
} |
|
||||
|
|
||||
if (index >= 0 && index < (int)m_transactions.size()) |
|
||||
{ |
|
||||
beginRemoveRows(QModelIndex(), index, index); |
|
||||
m_transactions.erase(m_transactions.begin() + index); |
|
||||
endRemoveRows(); |
|
||||
} |
|
||||
else |
|
||||
index = rowCount(QModelIndex()); |
|
||||
|
|
||||
beginInsertRows(QModelIndex(), index, index); |
|
||||
m_transactions.push_back(transaction); |
|
||||
emit countChanged(); |
|
||||
endInsertRows(); |
|
||||
} |
|
||||
|
|
||||
int TransactionListModel::getCount() const |
|
||||
{ |
|
||||
return rowCount(QModelIndex()); |
|
||||
} |
|
||||
|
|
||||
void TransactionListModel::runTransaction(int _index) |
|
||||
{ |
|
||||
TransactionSettings tr = m_transactions.at(_index); |
|
||||
emit transactionStarted(tr); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,168 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file TransactionListView.h
|
|
||||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|
||||
* @date 2014 |
|
||||
* Ethereum IDE client. |
|
||||
*/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <QObject> |
|
||||
#include <QVariant> |
|
||||
#include <QAbstractListModel> |
|
||||
#include <QHash> |
|
||||
#include <QByteArray> |
|
||||
#include <libdevcore/Common.h> |
|
||||
#include <libethcore/CommonEth.h> |
|
||||
|
|
||||
class QTextDocument; |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace mix |
|
||||
{ |
|
||||
|
|
||||
/// Backend transaction config class
|
|
||||
struct TransactionSettings |
|
||||
{ |
|
||||
TransactionSettings(): |
|
||||
value(0), gas(10000), gasPrice(10 * dev::eth::szabo) {} |
|
||||
|
|
||||
TransactionSettings(QString const& _title, QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice): |
|
||||
title(_title), functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {} |
|
||||
|
|
||||
/// User specified transaction title
|
|
||||
QString title; |
|
||||
/// Contract function name
|
|
||||
QString functionId; |
|
||||
/// Transaction value
|
|
||||
u256 value; |
|
||||
/// Gas
|
|
||||
u256 gas; |
|
||||
/// Gas price
|
|
||||
u256 gasPrice; |
|
||||
/// Mapping from contract function parameter name to value
|
|
||||
std::map<QString, u256> parameterValues; |
|
||||
}; |
|
||||
|
|
||||
/// QML transaction parameter class
|
|
||||
class TransactionParameterItem: public QObject |
|
||||
{ |
|
||||
Q_OBJECT |
|
||||
Q_PROPERTY(QString name READ name CONSTANT) |
|
||||
Q_PROPERTY(QString type READ type CONSTANT) |
|
||||
Q_PROPERTY(QString value READ value CONSTANT) |
|
||||
public: |
|
||||
TransactionParameterItem(QString const& _name, QString const& _type, QString const& _value): |
|
||||
m_name(_name), m_type(_type), m_value(_value) {} |
|
||||
|
|
||||
/// Parameter name, set by contract definition
|
|
||||
QString name() { return m_name; } |
|
||||
/// Parameter type, set by contract definition
|
|
||||
QString type() { return m_type; } |
|
||||
/// Parameter value, set by user
|
|
||||
QString value() { return m_value; } |
|
||||
|
|
||||
private: |
|
||||
QString m_name; |
|
||||
QString m_type; |
|
||||
QString m_value; |
|
||||
}; |
|
||||
|
|
||||
class TransactionListItem: public QObject |
|
||||
{ |
|
||||
Q_OBJECT |
|
||||
Q_PROPERTY(int index READ index CONSTANT) |
|
||||
Q_PROPERTY(QString title READ title CONSTANT) |
|
||||
Q_PROPERTY(QString functionId READ functionId CONSTANT) |
|
||||
Q_PROPERTY(QString gas READ gas CONSTANT) |
|
||||
Q_PROPERTY(QString gasPrice READ gasPrice CONSTANT) |
|
||||
Q_PROPERTY(QString value READ value CONSTANT) |
|
||||
|
|
||||
public: |
|
||||
TransactionListItem(int _index, TransactionSettings const& _t, QObject* _parent); |
|
||||
|
|
||||
/// User specified transaction title
|
|
||||
QString title() { return m_title; } |
|
||||
/// Gas
|
|
||||
QString gas() { return m_gas; } |
|
||||
/// Gas cost
|
|
||||
QString gasPrice() { return m_gasPrice; } |
|
||||
/// Transaction value
|
|
||||
QString value() { return m_value; } |
|
||||
/// Contract function name
|
|
||||
QString functionId() { return m_functionId; } |
|
||||
/// Index of this transaction in the transactions list
|
|
||||
int index() { return m_index; } |
|
||||
|
|
||||
private: |
|
||||
int m_index; |
|
||||
QString m_title; |
|
||||
QString m_functionId; |
|
||||
QString m_value; |
|
||||
QString m_gas; |
|
||||
QString m_gasPrice; |
|
||||
}; |
|
||||
|
|
||||
/// QML model for a list of transactions
|
|
||||
class TransactionListModel: public QAbstractListModel |
|
||||
{ |
|
||||
Q_OBJECT |
|
||||
Q_PROPERTY(int count READ getCount() NOTIFY countChanged()) |
|
||||
|
|
||||
enum Roles |
|
||||
{ |
|
||||
TitleRole = Qt::DisplayRole, |
|
||||
IdRole = Qt::UserRole + 1 |
|
||||
}; |
|
||||
|
|
||||
public: |
|
||||
TransactionListModel(QObject* _parent, QTextDocument* _document); |
|
||||
~TransactionListModel() {} |
|
||||
|
|
||||
QHash<int, QByteArray> roleNames() const override; |
|
||||
int rowCount(QModelIndex const& _parent) const override; |
|
||||
QVariant data(QModelIndex const& _index, int _role) const override; |
|
||||
int getCount() const; |
|
||||
/// Apply changes from transaction dialog. Argument is a dialog model as defined in TransactionDialog.qml
|
|
||||
/// @todo Change that to transaction item
|
|
||||
Q_INVOKABLE void edit(QObject* _data); |
|
||||
/// @returns transaction item for a give index
|
|
||||
Q_INVOKABLE TransactionListItem* getItem(int _index); |
|
||||
/// @returns a list of functions for current contract
|
|
||||
Q_INVOKABLE QList<QString> getFunctions(); |
|
||||
/// @returns function parameters along with parameter values if set. @see TransactionParameterItem
|
|
||||
Q_INVOKABLE QVariantList getParameters(int _id, QString const& _functionId); |
|
||||
/// Launch transaction execution UI handler
|
|
||||
Q_INVOKABLE void runTransaction(int _index); |
|
||||
|
|
||||
signals: |
|
||||
/// Transaction count has changed
|
|
||||
void countChanged(); |
|
||||
/// Transaction has been launched
|
|
||||
void transactionStarted(dev::mix::TransactionSettings); |
|
||||
|
|
||||
private: |
|
||||
std::vector<TransactionSettings> m_transactions; |
|
||||
QTextDocument* m_document; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
@ -0,0 +1,182 @@ |
|||||
|
import QtQuick 2.2 |
||||
|
import QtQuick.Controls 1.2 |
||||
|
import QtQuick.Layouts 1.1 |
||||
|
import QtQuick.Window 2.0 |
||||
|
|
||||
|
Window { |
||||
|
modality: Qt.WindowModal |
||||
|
|
||||
|
width:640 |
||||
|
height:480 |
||||
|
|
||||
|
visible: false |
||||
|
|
||||
|
property alias stateTitle : titleField.text |
||||
|
property alias stateBalance : balanceField.text |
||||
|
property int stateIndex |
||||
|
property var stateTransactions: [] |
||||
|
signal accepted |
||||
|
|
||||
|
function open(index, item) { |
||||
|
stateIndex = index; |
||||
|
stateTitle = item.title; |
||||
|
stateBalance = item.balance; |
||||
|
transactionsModel.clear(); |
||||
|
stateTransactions = []; |
||||
|
var transactions = item.transactions; |
||||
|
for (var t = 0; t < transactions.length; t++) { |
||||
|
transactionsModel.append(item.transactions[t]); |
||||
|
stateTransactions.push(item.transactions[t]); |
||||
|
} |
||||
|
visible = true; |
||||
|
titleField.focus = true; |
||||
|
} |
||||
|
|
||||
|
function close() { |
||||
|
visible = false; |
||||
|
} |
||||
|
|
||||
|
function getItem() { |
||||
|
var item = { |
||||
|
title: stateDialog.stateTitle, |
||||
|
balance: stateDialog.stateBalance, |
||||
|
transactions: [] |
||||
|
} |
||||
|
item.transactions = stateTransactions; |
||||
|
return item; |
||||
|
} |
||||
|
|
||||
|
GridLayout { |
||||
|
id: dialogContent |
||||
|
columns: 2 |
||||
|
anchors.fill: parent |
||||
|
anchors.margins: 10 |
||||
|
rowSpacing: 10 |
||||
|
columnSpacing: 10 |
||||
|
|
||||
|
Label { |
||||
|
text: qsTr("Title") |
||||
|
} |
||||
|
TextField { |
||||
|
id: titleField |
||||
|
focus: true |
||||
|
Layout.fillWidth: true |
||||
|
} |
||||
|
|
||||
|
Label { |
||||
|
text: qsTr("Balance") |
||||
|
} |
||||
|
TextField { |
||||
|
id: balanceField |
||||
|
Layout.fillWidth: true |
||||
|
} |
||||
|
|
||||
|
Label { |
||||
|
text: qsTr("Transactions") |
||||
|
} |
||||
|
ListView { |
||||
|
Layout.fillWidth: true |
||||
|
Layout.fillHeight: true |
||||
|
model: transactionsModel |
||||
|
delegate: transactionRenderDelegate |
||||
|
} |
||||
|
|
||||
|
Label { |
||||
|
|
||||
|
} |
||||
|
Button { |
||||
|
text: qsTr("Add") |
||||
|
onClicked: transactionsModel.addTransaction() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
RowLayout { |
||||
|
anchors.bottom: parent.bottom |
||||
|
anchors.right: parent.right; |
||||
|
|
||||
|
Button { |
||||
|
text: qsTr("Ok"); |
||||
|
onClicked: { |
||||
|
close(); |
||||
|
accepted(); |
||||
|
} |
||||
|
} |
||||
|
Button { |
||||
|
text: qsTr("Cancel"); |
||||
|
onClicked: close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ListModel { |
||||
|
id: transactionsModel |
||||
|
|
||||
|
function editTransaction(index) { |
||||
|
transactionDialog.open(index, transactionsModel.get(index)); |
||||
|
} |
||||
|
|
||||
|
function addTransaction() { |
||||
|
|
||||
|
// Set next id here to work around Qt bug |
||||
|
// https://bugreports.qt-project.org/browse/QTBUG-41327 |
||||
|
// Second call to signal handler would just edit the item that was just created, no harm done |
||||
|
var item = { |
||||
|
value: "0", |
||||
|
functionId: "", |
||||
|
gas: "1000000000000", |
||||
|
gasPrice: "100000" |
||||
|
}; |
||||
|
|
||||
|
transactionDialog.open(transactionsModel.count, item); |
||||
|
} |
||||
|
|
||||
|
function deleteTransaction(index) { |
||||
|
stateTransactions.splice(index, 1); |
||||
|
transactionsModel.remove(index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Component { |
||||
|
id: transactionRenderDelegate |
||||
|
Item { |
||||
|
id: wrapperItem |
||||
|
height: 20 |
||||
|
width: parent.width |
||||
|
RowLayout { |
||||
|
anchors.fill: parent |
||||
|
Text { |
||||
|
Layout.fillWidth: true |
||||
|
Layout.fillHeight: true |
||||
|
text: functionId |
||||
|
font.pointSize: 12 |
||||
|
verticalAlignment: Text.AlignBottom |
||||
|
} |
||||
|
ToolButton { |
||||
|
text: qsTr("Edit"); |
||||
|
Layout.fillHeight: true |
||||
|
onClicked: transactionsModel.editTransaction(index) |
||||
|
} |
||||
|
ToolButton { |
||||
|
text: qsTr("Delete"); |
||||
|
Layout.fillHeight: true |
||||
|
onClicked: transactionsModel.deleteTransaction(index) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TransactionDialog { |
||||
|
id: transactionDialog |
||||
|
onAccepted: { |
||||
|
var item = transactionDialog.getItem(); |
||||
|
|
||||
|
if (transactionDialog.transactionIndex < transactionsModel.count) { |
||||
|
transactionsModel.set(transactionDialog.transactionIndex, item); |
||||
|
stateTransactions[index] = item; |
||||
|
} else { |
||||
|
transactionsModel.append(item); |
||||
|
stateTransactions.push(item); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,132 @@ |
|||||
|
import QtQuick 2.2 |
||||
|
import QtQuick.Controls.Styles 1.2 |
||||
|
import QtQuick.Controls 1.2 |
||||
|
import QtQuick.Dialogs 1.2 |
||||
|
import QtQuick.Layouts 1.1 |
||||
|
|
||||
|
Rectangle { |
||||
|
color: "transparent" |
||||
|
id: stateListContainer |
||||
|
focus: true |
||||
|
anchors.topMargin: 10 |
||||
|
anchors.left: parent.left |
||||
|
height: parent.height |
||||
|
width: parent.width |
||||
|
property var stateList: [] |
||||
|
|
||||
|
Connections { |
||||
|
target: appContext |
||||
|
onProjectLoaded: { |
||||
|
var items = JSON.parse(_json); |
||||
|
for(var i = 0; i < items.length; i++) { |
||||
|
stateListModel.append(items[i]); |
||||
|
stateList.push(items[i]) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ListView { |
||||
|
anchors.top: parent.top |
||||
|
height: parent.height |
||||
|
width: parent.width |
||||
|
model: stateListModel |
||||
|
delegate: renderDelegate |
||||
|
} |
||||
|
|
||||
|
Button { |
||||
|
anchors.bottom: parent.bottom |
||||
|
action: addStateAction |
||||
|
} |
||||
|
|
||||
|
StateDialog { |
||||
|
id: stateDialog |
||||
|
onAccepted: { |
||||
|
var item = stateDialog.getItem(); |
||||
|
if (stateDialog.stateIndex < stateListModel.count) { |
||||
|
stateList[stateDialog.stateIndex] = item; |
||||
|
stateListModel.set(stateDialog.stateIndex, item); |
||||
|
} else { |
||||
|
stateList.push(item); |
||||
|
stateListModel.append(item); |
||||
|
} |
||||
|
|
||||
|
stateListModel.save(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ListModel { |
||||
|
id: stateListModel |
||||
|
|
||||
|
function addState() { |
||||
|
var item = { |
||||
|
title: "", |
||||
|
balance: "100000000000000000000000000", |
||||
|
transactions: [] |
||||
|
}; |
||||
|
stateDialog.open(stateListModel.count, item); |
||||
|
} |
||||
|
|
||||
|
function editState(index) { |
||||
|
stateDialog.open(index, stateList[index]); |
||||
|
} |
||||
|
|
||||
|
function runState(index) { |
||||
|
var item = stateList[index]; |
||||
|
debugModel.debugState(item); |
||||
|
} |
||||
|
|
||||
|
function deleteState(index) { |
||||
|
stateListModel.remove(index); |
||||
|
stateList.splice(index, 1); |
||||
|
save(); |
||||
|
} |
||||
|
|
||||
|
function save() { |
||||
|
var json = JSON.stringify(stateList); |
||||
|
appContext.saveProject(json); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Component { |
||||
|
id: renderDelegate |
||||
|
Item { |
||||
|
id: wrapperItem |
||||
|
height: 20 |
||||
|
width: parent.width |
||||
|
RowLayout { |
||||
|
anchors.fill: parent |
||||
|
Text { |
||||
|
Layout.fillWidth: true |
||||
|
Layout.fillHeight: true |
||||
|
text: title |
||||
|
font.pointSize: 12 |
||||
|
verticalAlignment: Text.AlignBottom |
||||
|
} |
||||
|
ToolButton { |
||||
|
text: qsTr("Edit"); |
||||
|
Layout.fillHeight: true |
||||
|
onClicked: stateListModel.editState(index); |
||||
|
} |
||||
|
ToolButton { |
||||
|
text: qsTr("Delete"); |
||||
|
Layout.fillHeight: true |
||||
|
onClicked: stateListModel.deleteState(index); |
||||
|
} |
||||
|
ToolButton { |
||||
|
text: qsTr("Run"); |
||||
|
Layout.fillHeight: true |
||||
|
onClicked: stateListModel.runState(index); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Action { |
||||
|
id: addStateAction |
||||
|
text: "&Add State" |
||||
|
shortcut: "Ctrl+N" |
||||
|
enabled: codeModel.hasContract && !debugModel.running; |
||||
|
onTriggered: stateListModel.addState(); |
||||
|
} |
||||
|
} |
||||
|
|
@ -1,89 +0,0 @@ |
|||||
import QtQuick 2.2 |
|
||||
import QtQuick.Controls.Styles 1.2 |
|
||||
import QtQuick.Controls 1.2 |
|
||||
import QtQuick.Dialogs 1.2 |
|
||||
import QtQuick.Layouts 1.1 |
|
||||
|
|
||||
|
|
||||
Rectangle { |
|
||||
color: "transparent" |
|
||||
id: transactionListContainer |
|
||||
focus: true |
|
||||
anchors.topMargin: 10 |
|
||||
anchors.left: parent.left |
|
||||
height: parent.height |
|
||||
width: parent.width |
|
||||
|
|
||||
ListView { |
|
||||
anchors.top: parent.top |
|
||||
height: parent.height |
|
||||
width: parent.width |
|
||||
id: transactionList |
|
||||
model: transactionListModel |
|
||||
delegate: renderDelegate |
|
||||
} |
|
||||
|
|
||||
Button { |
|
||||
anchors.bottom: parent.bottom |
|
||||
text: qsTr("Add") |
|
||||
onClicked: |
|
||||
{ |
|
||||
// Set next id here to work around Qt bug |
|
||||
// https://bugreports.qt-project.org/browse/QTBUG-41327 |
|
||||
// Second call to signal handle would just edit the item that was just created, no harm done |
|
||||
transactionDialog.reset(transactionListModel.count, transactionListModel); |
|
||||
transactionDialog.open(); |
|
||||
transactionDialog.focus = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
TransactionDialog { |
|
||||
id: transactionDialog |
|
||||
onAccepted: { |
|
||||
transactionListModel.edit(transactionDialog); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Component { |
|
||||
id: renderDelegate |
|
||||
Item { |
|
||||
id: wrapperItem |
|
||||
height: 20 |
|
||||
width: parent.width |
|
||||
RowLayout |
|
||||
{ |
|
||||
anchors.fill: parent |
|
||||
Text { |
|
||||
//anchors.fill: parent |
|
||||
Layout.fillWidth: true |
|
||||
Layout.fillHeight: true |
|
||||
text: title |
|
||||
font.pointSize: 12 |
|
||||
verticalAlignment: Text.AlignBottom |
|
||||
} |
|
||||
ToolButton { |
|
||||
text: qsTr("Edit"); |
|
||||
Layout.fillHeight: true |
|
||||
onClicked: { |
|
||||
transactionDialog.reset(index, transactionListModel); |
|
||||
transactionDialog.open(); |
|
||||
transactionDialog.focus = true; |
|
||||
} |
|
||||
} |
|
||||
ToolButton { |
|
||||
text: qsTr("Delete"); |
|
||||
Layout.fillHeight: true |
|
||||
onClicked: { |
|
||||
} |
|
||||
} |
|
||||
ToolButton { |
|
||||
text: qsTr("Run"); |
|
||||
Layout.fillHeight: true |
|
||||
onClicked: { |
|
||||
transactionListModel.runTransaction(index); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
Loading…
Reference in new issue