Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

Conflicts:
	mix/AssemblyDebuggerModel.cpp
cl-refactor
Gav Wood 10 years ago
parent
commit
9fa59b8b84
  1. 21
      libsolidity/Scanner.cpp
  2. 1
      libsolidity/Scanner.h
  3. 59
      libweb3jsonrpc/WebThreeStubServer.cpp
  4. 74
      mix/AppContext.cpp
  5. 38
      mix/AppContext.h
  6. 222
      mix/AssemblyDebuggerControl.cpp
  7. 52
      mix/AssemblyDebuggerControl.h
  8. 41
      mix/AssemblyDebuggerModel.cpp
  9. 26
      mix/AssemblyDebuggerModel.h
  10. 72
      mix/CodeEditorExtensionManager.cpp
  11. 11
      mix/CodeEditorExtensionManager.h
  12. 170
      mix/CodeHighlighter.cpp
  13. 109
      mix/CodeHighlighter.h
  14. 177
      mix/CodeModel.cpp
  15. 163
      mix/CodeModel.h
  16. 49
      mix/ConstantCompilationControl.cpp
  17. 11
      mix/ConstantCompilationControl.h
  18. 66
      mix/ConstantCompilationModel.cpp
  19. 60
      mix/ConstantCompilationModel.h
  20. 6
      mix/ContractCallDataEncoder.cpp
  21. 6
      mix/ContractCallDataEncoder.h
  22. 21
      mix/Extension.cpp
  23. 11
      mix/Extension.h
  24. 41
      mix/KeyEventManager.cpp
  25. 47
      mix/KeyEventManager.h
  26. 28
      mix/MixApplication.cpp
  27. 14
      mix/MixApplication.h
  28. 19
      mix/QContractDefinition.cpp
  29. 15
      mix/QContractDefinition.h
  30. 6
      mix/QFunctionDefinition.cpp
  31. 13
      mix/QFunctionDefinition.h
  32. 11
      mix/QVariableDeclaration.h
  33. 1
      mix/QVariableDefinition.h
  34. 26
      mix/StateListView.cpp
  35. 19
      mix/StateListView.h
  36. 221
      mix/TransactionListModel.cpp
  37. 168
      mix/TransactionListModel.h
  38. 15
      mix/main.cpp
  39. 3
      mix/qml.qrc
  40. 73
      mix/qml/MainContent.qml
  41. 182
      mix/qml/StateDialog.qml
  42. 132
      mix/qml/StateList.qml
  43. 75
      mix/qml/TransactionDialog.qml
  44. 89
      mix/qml/TransactionList.qml
  45. 28
      mix/qml/main.qml
  46. 7
      test/SolidityScanner.cpp

21
libsolidity/Scanner.cpp

@ -285,8 +285,6 @@ Token::Value Scanner::scanMultiLineDocComment()
bool endFound = false; bool endFound = false;
bool charsAdded = false; bool charsAdded = false;
advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
//handle newlines in multline comments //handle newlines in multline comments
@ -354,11 +352,20 @@ Token::Value Scanner::scanSlash()
return Token::WHITESPACE; return Token::WHITESPACE;
else if (m_char == '*') else if (m_char == '*')
{ {
Token::Value comment; advance(); //consume the last '*' at /**
m_nextSkippedComment.location.start = firstSlashPosition; skipWhitespaceExceptLF();
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = getSourcePos(); // special case of a closed normal multiline comment
m_nextSkippedComment.token = comment; if (!m_source.isPastEndOfInput() && m_source.get(0) == '/')
advance(); //skip the closing slash
else // we actually have a multiline documentation comment
{
Token::Value comment;
m_nextSkippedComment.location.start = firstSlashPosition;
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
}
return Token::WHITESPACE; return Token::WHITESPACE;
} }
else else

1
libsolidity/Scanner.h

@ -119,6 +119,7 @@ public:
{ {
return m_currentToken.token; return m_currentToken.token;
} }
Location getCurrentLocation() const { return m_currentToken.location; } Location getCurrentLocation() const { return m_currentToken.location; }
std::string const& getCurrentLiteral() const { return m_currentToken.literal; } std::string const& getCurrentLiteral() const { return m_currentToken.literal; }
///@} ///@}

59
libweb3jsonrpc/WebThreeStubServer.cpp

@ -266,21 +266,16 @@ std::string WebThreeStubServer::shh_addToGroup(std::string const& _group, std::s
std::string WebThreeStubServer::eth_balanceAt(string const& _address) std::string WebThreeStubServer::eth_balanceAt(string const& _address)
{ {
int block = 0; return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault()));
return toJS(client()->balanceAt(jsToAddress(_address), block));
} }
Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash) Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash)
{ {
if (!client())
return "";
return toJson(client()->blockInfo(jsToFixed<32>(_hash))); return toJson(client()->blockInfo(jsToFixed<32>(_hash)));
} }
Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number) Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number)
{ {
if (!client())
return "";
return toJson(client()->blockInfo(client()->hashFromNumber(_number))); return toJson(client()->blockInfo(client()->hashFromNumber(_number)));
} }
@ -332,8 +327,6 @@ static TransactionSkeleton toTransaction(Json::Value const& _json)
std::string WebThreeStubServer::eth_call(Json::Value const& _json) std::string WebThreeStubServer::eth_call(Json::Value const& _json)
{ {
std::string ret; std::string ret;
if (!client())
return ret;
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size()) if (!t.from && m_accounts.size())
{ {
@ -355,31 +348,27 @@ std::string WebThreeStubServer::eth_call(Json::Value const& _json)
bool WebThreeStubServer::eth_changed(int const& _id) bool WebThreeStubServer::eth_changed(int const& _id)
{ {
if (!client())
return false;
return client()->checkWatch(_id); return client()->checkWatch(_id);
} }
std::string WebThreeStubServer::eth_codeAt(string const& _address) std::string WebThreeStubServer::eth_codeAt(string const& _address)
{ {
int block = 0; return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault()));
return client() ? jsFromBinary(client()->codeAt(jsToAddress(_address), block)) : "";
} }
std::string WebThreeStubServer::eth_coinbase() std::string WebThreeStubServer::eth_coinbase()
{ {
return client() ? toJS(client()->address()) : ""; return toJS(client()->address());
} }
double WebThreeStubServer::eth_countAt(string const& _address) double WebThreeStubServer::eth_countAt(string const& _address)
{ {
int block = 0; return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault());
return client() ? (double)(uint64_t)client()->countAt(jsToAddress(_address), block) : 0;
} }
int WebThreeStubServer::eth_defaultBlock() int WebThreeStubServer::eth_defaultBlock()
{ {
return client() ? client()->getDefault() : 0; return client()->getDefault();
} }
std::string WebThreeStubServer::eth_gasPrice() std::string WebThreeStubServer::eth_gasPrice()
@ -397,15 +386,11 @@ std::string WebThreeStubServer::db_get(std::string const& _name, std::string con
Json::Value WebThreeStubServer::eth_filterLogs(int const& _id) Json::Value WebThreeStubServer::eth_filterLogs(int const& _id)
{ {
if (!client())
return Json::Value(Json::arrayValue);
return toJson(client()->logs(_id)); return toJson(client()->logs(_id));
} }
Json::Value WebThreeStubServer::eth_logs(Json::Value const& _json) Json::Value WebThreeStubServer::eth_logs(Json::Value const& _json)
{ {
if (!client())
return Json::Value(Json::arrayValue);
return toJson(client()->logs(toLogFilter(_json))); return toJson(client()->logs(toLogFilter(_json)));
} }
@ -429,15 +414,12 @@ bool WebThreeStubServer::eth_listening()
bool WebThreeStubServer::eth_mining() bool WebThreeStubServer::eth_mining()
{ {
return client() ? client()->isMining() : false; return client()->isMining();
} }
int WebThreeStubServer::eth_newFilter(Json::Value const& _json) int WebThreeStubServer::eth_newFilter(Json::Value const& _json)
{ {
unsigned ret = -1; unsigned ret = -1;
if (!client())
return ret;
// ret = client()->installWatch(toMessageFilter(_json));
ret = client()->installWatch(toLogFilter(_json)); ret = client()->installWatch(toLogFilter(_json));
return ret; return ret;
} }
@ -445,8 +427,6 @@ int WebThreeStubServer::eth_newFilter(Json::Value const& _json)
int WebThreeStubServer::eth_newFilterString(std::string const& _filter) int WebThreeStubServer::eth_newFilterString(std::string const& _filter)
{ {
unsigned ret = -1; unsigned ret = -1;
if (!client())
return ret;
if (_filter.compare("chain") == 0) if (_filter.compare("chain") == 0)
ret = client()->installWatch(dev::eth::ChainChangedFilter); ret = client()->installWatch(dev::eth::ChainChangedFilter);
else if (_filter.compare("pending") == 0) else if (_filter.compare("pending") == 0)
@ -528,7 +508,7 @@ std::string WebThreeStubServer::eth_solidity(std::string const& _code)
int WebThreeStubServer::eth_number() int WebThreeStubServer::eth_number()
{ {
return client() ? client()->number() + 1 : 0; return client()->number() + 1;
} }
int WebThreeStubServer::eth_peerCount() int WebThreeStubServer::eth_peerCount()
@ -538,7 +518,6 @@ int WebThreeStubServer::eth_peerCount()
bool WebThreeStubServer::shh_post(Json::Value const& _json) bool WebThreeStubServer::shh_post(Json::Value const& _json)
{ {
// cnote << this << m_ids;
shh::Message m = toMessage(_json); shh::Message m = toMessage(_json);
Secret from; Secret from;
@ -571,16 +550,12 @@ bool WebThreeStubServer::db_putString(std::string const& _name, std::string cons
bool WebThreeStubServer::eth_setCoinbase(std::string const& _address) bool WebThreeStubServer::eth_setCoinbase(std::string const& _address)
{ {
if (!client())
return false;
client()->setAddress(jsToAddress(_address)); client()->setAddress(jsToAddress(_address));
return true; return true;
} }
bool WebThreeStubServer::eth_setDefaultBlock(int const& _block) bool WebThreeStubServer::eth_setDefaultBlock(int const& _block)
{ {
if (!client())
return false;
client()->setDefault(_block); client()->setDefault(_block);
return true; return true;
} }
@ -596,9 +571,6 @@ bool WebThreeStubServer::eth_setListening(bool const& _listening)
bool WebThreeStubServer::eth_setMining(bool const& _mining) bool WebThreeStubServer::eth_setMining(bool const& _mining)
{ {
if (!client())
return false;
if (_mining) if (_mining)
client()->startMining(); client()->startMining();
else else
@ -646,22 +618,17 @@ bool WebThreeStubServer::shh_uninstallFilter(int const& _id)
std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage) std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage)
{ {
int block = 0; return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault()));
return client() ? toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), block)) : "";
} }
Json::Value WebThreeStubServer::eth_storageAt(string const& _address) Json::Value WebThreeStubServer::eth_storageAt(string const& _address)
{ {
if (!client())
return Json::Value(Json::objectValue);
return toJson(client()->storageAt(jsToAddress(_address))); return toJson(client()->storageAt(jsToAddress(_address)));
} }
std::string WebThreeStubServer::eth_transact(Json::Value const& _json) std::string WebThreeStubServer::eth_transact(Json::Value const& _json)
{ {
std::string ret; std::string ret;
if (!client())
return ret;
TransactionSkeleton t = toTransaction(_json); TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size()) if (!t.from && m_accounts.size())
{ {
@ -697,36 +664,26 @@ bool WebThreeStubServer::authenticate(TransactionSkeleton const& _t) const
Json::Value WebThreeStubServer::eth_transactionByHash(std::string const& _hash, int const& _i) Json::Value WebThreeStubServer::eth_transactionByHash(std::string const& _hash, int const& _i)
{ {
if (!client())
return "";
return toJson(client()->transaction(jsToFixed<32>(_hash), _i)); return toJson(client()->transaction(jsToFixed<32>(_hash), _i));
} }
Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i) Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i)
{ {
if (!client())
return "";
return toJson(client()->transaction(client()->hashFromNumber(_number), _i)); return toJson(client()->transaction(client()->hashFromNumber(_number), _i));
} }
Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i) Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i)
{ {
if (!client())
return "";
return toJson(client()->uncle(jsToFixed<32>(_hash), _i)); return toJson(client()->uncle(jsToFixed<32>(_hash), _i));
} }
Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i) Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i)
{ {
if (!client())
return "";
return toJson(client()->uncle(client()->hashFromNumber(_number), _i)); return toJson(client()->uncle(client()->hashFromNumber(_number), _i));
} }
bool WebThreeStubServer::eth_uninstallFilter(int const& _id) bool WebThreeStubServer::eth_uninstallFilter(int const& _id)
{ {
if (!client())
return false;
client()->uninstallWatch(_id); client()->uninstallWatch(_id);
return true; return true;
} }

74
mix/AppContext.cpp

@ -25,55 +25,53 @@
#include <QDebug> #include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <libwebthree/WebThree.h> #include <QStandardPaths>
#include <QFile>
#include <QDir>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libsolidity/CompilerStack.h> #include <libwebthree/WebThree.h>
#include "KeyEventManager.h"
#include "AppContext.h" #include "AppContext.h"
#include "CodeModel.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
AppContext* AppContext::Instance = nullptr; const QString c_projectFileName = "project.mix";
AppContext::AppContext(QQmlApplicationEngine* _engine) AppContext::AppContext(QQmlApplicationEngine* _engine)
{ {
m_applicationEngine = std::unique_ptr<QQmlApplicationEngine>(_engine); m_applicationEngine = _engine;
m_keyEventManager = std::unique_ptr<KeyEventManager>(new KeyEventManager()); //m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"}));
m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"})); m_codeModel = std::unique_ptr<CodeModel>(new CodeModel(this));
m_compiler = std::unique_ptr<CompilerStack>(new CompilerStack()); //TODO : to move in a codel model structure. m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
} m_applicationEngine->rootContext()->setContextProperty("appContext", this);
QQmlApplicationEngine* AppContext::appEngine()
{
return m_applicationEngine.get();
} }
void AppContext::initKeyEventManager(QObject* _res) AppContext::~AppContext()
{ {
QObject* mainContent = _res->findChild<QObject*>("mainContent", Qt::FindChildrenRecursively);
if (mainContent)
QObject::connect(mainContent, SIGNAL(keyPressed(QVariant)), m_keyEventManager.get(), SLOT(keyPressed(QVariant)));
else
qDebug() << "Unable to find QObject of mainContent.qml. KeyEvent will not be handled!";
} }
KeyEventManager* AppContext::getKeyEventManager() void AppContext::loadProject()
{ {
return m_keyEventManager.get(); QString path = QStandardPaths::locate(QStandardPaths::DataLocation, c_projectFileName);
if (!path.isEmpty())
{
QFile file(path);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&file);
QString json = stream.readAll();
emit projectLoaded(json);
}
}
} }
CompilerStack* AppContext::compiler() QQmlApplicationEngine* AppContext::appEngine()
{
return m_compiler.get();
}
void AppContext::setApplicationContext(QQmlApplicationEngine* _engine)
{ {
if (Instance == nullptr) return m_applicationEngine;
Instance = new AppContext(_engine);
} }
void AppContext::displayMessageDialog(QString _title, QString _message) void AppContext::displayMessageDialog(QString _title, QString _message)
@ -88,3 +86,19 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message); dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message);
QMetaObject::invokeMethod(dialogWin, "open"); QMetaObject::invokeMethod(dialogWin, "open");
} }
void AppContext::saveProject(QString const& _json)
{
QDir dirPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
QString path = QDir(dirPath).filePath(c_projectFileName);
if (!path.isEmpty())
{
dirPath.mkpath(dirPath.path());
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream stream(&file);
stream << _json;
}
}
}

38
mix/AppContext.h

@ -28,11 +28,10 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QQmlApplicationEngine> #include <QUrl>
#include <libsolidity/CompilerStack.h> #include <QObject>
#include <libwebthree/WebThree.h>
#include "KeyEventManager.h"
class QQmlApplicationEngine;
namespace dev namespace dev
{ {
class WebThreeDirect; class WebThreeDirect;
@ -47,42 +46,39 @@ namespace dev
namespace mix namespace mix
{ {
class CodeModel;
/** /**
* @brief Provides access to application scope variable. * @brief Provides access to application scope variable.
*/ */
class AppContext: public QObject class AppContext: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
AppContext(QQmlApplicationEngine* _engine); AppContext(QQmlApplicationEngine* _engine);
/// Get the current QQmlApplicationEngine instance. virtual ~AppContext();
static AppContext* getInstance() { return Instance; }
/// Renew QQMLApplicationEngine with a new instance.
static void setApplicationContext(QQmlApplicationEngine* _engine);
/// Get the current QQMLApplicationEngine instance. /// Get the current QQMLApplicationEngine instance.
QQmlApplicationEngine* appEngine(); QQmlApplicationEngine* appEngine();
/// Initialize KeyEventManager (used to handle key pressed event). /// Get code model
void initKeyEventManager(QObject* _obj); CodeModel* codeModel() { return m_codeModel.get(); }
/// Get the current KeyEventManager instance.
KeyEventManager* getKeyEventManager();
/// Get the current Compiler instance (used to parse and compile contract code).
dev::solidity::CompilerStack* compiler();
/// Display an alert message. /// Display an alert message.
void displayMessageDialog(QString _title, QString _message); void displayMessageDialog(QString _title, QString _message);
/// Load project settings
void loadProject();
signals:
void projectLoaded(QString const& _json);
private: private:
static AppContext* Instance; QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<QQmlApplicationEngine> m_applicationEngine;
std::unique_ptr<dev::WebThreeDirect> m_webThree; std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<KeyEventManager> m_keyEventManager; std::unique_ptr<CodeModel> m_codeModel;
std::unique_ptr<solidity::CompilerStack> m_compiler;
public slots: public slots:
/// Delete the current instance when application quit. /// Delete the current instance when application quit.
void quitApplication() { delete Instance; } void quitApplication() {}
/// Initialize components after the loading of the main QML view. /// Write json to a settings file
void resourceLoaded(QObject* _obj, QUrl _url) { Q_UNUSED(_url); initKeyEventManager(_obj); } void saveProject(QString const& _json);
}; };
} }

222
mix/AssemblyDebuggerControl.cpp

@ -19,24 +19,39 @@
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QDebug> #include <QDebug>
#include <QVariableDefinition.h>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QModelIndex> #include <QModelIndex>
#include <libdevcore/CommonJS.h> #include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include "AssemblyDebuggerModel.h" #include "AssemblyDebuggerModel.h"
#include "AssemblyDebuggerControl.h" #include "AssemblyDebuggerControl.h"
#include "KeyEventManager.h"
#include "AppContext.h" #include "AppContext.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "TransactionListModel.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
#include "QVariableDeclaration.h" #include "QVariableDeclaration.h"
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
#include "CodeModel.h"
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
AssemblyDebuggerControl::AssemblyDebuggerControl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::ModalDialog) /// @todo Move this to QML
dev::u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(dev::u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::ModalDialog), m_running(false)
{ {
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*"); qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*"); qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
@ -44,14 +59,11 @@ AssemblyDebuggerControl::AssemblyDebuggerControl(QTextDocument* _doc): Extension
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>"); qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*"); qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData"); qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData");
qRegisterMetaType<DebuggingStatusResult>("DebuggingStatusResult");
connect(this, SIGNAL(dataAvailable(bool, DebuggingStatusResult, QList<QVariableDefinition*>, QList<QObject*>, AssemblyDebuggerData)),
this, SLOT(updateGUI(bool, DebuggingStatusResult, QList<QVariableDefinition*>, QList<QObject*>, AssemblyDebuggerData)), Qt::QueuedConnection);
connect(this, &AssemblyDebuggerControl::dataAvailable, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
m_modelDebugger = std::unique_ptr<AssemblyDebuggerModel>(new AssemblyDebuggerModel); m_modelDebugger = std::unique_ptr<AssemblyDebuggerModel>(new AssemblyDebuggerModel);
m_compilation = std::unique_ptr<ConstantCompilationModel>(new ConstantCompilationModel);
m_doc = _doc; _context->appEngine()->rootContext()->setContextProperty("debugModel", this);
} }
QString AssemblyDebuggerControl::contentUrl() const QString AssemblyDebuggerControl::contentUrl() const
@ -61,111 +73,139 @@ QString AssemblyDebuggerControl::contentUrl() const
QString AssemblyDebuggerControl::title() const QString AssemblyDebuggerControl::title() const
{ {
return QApplication::tr("debugger"); return QApplication::tr("Debugger");
} }
void AssemblyDebuggerControl::start() const void AssemblyDebuggerControl::start() const
{ {
//start to listen on F5
m_ctx->getKeyEventManager()->registerEvent(this, SLOT(keyPressed(int)));
} }
void AssemblyDebuggerControl::keyPressed(int _key) void AssemblyDebuggerControl::debugDeployment()
{ {
if (_key == Qt::Key_F5) executeSequence(std::vector<TransactionSettings>(), 0);
{ }
QtConcurrent::run([this]()
{ void AssemblyDebuggerControl::debugState(QVariantMap _state)
deployContract(m_doc->toPlainText()); {
}); u256 balance = fromQString(_state.value("balance").toString());
} QVariantList transactions = _state.value("transactions").toList();
else if (_key == Qt::Key_F6)
std::vector<TransactionSettings> transactionSequence;
for (auto const& t: transactions)
{ {
m_modelDebugger->resetState(); QVariantMap transaction = t.toMap();
AppContext::getInstance()->displayMessageDialog(QApplication::tr("State status"), QApplication::tr("State reseted ... need to redeploy contract"));
QString functionId = transaction.value("functionId").toString();
u256 value = fromQString(transaction.value("value").toString());
u256 gas = fromQString(transaction.value("gas").toString());
u256 gasPrice = fromQString(transaction.value("gasPrice").toString());
QVariantMap params = transaction.value("parameters").toMap();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (auto p = params.cbegin(); p != params.cend(); ++p)
transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString())));
transactionSequence.push_back(transactionSettings);
} }
executeSequence(transactionSequence, balance);
} }
void AssemblyDebuggerControl::callContract(TransactionSettings _tr, dev::Address _contract) void AssemblyDebuggerControl::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
{ {
CompilerResult compilerRes = m_compilation->compile(m_doc->toPlainText()); if (m_running)
if (!compilerRes.success) throw (std::logic_error("debugging already running"));
AppContext::getInstance()->displayMessageDialog("debugger","compilation failed"); auto compilerRes = m_ctx->codeModel()->code();
else std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
m_running = true;
emit runStarted();
emit stateChanged();
//run sequence
QtConcurrent::run([=]()
{ {
ContractCallDataEncoder c; try
std::shared_ptr<QContractDefinition> contractDef = QContractDefinition::Contract(m_doc->toPlainText());
QFunctionDefinition* f = nullptr;
for (int k = 0; k < contractDef->functions().size(); k++)
{ {
if (contractDef->functions().at(k)->name() == _tr.functionId) bytes contractCode = compilerRes->bytes();
std::vector<dev::bytes> transactonData;
QFunctionDefinition* f;
ContractCallDataEncoder c;
//encode data for all transactions
for (auto const& t: _sequence)
{ {
f = (QFunctionDefinition*)contractDef->functions().at(k); f = nullptr;
break; for (int tf = 0; tf < contractDef->functionsList().size(); tf++)
{
if (contractDef->functionsList().at(tf)->name() == t.functionId)
{
f = contractDef->functionsList().at(tf);
break;
}
}
if (!f)
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
c.encode(f->index());
for (int p = 0; p < f->parametersList().size(); p++)
{
QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p);
u256 value = 0;
auto v = t.parameterValues.find(var->name());
if (v != t.parameterValues.cend())
value = v->second;
c.encode(var, value);
}
transactonData.emplace_back(c.encodedData());
} }
}
if (!f) //run contract creation first
AppContext::getInstance()->displayMessageDialog(QApplication::tr("debugger"), QApplication::tr("function not found. Please redeploy this contract.")); m_modelDebugger->resetState(_balance);
else DebuggingContent debuggingContent = m_modelDebugger->deployContract(contractCode);
{ Address address = debuggingContent.contractAddress;
c.encode(f->index()); for (unsigned i = 0; i < _sequence.size(); ++i)
for (int k = 0; k < f->parameters().size(); k++) debuggingContent = m_modelDebugger->callContract(address, transactonData.at(i), _sequence.at(i));
if (f)
debuggingContent.returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
for (int i = 0; i < debuggingContent.machineStates.size(); i++)
{ {
QVariableDeclaration* var = (QVariableDeclaration*)f->parameters().at(k); QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes()));
c.encode(var, _tr.parameterValues[var->name()]); s->setState(debuggingContent.machineStates.at(i));
wStates.append(s);
} }
DebuggingContent debuggingContent = m_modelDebugger->callContract(_contract, c.encodedData(), _tr); //collect states for last transaction
debuggingContent.returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue); AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
finalizeExecution(debuggingContent); emit dataAvailable(debuggingContent.returnParameters, wStates, code);
emit runComplete();
}
catch(boost::exception const&)
{
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
} }
}
}
void AssemblyDebuggerControl::deployContract(QString _source)
{
CompilerResult compilerRes = m_compilation->compile(_source);
if (!compilerRes.success)
emit dataAvailable(false, DebuggingStatusResult::Compilationfailed);
else
{
m_previousDebugResult = m_modelDebugger->deployContract(compilerRes.bytes);
finalizeExecution(m_previousDebugResult);
}
}
void AssemblyDebuggerControl::finalizeExecution(DebuggingContent _debuggingContent) catch(std::exception const& e)
{ {
//we need to wrap states in a QObject before sending to QML. emit runFailed(e.what());
QList<QObject*> wStates; }
for(int i = 0; i < _debuggingContent.machineStates.size(); i++) m_running = false;
{ emit stateChanged();
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(_debuggingContent.executionCode, _debuggingContent.executionData.toBytes())); });
s->setState(_debuggingContent.machineStates.at(i));
wStates.append(s);
}
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(_debuggingContent.executionCode);
emit dataAvailable(true, DebuggingStatusResult::Ok, _debuggingContent.returnParameters, wStates, code);
} }
void AssemblyDebuggerControl::updateGUI(bool _success, DebuggingStatusResult const& _reason, QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code) void AssemblyDebuggerControl::showDebugger(QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code)
{ {
Q_UNUSED(_reason); m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
if (_success) m_appEngine->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code)));
{ m_appEngine->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates)); m_appEngine->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
m_appEngine->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code))); this->addContentOn(this);
m_appEngine->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
m_appEngine->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
this->addContentOn(this);
}
else
m_ctx->displayMessageDialog(QApplication::tr("debugger"), QApplication::tr("compilation failed"));
} }
void AssemblyDebuggerControl::runTransaction(TransactionSettings const& _tr) void AssemblyDebuggerControl::showDebugError(QString const& _error)
{ {
QtConcurrent::run([this, _tr]() m_ctx->displayMessageDialog(QApplication::tr("Debugger"), _error);
{
callContract(_tr, m_previousDebugResult.contractAddress);
});
} }

52
mix/AssemblyDebuggerControl.h

@ -19,25 +19,18 @@
#pragma once #pragma once
#include <atomic>
#include <QKeySequence> #include <QKeySequence>
#include <QTextDocument>
#include "Extension.h" #include "Extension.h"
#include "ConstantCompilationModel.h"
#include "TransactionListModel.h"
#include "AssemblyDebuggerModel.h" #include "AssemblyDebuggerModel.h"
#include "AppContext.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>; using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
enum DebuggingStatusResult
{
Ok,
Compilationfailed
};
Q_DECLARE_METATYPE(AssemblyDebuggerData) Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(DebuggingStatusResult)
Q_DECLARE_METATYPE(dev::mix::DebuggingContent) Q_DECLARE_METATYPE(dev::mix::DebuggingContent)
class AppContext;
namespace dev namespace dev
{ {
namespace mix namespace mix
@ -51,33 +44,46 @@ class AssemblyDebuggerControl: public Extension
Q_OBJECT Q_OBJECT
public: public:
AssemblyDebuggerControl(QTextDocument* _doc); AssemblyDebuggerControl(AppContext* _context);
~AssemblyDebuggerControl() {} ~AssemblyDebuggerControl() {}
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
private: private:
void deployContract(QString _source); void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
void callContract(TransactionSettings _tr, Address _contract);
void finalizeExecution(DebuggingContent _content);
std::unique_ptr<AssemblyDebuggerModel> m_modelDebugger; std::unique_ptr<AssemblyDebuggerModel> m_modelDebugger;
std::unique_ptr<ConstantCompilationModel> m_compilation; std::atomic<bool> m_running;
DebuggingContent m_previousDebugResult; //TODO: to be replaced in a more consistent struct. Used for now to keep the contract address in case of future transaction call.
QTextDocument* m_doc;
public slots: public slots:
/// Handle key pressed. F5 deploy contract - F6 reset state. /// Run the contract constructor and show debugger window.
void keyPressed(int); void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void debugState(QVariantMap _state);
private slots:
/// Update UI with machine states result. Display a modal dialog. /// Update UI with machine states result. Display a modal dialog.
void updateGUI(bool _success, DebuggingStatusResult const& _reason, QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); void showDebugger(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// Run the given transaction. /// Update UI with transaction run error.
void runTransaction(TransactionSettings const& _tr); void showDebugError(QString const& _error);
signals: signals:
/// Transaction execution started
void runStarted();
/// Transaction execution completed successfully
void runComplete();
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
/// Execution state changed
void stateChanged();
/// Emited when machine states are available. /// Emited when machine states are available.
void dataAvailable(bool _success, DebuggingStatusResult const& _reason, QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
}; };
} }

41
mix/AssemblyDebuggerModel.cpp

@ -23,30 +23,30 @@
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
#include "AppContext.h"
#include "TransactionListModel.h"
#include "AssemblyDebuggerModel.h" #include "AssemblyDebuggerModel.h"
#include "ConstantCompilationModel.h" #include "AppContext.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix;
namespace dev
{
namespace mix
{
AssemblyDebuggerModel::AssemblyDebuggerModel(): AssemblyDebuggerModel::AssemblyDebuggerModel():
m_userAccount(KeyPair::create()), m_userAccount(KeyPair::create())
m_baseState(Address(), m_overlayDB, BaseState::Empty)
{ {
m_baseState.addBalance(m_userAccount.address(), 10000000 * ether); resetState(10000000 * ether);
m_executiveState = m_baseState;
m_currentExecution = std::unique_ptr<Executive>(new Executive(m_executiveState, LastHashes(), 0));
} }
DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& _rawTransaction) DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& _rawTransaction)
{ {
QList<DebuggingState> machineStates; QList<DebuggingState> machineStates;
m_currentExecution.reset(new Executive(m_executiveState, LastHashes(), 0)); eth::Executive execution(m_executiveState, LastHashes(), 0);
m_currentExecution->setup(_rawTransaction); execution.setup(_rawTransaction);
std::vector<DebuggingState const*> levels; std::vector<DebuggingState const*> levels;
bytes code; bytes code;
bytesConstRef data; bytesConstRef data;
@ -72,12 +72,12 @@ DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const&
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
}; };
m_currentExecution->go(onOp); execution.go(onOp);
m_currentExecution->finalize(); execution.finalize();
m_executiveState.completeMine(); m_executiveState.completeMine();
DebuggingContent d; DebuggingContent d;
d.returnValue = m_currentExecution->out().toVector(); d.returnValue = execution.out().toVector();
d.machineStates = machineStates; d.machineStates = machineStates;
d.executionCode = code; d.executionCode = code;
d.executionData = data; d.executionData = data;
@ -91,7 +91,7 @@ DebuggingContent AssemblyDebuggerModel::deployContract(bytes const& _code)
u256 gasPrice = 10000000000000; u256 gasPrice = 10000000000000;
u256 gas = 1000000; u256 gas = 1000000;
u256 amount = 100; u256 amount = 100;
Transaction _tr(amount, gasPrice, min(gas, m_baseState.gasLimitRemaining()), _code, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret()); Transaction _tr(amount, gasPrice, min(gas, m_executiveState.gasLimitRemaining()), _code, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret());
bytes b = _tr.rlp(); bytes b = _tr.rlp();
dev::bytesConstRef bytesRef = &b; dev::bytesConstRef bytesRef = &b;
DebuggingContent d = executeTransaction(bytesRef); DebuggingContent d = executeTransaction(bytesRef);
@ -102,7 +102,7 @@ DebuggingContent AssemblyDebuggerModel::deployContract(bytes const& _code)
DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{ {
Transaction tr = Transaction(_tr.value, _tr.gasPrice, min(_tr.gas, m_baseState.gasLimitRemaining()), _contract, _data, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret()); Transaction tr = Transaction(_tr.value, _tr.gasPrice, min(_tr.gas, m_executiveState.gasLimitRemaining()), _contract, _data, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret());
bytes b = tr.rlp(); bytes b = tr.rlp();
dev::bytesConstRef bytesRef = &b; dev::bytesConstRef bytesRef = &b;
DebuggingContent d = executeTransaction(bytesRef); DebuggingContent d = executeTransaction(bytesRef);
@ -110,8 +110,11 @@ DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, b
return d; return d;
} }
void AssemblyDebuggerModel::resetState() void AssemblyDebuggerModel::resetState(u256 _balance)
{ {
// Reset the state back to our clean premine. m_executiveState = eth::State(Address(), m_overlayDB, BaseState::Empty);
m_executiveState = m_baseState; m_executiveState.addBalance(m_userAccount.address(), _balance);
}
}
} }

26
mix/AssemblyDebuggerModel.h

@ -26,13 +26,31 @@
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "TransactionListModel.h"
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
/// 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;
};
/** /**
* @brief Long-life object for managing all executions. * @brief Long-life object for managing all executions.
*/ */
@ -44,15 +62,13 @@ public:
DebuggingContent callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); DebuggingContent callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
/// Deploy the contract described by _code. /// Deploy the contract described by _code.
DebuggingContent deployContract(bytes const& _code); DebuggingContent deployContract(bytes const& _code);
/// Reset state to the base state. /// Reset state to the empty state with given balance.
void resetState(); void resetState(u256 _balance);
private: private:
KeyPair m_userAccount; KeyPair m_userAccount;
OverlayDB m_overlayDB; OverlayDB m_overlayDB;
eth::State m_baseState;
eth::State m_executiveState; eth::State m_executiveState;
std::unique_ptr<eth::Executive> m_currentExecution;
DebuggingContent executeTransaction(dev::bytesConstRef const& _rawTransaction); DebuggingContent executeTransaction(dev::bytesConstRef const& _rawTransaction);
}; };

72
mix/CodeEditorExtensionManager.cpp

@ -25,14 +25,22 @@
#include <QQmlEngine> #include <QQmlEngine>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQuickTextDocument> #include <QQuickTextDocument>
#include <libevm/VM.h>
#include "ConstantCompilationControl.h" #include "ConstantCompilationControl.h"
#include "AssemblyDebuggerControl.h" #include "AssemblyDebuggerControl.h"
#include "TransactionListView.h" #include "StateListView.h"
#include "AppContext.h" #include "AppContext.h"
#include "MixApplication.h"
#include "CodeModel.h"
#include "CodeHighlighter.h"
#include "CodeEditorExtensionManager.h" #include "CodeEditorExtensionManager.h"
using namespace dev::mix; using namespace dev::mix;
CodeEditorExtensionManager::CodeEditorExtensionManager():
m_appContext(static_cast<MixApplication*>(QApplication::instance())->context())
{
}
CodeEditorExtensionManager::~CodeEditorExtensionManager() CodeEditorExtensionManager::~CodeEditorExtensionManager()
{ {
m_features.clear(); m_features.clear();
@ -42,40 +50,30 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
{ {
if (!_editor) if (!_editor)
return; return;
try
QVariant doc = _editor->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>())
{ {
QVariant doc = _editor->property("textDocument"); QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>();
if (doc.canConvert<QQuickTextDocument*>()) if (qqdoc)
{ {
QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>(); m_doc = qqdoc->textDocument();
if (qqdoc)
{
m_doc = qqdoc->textDocument();
auto args = QApplication::arguments();
if (args.length() > 1)
{
QString path = args[1];
QFile file(path);
if (file.exists() && file.open(QFile::ReadOnly))
m_doc->setPlainText(file.readAll());
}
}
} }
} }
catch (...)
{
qDebug() << "unable to load editor: ";
}
} }
void CodeEditorExtensionManager::initExtensions() void CodeEditorExtensionManager::initExtensions()
{ {
initExtension(std::make_shared<ConstantCompilationControl>(m_doc)); std::shared_ptr<ConstantCompilationControl> output = std::make_shared<ConstantCompilationControl>(m_appContext);
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_doc); std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<TransactionListView> tr = std::make_shared<TransactionListView>(m_doc); std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
QObject::connect(tr->model(), &TransactionListModel::transactionStarted, debug.get(), &AssemblyDebuggerControl::runTransaction); QObject::connect(m_doc, &QTextDocument::contentsChange, this, &CodeEditorExtensionManager::onCodeChange);
QObject::connect(debug.get(), &AssemblyDebuggerControl::runFailed, output.get(), &ConstantCompilationControl::displayError);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output);
initExtension(debug); initExtension(debug);
initExtension(tr); initExtension(stateList);
} }
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext) void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
@ -103,6 +101,26 @@ void CodeEditorExtensionManager::setEditor(QQuickItem* _editor)
{ {
this->loadEditor(_editor); this->loadEditor(_editor);
this->initExtensions(); this->initExtensions();
auto args = QApplication::arguments();
if (args.length() > 1)
{
QString path = args[1];
QFile file(path);
if (file.exists() && file.open(QFile::ReadOnly))
m_doc->setPlainText(file.readAll());
}
}
void CodeEditorExtensionManager::onCodeChange()
{
m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting
m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText());
}
void CodeEditorExtensionManager::applyCodeHighlight()
{
m_appContext->codeModel()->updateFormatting(m_doc);
} }
void CodeEditorExtensionManager::setRightTabView(QQuickItem* _tabView) void CodeEditorExtensionManager::setRightTabView(QQuickItem* _tabView)

11
mix/CodeEditorExtensionManager.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <memory>
#include <QQuickItem> #include <QQuickItem>
#include <QTextDocument> #include <QTextDocument>
#include <QVector> #include <QVector>
@ -32,6 +33,9 @@ namespace dev
namespace mix namespace mix
{ {
class AppContext;
/** /**
* @brief Init and provides connection between extensions. * @brief Init and provides connection between extensions.
*/ */
@ -44,7 +48,7 @@ class CodeEditorExtensionManager: public QObject
Q_PROPERTY(QQuickItem* rightTabView MEMBER m_rightTabView WRITE setRightTabView) Q_PROPERTY(QQuickItem* rightTabView MEMBER m_rightTabView WRITE setRightTabView)
public: public:
CodeEditorExtensionManager() {} CodeEditorExtensionManager();
~CodeEditorExtensionManager(); ~CodeEditorExtensionManager();
/// Initialize all extensions. /// Initialize all extensions.
void initExtensions(); void initExtensions();
@ -57,12 +61,17 @@ public:
/// Set current right tab view. /// Set current right tab view.
void setRightTabView(QQuickItem*); void setRightTabView(QQuickItem*);
private slots:
void onCodeChange();
void applyCodeHighlight();
private: private:
QQuickItem* m_editor; QQuickItem* m_editor;
QVector<std::shared_ptr<Extension>> m_features; QVector<std::shared_ptr<Extension>> m_features;
QQuickItem* m_tabView; QQuickItem* m_tabView;
QQuickItem* m_rightTabView; QQuickItem* m_rightTabView;
QTextDocument* m_doc; QTextDocument* m_doc;
AppContext* m_appContext;
void loadEditor(QQuickItem* _editor); void loadEditor(QQuickItem* _editor);
}; };

170
mix/CodeHighlighter.cpp

@ -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;
}
}

109
mix/CodeHighlighter.h

@ -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;
};
}
}

177
mix/CodeModel.cpp

@ -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);
}

163
mix/CodeModel.h

@ -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;
};
}
}

49
mix/ConstantCompilationControl.cpp

@ -28,14 +28,16 @@
#include <QtCore/QtCore> #include <QtCore/QtCore>
#include <QDebug> #include <QDebug>
#include "ConstantCompilationControl.h" #include "ConstantCompilationControl.h"
#include "ConstantCompilationModel.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
#include "AppContext.h"
#include "CodeModel.h"
using namespace dev::mix; using namespace dev::mix;
ConstantCompilationControl::ConstantCompilationControl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::Tab) ConstantCompilationControl::ConstantCompilationControl(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::Tab)
{ {
m_editor = _doc; connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update);
m_compilationModel = std::unique_ptr<ConstantCompilationModel>(new ConstantCompilationModel()); connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update);
} }
QString ConstantCompilationControl::contentUrl() const QString ConstantCompilationControl::contentUrl() const
@ -50,18 +52,25 @@ QString ConstantCompilationControl::title() const
void ConstantCompilationControl::start() const void ConstantCompilationControl::start() const
{ {
connect(m_editor, SIGNAL(contentsChange(int,int,int)), this, SLOT(compile()));
} }
void ConstantCompilationControl::compile() void ConstantCompilationControl::update()
{ {
QString codeContent = m_editor->toPlainText().replace("\n", ""); auto result = m_ctx->codeModel()->code();
if (codeContent.isEmpty())
resetOutPut(); QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (result->successfull())
{
status->setProperty("text", "succeeded");
status->setProperty("color", "green");
content->setProperty("text", result->assemblyCode());
}
else else
{ {
CompilerResult res = m_compilationModel->compile(m_editor->toPlainText().replace("\t", " ")); status->setProperty("text", "failure");
writeOutPut(res); status->setProperty("color", "red");
content->setProperty("text", result->compilerMessage());
} }
} }
@ -73,20 +82,12 @@ void ConstantCompilationControl::resetOutPut()
content->setProperty("text", ""); content->setProperty("text", "");
} }
void ConstantCompilationControl::writeOutPut(CompilerResult const& _res)
void ConstantCompilationControl::displayError(QString const& _error)
{ {
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively); QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively); QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (_res.success) status->setProperty("text", "failure");
{ status->setProperty("color", "red");
status->setProperty("text", "succeeded"); content->setProperty("text", _error);
status->setProperty("color", "green");
content->setProperty("text", _res.hexCode);
}
else
{
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", _res.comment);
}
} }

11
mix/ConstantCompilationControl.h

@ -19,8 +19,6 @@
#pragma once #pragma once
#include <QTextDocument>
#include "ConstantCompilationModel.h"
#include "Extension.h" #include "Extension.h"
namespace dev namespace dev
@ -36,21 +34,18 @@ class ConstantCompilationControl: public Extension
Q_OBJECT Q_OBJECT
public: public:
ConstantCompilationControl(QTextDocument* _doc); ConstantCompilationControl(AppContext* _appContext);
~ConstantCompilationControl() {} ~ConstantCompilationControl() {}
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
private: private:
QTextDocument* m_editor;
std::unique_ptr<ConstantCompilationModel> m_compilationModel;
void writeOutPut(CompilerResult const& _res);
void resetOutPut(); void resetOutPut();
public slots: public slots:
/// Compile text editor content. void update();
void compile(); void displayError(QString const& _error);
}; };
} }

66
mix/ConstantCompilationModel.cpp

@ -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;
}

60
mix/ConstantCompilationModel.h

@ -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);
};
}
}

6
mix/ContractCallDataEncoder.cpp

@ -43,19 +43,19 @@ void ContractCallDataEncoder::encode(int _functionIndex)
m_encodedData.insert(m_encodedData.end(), i.begin(), i.end()); m_encodedData.insert(m_encodedData.end(), i.begin(), i.end());
} }
void ContractCallDataEncoder::encode(QVariableDeclaration* _dec, bool _value) void ContractCallDataEncoder::encode(QVariableDeclaration const* _dec, bool _value)
{ {
return encode(_dec, QString(formatBool(_value))); return encode(_dec, QString(formatBool(_value)));
} }
void ContractCallDataEncoder::encode(QVariableDeclaration* _dec, QString _value) void ContractCallDataEncoder::encode(QVariableDeclaration const* _dec, QString _value)
{ {
int padding = this->padding(_dec->type()); int padding = this->padding(_dec->type());
bytes data = padded(jsToBytes(_value.toStdString()), padding); bytes data = padded(jsToBytes(_value.toStdString()), padding);
m_encodedData.insert(m_encodedData.end(), data.begin(), data.end()); m_encodedData.insert(m_encodedData.end(), data.begin(), data.end());
} }
void ContractCallDataEncoder::encode(QVariableDeclaration* _dec, u256 _value) void ContractCallDataEncoder::encode(QVariableDeclaration const* _dec, u256 _value)
{ {
int padding = this->padding(_dec->type()); int padding = this->padding(_dec->type());
std::ostringstream s; std::ostringstream s;

6
mix/ContractCallDataEncoder.h

@ -38,11 +38,11 @@ class ContractCallDataEncoder
public: public:
ContractCallDataEncoder() {} ContractCallDataEncoder() {}
/// Encode variable in order to be sent as parameter. /// Encode variable in order to be sent as parameter.
void encode(QVariableDeclaration* _dec, QString _value); void encode(QVariableDeclaration const* _dec, QString _value);
/// Encode variable in order to be sent as parameter. /// Encode variable in order to be sent as parameter.
void encode(QVariableDeclaration* _dec, u256 _value); void encode(QVariableDeclaration const* _dec, u256 _value);
/// Encode variable in order to be sent as parameter. /// Encode variable in order to be sent as parameter.
void encode(QVariableDeclaration* _dec, bool _value); void encode(QVariableDeclaration const* _dec, bool _value);
/// Encode index of the function to call. /// Encode index of the function to call.
void encode(int _functionIndex); void encode(int _functionIndex);
/// Decode variable in order to be sent to QML view. /// Decode variable in order to be sent to QML view.

21
mix/Extension.cpp

@ -19,6 +19,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QDebug> #include <QDebug>
#include <QQmlApplicationEngine>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include "Extension.h" #include "Extension.h"
@ -26,20 +27,20 @@
using namespace dev; using namespace dev;
using namespace dev::mix; using namespace dev::mix;
Extension::Extension() Extension::Extension(AppContext* _context)
{ {
init(); init(_context);
} }
Extension::Extension(ExtensionDisplayBehavior _displayBehavior) Extension::Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior)
{ {
init(); init(_context);
m_displayBehavior = _displayBehavior; m_displayBehavior = _displayBehavior;
} }
void Extension::init() void Extension::init(AppContext* _context)
{ {
m_ctx = AppContext::getInstance(); m_ctx = _context;
m_appEngine = m_ctx->appEngine(); m_appEngine = m_ctx->appEngine();
} }
@ -50,7 +51,7 @@ void Extension::addTabOn(QObject* _view)
QVariant returnValue; QVariant returnValue;
QQmlComponent* component = new QQmlComponent( QQmlComponent* component = new QQmlComponent(
AppContext::getInstance()->appEngine(), m_appEngine,
QUrl(contentUrl()), _view); QUrl(contentUrl()), _view);
QMetaObject::invokeMethod(_view, "addTab", QMetaObject::invokeMethod(_view, "addTab",
@ -66,9 +67,9 @@ void Extension::addContentOn(QObject* _view)
Q_UNUSED(_view); Q_UNUSED(_view);
if (m_displayBehavior == ExtensionDisplayBehavior::ModalDialog) if (m_displayBehavior == ExtensionDisplayBehavior::ModalDialog)
{ {
QQmlComponent* component = new QQmlComponent(AppContext::getInstance()->appEngine(), QUrl(contentUrl()), _view); QQmlComponent* component = new QQmlComponent(m_appEngine, QUrl(contentUrl()), _view);
QObject* dialogWin = AppContext::getInstance()->appEngine()->rootObjects().at(0)->findChild<QObject*>("dialog", Qt::FindChildrenRecursively); QObject* dialogWin = m_appEngine->rootObjects().at(0)->findChild<QObject*>("dialog", Qt::FindChildrenRecursively);
QObject* dialogWinComponent = AppContext::getInstance()->appEngine()->rootObjects().at(0)->findChild<QObject*>("modalDialogContent", Qt::FindChildrenRecursively); QObject* dialogWinComponent = m_appEngine->rootObjects().at(0)->findChild<QObject*>("modalDialogContent", Qt::FindChildrenRecursively);
dialogWinComponent->setProperty("sourceComponent", QVariant::fromValue(component)); dialogWinComponent->setProperty("sourceComponent", QVariant::fromValue(component));
dialogWin->setProperty("title", title()); dialogWin->setProperty("title", title());
QMetaObject::invokeMethod(dialogWin, "open"); QMetaObject::invokeMethod(dialogWin, "open");

11
mix/Extension.h

@ -21,13 +21,16 @@
#include <QApplication> #include <QApplication>
#include <QQmlComponent> #include <QQmlComponent>
#include "AppContext.h"
class QQmlApplicationEngine;
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
class AppContext;
enum ExtensionDisplayBehavior enum ExtensionDisplayBehavior
{ {
Tab, Tab,
@ -41,8 +44,8 @@ class Extension: public QObject
Q_OBJECT Q_OBJECT
public: public:
Extension(); Extension(AppContext* _context);
Extension(ExtensionDisplayBehavior _displayBehavior); Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior);
/// Return the QML url of the view to display. /// Return the QML url of the view to display.
virtual QString contentUrl() const { return ""; } virtual QString contentUrl() const { return ""; }
/// Return the title of this extension. /// Return the title of this extension.
@ -65,7 +68,7 @@ protected:
QQmlApplicationEngine* m_appEngine; QQmlApplicationEngine* m_appEngine;
private: private:
void init(); void init(AppContext* _context);
}; };
} }

41
mix/KeyEventManager.cpp

@ -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());
}

47
mix/KeyEventManager.h

@ -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);
};

28
mix/MixApplication.cpp

@ -20,26 +20,24 @@
*/ */
#include <QDebug> #include <QDebug>
#include <QQmlApplicationEngine>
#include "CodeEditorExtensionManager.h"
#include "MixApplication.h" #include "MixApplication.h"
#include "AppContext.h"
#include <QMenuBar>
using namespace dev::mix; using namespace dev::mix;
MixApplication::MixApplication(int _argc, char* _argv[]): QApplication(_argc, _argv) MixApplication::MixApplication(int _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get()))
{ {
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
m_engine->load(QUrl("qrc:/qml/main.qml"));
m_appContext->loadProject();
} }
bool MixApplication::notify(QObject* _receiver, QEvent* _event) MixApplication::~MixApplication()
{ {
try
{
return MixApplication::notify(_receiver, _event);
}
catch (std::exception& _ex)
{
qDebug() << "std::exception was caught " << _ex.what();
}
catch (...)
{
qDebug() << "uncaught exception ";
}
return false;
} }

14
mix/MixApplication.h

@ -23,21 +23,31 @@
#pragma once #pragma once
#include <memory>
#include <QApplication> #include <QApplication>
class QQmlApplicationEngine;
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
class AppContext;
class MixApplication: public QApplication class MixApplication: public QApplication
{ {
Q_OBJECT Q_OBJECT
public: public:
MixApplication(int _argc, char* _argv[]); MixApplication(int _argc, char* _argv[]);
virtual ~MixApplication() {} virtual ~MixApplication();
virtual bool notify(QObject* _receiver, QEvent* _event); AppContext* context() { return m_appContext.get(); }
QQmlApplicationEngine* engine() { return m_engine.get(); }
private:
std::unique_ptr<QQmlApplicationEngine> m_engine;
std::unique_ptr<AppContext> m_appContext;
}; };
} }

19
mix/QContractDefinition.cpp

@ -31,18 +31,13 @@
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
std::shared_ptr<QContractDefinition> QContractDefinition::Contract(QString _source) QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{ {
CompilerStack* comp = AppContext::getInstance()->compiler(); std::vector<FunctionDefinition const*> functions = _contract->getInterfaceFunctions();
comp->addSource("contract", _source.toStdString());
comp->parse();
ContractDefinition const* def = &comp->getContractDefinition(comp->getContractNames().front());
return std::make_shared<QContractDefinition>(def);
}
void QContractDefinition::initQFunctions()
{
std::vector<FunctionDefinition const*> functions = m_contract->getInterfaceFunctions();
for (unsigned i = 0; i < functions.size(); i++) for (unsigned i = 0; i < functions.size(); i++)
m_functions.append(new QFunctionDefinition(functions.at(i), i)); {
FunctionDefinition const* func = functions.at(i);
m_functions.append(new QFunctionDefinition(func, i));
}
} }

15
mix/QContractDefinition.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include "QFunctionDefinition.h" #include "QFunctionDefinition.h"
#include "QBasicNodeDefinition.h" #include "QBasicNodeDefinition.h"
@ -34,20 +35,18 @@ namespace mix
class QContractDefinition: public QBasicNodeDefinition class QContractDefinition: public QBasicNodeDefinition
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QList<QFunctionDefinition*> functions READ functions) Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
public: public:
QContractDefinition(solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract), m_contract(_contract) { initQFunctions(); } QContractDefinition() {}
QContractDefinition(solidity::ContractDefinition const* _contract);
/// Get all the functions of the contract. /// Get all the functions of the contract.
QList<QFunctionDefinition*> functions() const { return m_functions; } QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
/// Get the description (functions, parameters, return parameters, ...) of the contract describes by _code. QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
static std::shared_ptr<QContractDefinition> Contract(QString _code);
private: private:
solidity::ContractDefinition const* m_contract;
QList<QFunctionDefinition*> m_functions; QList<QFunctionDefinition*> m_functions;
void initQFunctions();
}; };
} }
} }

6
mix/QFunctionDefinition.cpp

@ -25,13 +25,13 @@
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
void QFunctionDefinition::initQParameters() QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index)
{ {
std::vector<std::shared_ptr<VariableDeclaration>> parameters = m_functions->getParameterList().getParameters(); std::vector<std::shared_ptr<VariableDeclaration>> parameters = _f->getParameterList().getParameters();
for (unsigned i = 0; i < parameters.size(); i++) for (unsigned i = 0; i < parameters.size(); i++)
m_parameters.append(new QVariableDeclaration(parameters.at(i).get())); m_parameters.append(new QVariableDeclaration(parameters.at(i).get()));
std::vector<std::shared_ptr<VariableDeclaration>> returnParameters = m_functions->getReturnParameters(); std::vector<std::shared_ptr<VariableDeclaration>> returnParameters = _f->getReturnParameters();
for (unsigned i = 0; i < returnParameters.size(); i++) for (unsigned i = 0; i < returnParameters.size(); i++)
m_returnParameters.append(new QVariableDeclaration(returnParameters.at(i).get())); m_returnParameters.append(new QVariableDeclaration(returnParameters.at(i).get()));
} }

13
mix/QFunctionDefinition.h

@ -22,8 +22,9 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <QVariableDeclaration.h> #include "QVariableDeclaration.h"
#include "QBasicNodeDefinition.h" #include "QBasicNodeDefinition.h"
namespace dev namespace dev
@ -34,13 +35,16 @@ namespace mix
class QFunctionDefinition: public QBasicNodeDefinition class QFunctionDefinition: public QBasicNodeDefinition
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QList<QVariableDeclaration*> parameters READ parameters) Q_PROPERTY(QQmlListProperty<dev::mix::QVariableDeclaration> parameters READ parameters)
Q_PROPERTY(int index READ index) Q_PROPERTY(int index READ index)
public: public:
QFunctionDefinition(solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index), m_functions(_f) { initQParameters(); } QFunctionDefinition() {}
QFunctionDefinition(solidity::FunctionDefinition const* _f, int _index);
/// Get all input parameters of this function. /// Get all input parameters of this function.
QList<QVariableDeclaration*> parameters() const { return m_parameters; } QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property.
QQmlListProperty<QVariableDeclaration> parameters() const { return QQmlListProperty<QVariableDeclaration>(const_cast<QFunctionDefinition*>(this), const_cast<QFunctionDefinition*>(this)->m_parameters); }
/// Get all return parameters of this function. /// Get all return parameters of this function.
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; } QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; }
/// Get the index of this function on the contract ABI. /// Get the index of this function on the contract ABI.
@ -48,7 +52,6 @@ public:
private: private:
int m_index; int m_index;
solidity::FunctionDefinition const* m_functions;
QList<QVariableDeclaration*> m_parameters; QList<QVariableDeclaration*> m_parameters;
QList<QVariableDeclaration*> m_returnParameters; QList<QVariableDeclaration*> m_returnParameters;
void initQParameters(); void initQParameters();

11
mix/QVariableDeclaration.h

@ -35,15 +35,12 @@ class QVariableDeclaration: public QBasicNodeDefinition
Q_PROPERTY(QString type READ type CONSTANT) Q_PROPERTY(QString type READ type CONSTANT)
public: public:
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_variable(_v) {} QVariableDeclaration() {}
/// Get the type of this variable. QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {}
QString type() const { return QString::fromStdString(m_variable->getType()->toString()); } QString type() const { return m_type; }
private: private:
solidity::VariableDeclaration const* m_variable; QString m_type;
}; };
} }
} }
Q_DECLARE_METATYPE(dev::mix::QVariableDeclaration*)

1
mix/QVariableDefinition.h

@ -32,6 +32,7 @@ namespace mix
class QVariableDefinition: public QObject class QVariableDefinition: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString value READ value CONSTANT) Q_PROPERTY(QString value READ value CONSTANT)
Q_PROPERTY(QVariableDeclaration* declaration READ declaration CONSTANT) Q_PROPERTY(QVariableDeclaration* declaration READ declaration CONSTANT)

26
mix/TransactionListView.cpp → mix/StateListView.cpp

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file TransactionListView.cpp /** @file StateListView.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014 * @date 2014
* Ethereum IDE client. * Ethereum IDE client.
@ -25,32 +25,24 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QDebug> #include <QDebug>
#include "TransactionListView.h" #include "StateListView.h"
#include "TransactionListModel.h"
using namespace dev::mix;
TransactionListView::TransactionListView(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::RightTab) using namespace dev::mix;
{
m_editor = _doc;
m_model.reset(new TransactionListModel(this, _doc));
m_appEngine->rootContext()->setContextProperty("transactionListModel", m_model.get());
}
TransactionListView::~TransactionListView() StateListView::StateListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightTab)
{ {
//implementation is in cpp file so that all types deleted are complete
} }
QString TransactionListView::contentUrl() const QString StateListView::contentUrl() const
{ {
return QStringLiteral("qrc:/qml/TransactionList.qml"); return QStringLiteral("qrc:/qml/StateList.qml");
} }
QString TransactionListView::title() const QString StateListView::title() const
{ {
return QApplication::tr("Transactions"); return QApplication::tr("States");
} }
void TransactionListView::start() const void StateListView::start() const
{ {
} }

19
mix/TransactionListView.h → mix/StateListView.h

@ -11,7 +11,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file TransactionListView.h /** @file StateListView.h
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014 * @date 2014
* Ethereum IDE client. * Ethereum IDE client.
@ -19,6 +19,7 @@
#pragma once #pragma once
#include <memory>
#include <QTextDocument> #include <QTextDocument>
#include "Extension.h" #include "Extension.h"
@ -27,26 +28,16 @@ namespace dev
namespace mix namespace mix
{ {
class TransactionListModel; /// State list control
class StateListView: public Extension
/// Transactions list control
/// @todo This should be moved into state as a sequence
class TransactionListView: public Extension
{ {
Q_OBJECT Q_OBJECT
public: public:
TransactionListView(QTextDocument*); StateListView(AppContext* _context);
~TransactionListView();
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
/// @returns the underlying model
TransactionListModel* model() const { return m_model.get(); }
private:
QTextDocument* m_editor;
std::unique_ptr<TransactionListModel> m_model;
}; };
} }

221
mix/TransactionListModel.cpp

@ -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);
}
}
}

168
mix/TransactionListModel.h

@ -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;
};
}
}

15
mix/main.cpp

@ -20,22 +20,11 @@
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include "CodeEditorExtensionManager.h"
#include "AppContext.h"
#include "MixApplication.h" #include "MixApplication.h"
using namespace dev::mix; using namespace dev::mix;
int main(int _argc, char *_argv[]) int main(int _argc, char* _argv[])
{ {
QApplication app(_argc, _argv); MixApplication app(_argc, _argv);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QQmlApplicationEngine* engine = new QQmlApplicationEngine();
AppContext::setApplicationContext(engine);
QObject::connect(&app, SIGNAL(lastWindowClosed()), AppContext::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
QObject::connect(engine, SIGNAL(objectCreated(QObject*, QUrl)), AppContext::getInstance(), SLOT(resourceLoaded(QObject*, QUrl)));
engine->load(QUrl("qrc:/qml/main.qml"));
return app.exec(); return app.exec();
} }

3
mix/qml.qrc

@ -8,8 +8,9 @@
<file>qml/js/Debugger.js</file> <file>qml/js/Debugger.js</file>
<file>qml/BasicMessage.qml</file> <file>qml/BasicMessage.qml</file>
<file>qml/TransactionDialog.qml</file> <file>qml/TransactionDialog.qml</file>
<file>qml/TransactionList.qml</file>
<file>qml/ModalDialog.qml</file> <file>qml/ModalDialog.qml</file>
<file>qml/AlertMessageDialog.qml</file> <file>qml/AlertMessageDialog.qml</file>
<file>qml/StateDialog.qml</file>
<file>qml/StateList.qml</file>
</qresource> </qresource>
</RCC> </RCC>

73
mix/qml/MainContent.qml

@ -29,21 +29,64 @@ Rectangle {
id: contentView id: contentView
width: parent.width width: parent.width
height: parent.height * 0.7 height: parent.height * 0.7
TextArea {
id: codeEditor Item {
height: parent.height anchors.fill: parent
font.family: "Monospace" Rectangle {
font.pointSize: 12 id: lineColumn
width: parent.width property int rowHeight: codeEditor.font.pixelSize + 3
anchors.centerIn: parent color: "#202020"
tabChangesFocus: false width: 50
Keys.onPressed: { height: parent.height
if (event.key === Qt.Key_Tab) { Column {
codeEditor.insert(codeEditor.cursorPosition, "\t"); y: -codeEditor.flickableItem.contentY + 4
event.accepted = true; width: parent.width
} Repeater {
} model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
} delegate: Text {
id: text
color: codeEditor.textColor
font: codeEditor.font
width: lineColumn.width - 4
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
}
}
}
}
TextArea {
id: codeEditor
textColor: "#EEE8D5"
style: TextAreaStyle {
backgroundColor: "#002B36"
}
anchors.left: lineColumn.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
wrapMode: TextEdit.NoWrap
frameVisible: false
height: parent.height
font.family: "Monospace"
font.pointSize: 12
width: parent.width
//anchors.centerIn: parent
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
}
}
} }
Rectangle { Rectangle {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom

182
mix/qml/StateDialog.qml

@ -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);
}
}
}
}

132
mix/qml/StateList.qml

@ -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();
}
}

75
mix/qml/TransactionDialog.qml

@ -5,51 +5,43 @@ import QtQuick.Window 2.0
Window { Window {
modality: Qt.WindowModal modality: Qt.WindowModal
width:640 width:640
height:480 height:480
visible: false visible: false
function open()
{
visible = true;
}
function close()
{
visible = false;
}
property alias focus : titleField.focus
property alias transactionTitle : titleField.text
property int transactionIndex property int transactionIndex
property alias transactionParams : paramsModel; property alias transactionParams : paramsModel;
property alias gas : gasField.text; property alias gas : gasField.text;
property alias gasPrice : gasPriceField.text; property alias gasPrice : gasPriceField.text;
property alias transactionValue : valueField.text; property alias transactionValue : valueField.text;
property alias functionId : functionComboBox.currentText; property alias functionId : functionComboBox.currentText;
property var model; property var itemParams;
signal accepted; signal accepted;
function reset(index, m) { function open(index, item) {
model = m;
var item = model.getItem(index);
transactionIndex = index; transactionIndex = index;
transactionTitle = item.title;
gas = item.gas; gas = item.gas;
gasPrice = item.gasPrice; gasPrice = item.gasPrice;
transactionValue = item.value; transactionValue = item.value;
var functionId = item.functionId; var functionId = item.functionId;
itemParams = item.parameters !== undefined ? item.parameters : {};
functionsModel.clear(); functionsModel.clear();
var functionIndex = -1; var functionIndex = -1;
var functions = model.getFunctions(); var functions = codeModel.code.contract.functions;
for (var f = 0; f < functions.length; f++) { for (var f = 0; f < functions.length; f++) {
functionsModel.append({ text: functions[f] }); functionsModel.append({ text: functions[f].name });
if (functions[f] === item.functionId) if (functions[f].name === item.functionId)
functionIndex = f; functionIndex = f;
} }
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused funtion
functionComboBox.currentIndex = functionIndex; functionComboBox.currentIndex = functionIndex;
loadParameters();
visible = true;
valueField.focus = true;
} }
function loadParameters() { function loadParameters() {
@ -57,13 +49,36 @@ Window {
return; return;
paramsModel.clear(); paramsModel.clear();
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var parameters = model.getParameters(transactionIndex, functionsModel.get(functionComboBox.currentIndex).text); var func = codeModel.code.contract.functions[functionComboBox.currentIndex];
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++) { for (var p = 0; p < parameters.length; p++) {
paramsModel.append({ name: parameters[p].name, type: parameters[p].type, value: parameters[p].value }); var pname = parameters[p].name;
paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname] : "" });
} }
} }
} }
function close()
{
visible = false;
}
function getItem()
{
var item = {
functionId: transactionDialog.functionId,
gas: transactionDialog.gas,
gasPrice: transactionDialog.gasPrice,
value: transactionDialog.transactionValue,
parameters: {}
}
for (var p = 0; p < transactionDialog.transactionParams.count; p++) {
var parameter = transactionDialog.transactionParams.get(p);
item.parameters[parameter.name] = parameter.value;
}
return item;
}
GridLayout { GridLayout {
id: dialogContent id: dialogContent
columns: 2 columns: 2
@ -72,19 +87,9 @@ Window {
rowSpacing: 10 rowSpacing: 10
columnSpacing: 10 columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
}
Label { Label {
text: qsTr("Function") text: qsTr("Function")
} }
ComboBox { ComboBox {
id: functionComboBox id: functionComboBox
Layout.fillWidth: true Layout.fillWidth: true
@ -98,6 +103,7 @@ Window {
loadParameters(); loadParameters();
} }
} }
Label { Label {
text: qsTr("Value") text: qsTr("Value")
} }
@ -195,7 +201,8 @@ Window {
Connections { Connections {
target: loaderEditor.item target: loaderEditor.item
onTextChanged: { onTextChanged: {
paramsModel.setProperty(styleData.row, styleData.role, loaderEditor.item.text); if (styleData.role === "value" && styleData.row < paramsModel.count)
paramsModel.setProperty(styleData.row, styleData.role, loaderEditor.item.text);
} }
} }
sourceComponent: (styleData.selected) ? editor : null sourceComponent: (styleData.selected) ? editor : null

89
mix/qml/TransactionList.qml

@ -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);
}
}
}
}
}
}

28
mix/qml/main.qml

@ -1,9 +1,9 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.2
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.1
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
ApplicationWindow { ApplicationWindow {
@ -23,6 +23,11 @@ ApplicationWindow {
onTriggered: Qt.quit(); onTriggered: Qt.quit();
} }
} }
Menu {
title: qsTr("Debug")
MenuItem { action: debugRunAction }
MenuItem { action: debugResetStateAction }
}
} }
Component.onCompleted: { Component.onCompleted: {
setX(Screen.width / 2 - width / 2); setX(Screen.width / 2 - width / 2);
@ -41,4 +46,21 @@ ApplicationWindow {
objectName: "alertMessageDialog" objectName: "alertMessageDialog"
id: messageDialog id: messageDialog
} }
Action {
id: debugRunAction
text: "&Run"
shortcut: "F5"
enabled: codeModel.hasContract && !debugModel.running;
onTriggered: debugModel.debugDeployment();
}
Action {
id: debugResetStateAction
text: "Reset &State"
shortcut: "F6"
onTriggered: debugModel.resetState();
}
} }

7
test/SolidityScanner.cpp

@ -227,6 +227,13 @@ BOOST_AUTO_TEST_CASE(documentation_comment_before_eos)
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "");
} }
BOOST_AUTO_TEST_CASE(empty_multiline_comment)
{
Scanner scanner(CharStream("/**/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "");
}
BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos) BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos)
{ {
Scanner scanner(CharStream("/***/")); Scanner scanner(CharStream("/***/"));

Loading…
Cancel
Save