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 charsAdded = false;
advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput())
{
//handle newlines in multline comments
@ -354,11 +352,20 @@ Token::Value Scanner::scanSlash()
return Token::WHITESPACE;
else if (m_char == '*')
{
Token::Value comment;
m_nextSkippedComment.location.start = firstSlashPosition;
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
// special case of a closed normal multiline 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;
}
else

1
libsolidity/Scanner.h

@ -119,6 +119,7 @@ public:
{
return m_currentToken.token;
}
Location getCurrentLocation() const { return m_currentToken.location; }
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)
{
int block = 0;
return toJS(client()->balanceAt(jsToAddress(_address), block));
return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault()));
}
Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash)
{
if (!client())
return "";
return toJson(client()->blockInfo(jsToFixed<32>(_hash)));
}
Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number)
{
if (!client())
return "";
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 ret;
if (!client())
return ret;
TransactionSkeleton t = toTransaction(_json);
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)
{
if (!client())
return false;
return client()->checkWatch(_id);
}
std::string WebThreeStubServer::eth_codeAt(string const& _address)
{
int block = 0;
return client() ? jsFromBinary(client()->codeAt(jsToAddress(_address), block)) : "";
return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault()));
}
std::string WebThreeStubServer::eth_coinbase()
{
return client() ? toJS(client()->address()) : "";
return toJS(client()->address());
}
double WebThreeStubServer::eth_countAt(string const& _address)
{
int block = 0;
return client() ? (double)(uint64_t)client()->countAt(jsToAddress(_address), block) : 0;
return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault());
}
int WebThreeStubServer::eth_defaultBlock()
{
return client() ? client()->getDefault() : 0;
return client()->getDefault();
}
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)
{
if (!client())
return Json::Value(Json::arrayValue);
return toJson(client()->logs(_id));
}
Json::Value WebThreeStubServer::eth_logs(Json::Value const& _json)
{
if (!client())
return Json::Value(Json::arrayValue);
return toJson(client()->logs(toLogFilter(_json)));
}
@ -429,15 +414,12 @@ bool WebThreeStubServer::eth_listening()
bool WebThreeStubServer::eth_mining()
{
return client() ? client()->isMining() : false;
return client()->isMining();
}
int WebThreeStubServer::eth_newFilter(Json::Value const& _json)
{
unsigned ret = -1;
if (!client())
return ret;
// ret = client()->installWatch(toMessageFilter(_json));
ret = client()->installWatch(toLogFilter(_json));
return ret;
}
@ -445,8 +427,6 @@ int WebThreeStubServer::eth_newFilter(Json::Value const& _json)
int WebThreeStubServer::eth_newFilterString(std::string const& _filter)
{
unsigned ret = -1;
if (!client())
return ret;
if (_filter.compare("chain") == 0)
ret = client()->installWatch(dev::eth::ChainChangedFilter);
else if (_filter.compare("pending") == 0)
@ -528,7 +508,7 @@ std::string WebThreeStubServer::eth_solidity(std::string const& _code)
int WebThreeStubServer::eth_number()
{
return client() ? client()->number() + 1 : 0;
return client()->number() + 1;
}
int WebThreeStubServer::eth_peerCount()
@ -538,7 +518,6 @@ int WebThreeStubServer::eth_peerCount()
bool WebThreeStubServer::shh_post(Json::Value const& _json)
{
// cnote << this << m_ids;
shh::Message m = toMessage(_json);
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)
{
if (!client())
return false;
client()->setAddress(jsToAddress(_address));
return true;
}
bool WebThreeStubServer::eth_setDefaultBlock(int const& _block)
{
if (!client())
return false;
client()->setDefault(_block);
return true;
}
@ -596,9 +571,6 @@ bool WebThreeStubServer::eth_setListening(bool const& _listening)
bool WebThreeStubServer::eth_setMining(bool const& _mining)
{
if (!client())
return false;
if (_mining)
client()->startMining();
else
@ -646,22 +618,17 @@ bool WebThreeStubServer::shh_uninstallFilter(int const& _id)
std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage)
{
int block = 0;
return client() ? toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), block)) : "";
return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault()));
}
Json::Value WebThreeStubServer::eth_storageAt(string const& _address)
{
if (!client())
return Json::Value(Json::objectValue);
return toJson(client()->storageAt(jsToAddress(_address)));
}
std::string WebThreeStubServer::eth_transact(Json::Value const& _json)
{
std::string ret;
if (!client())
return ret;
TransactionSkeleton t = toTransaction(_json);
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)
{
if (!client())
return "";
return toJson(client()->transaction(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i)
{
if (!client())
return "";
return toJson(client()->transaction(client()->hashFromNumber(_number), _i));
}
Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i)
{
if (!client())
return "";
return toJson(client()->uncle(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i)
{
if (!client())
return "";
return toJson(client()->uncle(client()->hashFromNumber(_number), _i));
}
bool WebThreeStubServer::eth_uninstallFilter(int const& _id)
{
if (!client())
return false;
client()->uninstallWatch(_id);
return true;
}

74
mix/AppContext.cpp

@ -25,55 +25,53 @@
#include <QDebug>
#include <QMessageBox>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <libwebthree/WebThree.h>
#include <QStandardPaths>
#include <QFile>
#include <QDir>
#include <libdevcrypto/FileSystem.h>
#include <libsolidity/CompilerStack.h>
#include "KeyEventManager.h"
#include <libwebthree/WebThree.h>
#include "AppContext.h"
#include "CodeModel.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::solidity;
using namespace dev::mix;
AppContext* AppContext::Instance = nullptr;
const QString c_projectFileName = "project.mix";
AppContext::AppContext(QQmlApplicationEngine* _engine)
{
m_applicationEngine = std::unique_ptr<QQmlApplicationEngine>(_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_compiler = std::unique_ptr<CompilerStack>(new CompilerStack()); //TODO : to move in a codel model structure.
}
QQmlApplicationEngine* AppContext::appEngine()
{
return m_applicationEngine.get();
m_applicationEngine = _engine;
//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_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
}
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()
{
return m_compiler.get();
}
void AppContext::setApplicationContext(QQmlApplicationEngine* _engine)
QQmlApplicationEngine* AppContext::appEngine()
{
if (Instance == nullptr)
Instance = new AppContext(_engine);
return m_applicationEngine;
}
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);
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
#include <memory>
#include <QQmlApplicationEngine>
#include <libsolidity/CompilerStack.h>
#include <libwebthree/WebThree.h>
#include "KeyEventManager.h"
#include <QUrl>
#include <QObject>
class QQmlApplicationEngine;
namespace dev
{
class WebThreeDirect;
@ -47,42 +46,39 @@ namespace dev
namespace mix
{
class CodeModel;
/**
* @brief Provides access to application scope variable.
*/
class AppContext: public QObject
{
Q_OBJECT
public:
AppContext(QQmlApplicationEngine* _engine);
/// Get the current QQmlApplicationEngine instance.
static AppContext* getInstance() { return Instance; }
/// Renew QQMLApplicationEngine with a new instance.
static void setApplicationContext(QQmlApplicationEngine* _engine);
virtual ~AppContext();
/// Get the current QQMLApplicationEngine instance.
QQmlApplicationEngine* appEngine();
/// Initialize KeyEventManager (used to handle key pressed event).
void initKeyEventManager(QObject* _obj);
/// Get the current KeyEventManager instance.
KeyEventManager* getKeyEventManager();
/// Get the current Compiler instance (used to parse and compile contract code).
dev::solidity::CompilerStack* compiler();
/// Get code model
CodeModel* codeModel() { return m_codeModel.get(); }
/// Display an alert message.
void displayMessageDialog(QString _title, QString _message);
/// Load project settings
void loadProject();
signals:
void projectLoaded(QString const& _json);
private:
static AppContext* Instance;
std::unique_ptr<QQmlApplicationEngine> m_applicationEngine;
QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<KeyEventManager> m_keyEventManager;
std::unique_ptr<solidity::CompilerStack> m_compiler;
std::unique_ptr<CodeModel> m_codeModel;
public slots:
/// Delete the current instance when application quit.
void quitApplication() { delete Instance; }
/// Initialize components after the loading of the main QML view.
void resourceLoaded(QObject* _obj, QUrl _url) { Q_UNUSED(_url); initKeyEventManager(_obj); }
void quitApplication() {}
/// Write json to a settings file
void saveProject(QString const& _json);
};
}

222
mix/AssemblyDebuggerControl.cpp

@ -19,24 +19,39 @@
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QVariableDefinition.h>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QModelIndex>
#include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h>
#include "AssemblyDebuggerModel.h"
#include "AssemblyDebuggerControl.h"
#include "KeyEventManager.h"
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
#include "TransactionListModel.h"
#include "QContractDefinition.h"
#include "QVariableDeclaration.h"
#include "ContractCallDataEncoder.h"
#include "CodeModel.h"
using namespace dev::eth;
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<QVariableDefinitionList*>("QVariableDefinitionList*");
@ -44,14 +59,11 @@ AssemblyDebuggerControl::AssemblyDebuggerControl(QTextDocument* _doc): Extension
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
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_compilation = std::unique_ptr<ConstantCompilationModel>(new ConstantCompilationModel);
m_doc = _doc;
_context->appEngine()->rootContext()->setContextProperty("debugModel", this);
}
QString AssemblyDebuggerControl::contentUrl() const
@ -61,111 +73,139 @@ QString AssemblyDebuggerControl::contentUrl() const
QString AssemblyDebuggerControl::title() const
{
return QApplication::tr("debugger");
return QApplication::tr("Debugger");
}
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)
{
QtConcurrent::run([this]()
{
deployContract(m_doc->toPlainText());
});
}
else if (_key == Qt::Key_F6)
executeSequence(std::vector<TransactionSettings>(), 0);
}
void AssemblyDebuggerControl::debugState(QVariantMap _state)
{
u256 balance = fromQString(_state.value("balance").toString());
QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence;
for (auto const& t: transactions)
{
m_modelDebugger->resetState();
AppContext::getInstance()->displayMessageDialog(QApplication::tr("State status"), QApplication::tr("State reseted ... need to redeploy contract"));
QVariantMap transaction = t.toMap();
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 (!compilerRes.success)
AppContext::getInstance()->displayMessageDialog("debugger","compilation failed");
else
if (m_running)
throw (std::logic_error("debugging already running"));
auto compilerRes = m_ctx->codeModel()->code();
std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
m_running = true;
emit runStarted();
emit stateChanged();
//run sequence
QtConcurrent::run([=]()
{
ContractCallDataEncoder c;
std::shared_ptr<QContractDefinition> contractDef = QContractDefinition::Contract(m_doc->toPlainText());
QFunctionDefinition* f = nullptr;
for (int k = 0; k < contractDef->functions().size(); k++)
try
{
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);
break;
f = nullptr;
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)
AppContext::getInstance()->displayMessageDialog(QApplication::tr("debugger"), QApplication::tr("function not found. Please redeploy this contract."));
else
{
c.encode(f->index());
for (int k = 0; k < f->parameters().size(); k++)
//run contract creation first
m_modelDebugger->resetState(_balance);
DebuggingContent debuggingContent = m_modelDebugger->deployContract(contractCode);
Address address = debuggingContent.contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
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);
c.encode(var, _tr.parameterValues[var->name()]);
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes()));
s->setState(debuggingContent.machineStates.at(i));
wStates.append(s);
}
DebuggingContent debuggingContent = m_modelDebugger->callContract(_contract, c.encodedData(), _tr);
debuggingContent.returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
finalizeExecution(debuggingContent);
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
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)
{
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
for(int i = 0; i < _debuggingContent.machineStates.size(); i++)
{
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);
catch(std::exception const& e)
{
emit runFailed(e.what());
}
m_running = false;
emit stateChanged();
});
}
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);
if (_success)
{
m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
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("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
this->addContentOn(this);
}
else
m_ctx->displayMessageDialog(QApplication::tr("debugger"), QApplication::tr("compilation failed"));
m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
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("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
this->addContentOn(this);
}
void AssemblyDebuggerControl::runTransaction(TransactionSettings const& _tr)
void AssemblyDebuggerControl::showDebugError(QString const& _error)
{
QtConcurrent::run([this, _tr]()
{
callContract(_tr, m_previousDebugResult.contractAddress);
});
m_ctx->displayMessageDialog(QApplication::tr("Debugger"), _error);
}

52
mix/AssemblyDebuggerControl.h

@ -19,25 +19,18 @@
#pragma once
#include <atomic>
#include <QKeySequence>
#include <QTextDocument>
#include "Extension.h"
#include "ConstantCompilationModel.h"
#include "TransactionListModel.h"
#include "AssemblyDebuggerModel.h"
#include "AppContext.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
enum DebuggingStatusResult
{
Ok,
Compilationfailed
};
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(DebuggingStatusResult)
Q_DECLARE_METATYPE(dev::mix::DebuggingContent)
class AppContext;
namespace dev
{
namespace mix
@ -51,33 +44,46 @@ class AssemblyDebuggerControl: public Extension
Q_OBJECT
public:
AssemblyDebuggerControl(QTextDocument* _doc);
AssemblyDebuggerControl(AppContext* _context);
~AssemblyDebuggerControl() {}
void start() const override;
QString title() const override;
QString contentUrl() const override;
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
private:
void deployContract(QString _source);
void callContract(TransactionSettings _tr, Address _contract);
void finalizeExecution(DebuggingContent _content);
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
std::unique_ptr<AssemblyDebuggerModel> m_modelDebugger;
std::unique_ptr<ConstantCompilationModel> m_compilation;
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;
std::atomic<bool> m_running;
public slots:
/// Handle key pressed. F5 deploy contract - F6 reset state.
void keyPressed(int);
/// Run the contract constructor and show debugger window.
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.
void updateGUI(bool _success, DebuggingStatusResult const& _reason, QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// Run the given transaction.
void runTransaction(TransactionSettings const& _tr);
void showDebugger(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
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.
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/Transaction.h>
#include <libethereum/ExtVM.h>
#include "AppContext.h"
#include "TransactionListModel.h"
#include "AssemblyDebuggerModel.h"
#include "ConstantCompilationModel.h"
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
namespace dev
{
namespace mix
{
AssemblyDebuggerModel::AssemblyDebuggerModel():
m_userAccount(KeyPair::create()),
m_baseState(Address(), m_overlayDB, BaseState::Empty)
m_userAccount(KeyPair::create())
{
m_baseState.addBalance(m_userAccount.address(), 10000000 * ether);
m_executiveState = m_baseState;
m_currentExecution = std::unique_ptr<Executive>(new Executive(m_executiveState, LastHashes(), 0));
resetState(10000000 * ether);
}
DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& _rawTransaction)
{
QList<DebuggingState> machineStates;
m_currentExecution.reset(new Executive(m_executiveState, LastHashes(), 0));
m_currentExecution->setup(_rawTransaction);
eth::Executive execution(m_executiveState, LastHashes(), 0);
execution.setup(_rawTransaction);
std::vector<DebuggingState const*> levels;
bytes code;
bytesConstRef data;
@ -72,12 +72,12 @@ DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const&
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
m_currentExecution->go(onOp);
m_currentExecution->finalize();
execution.go(onOp);
execution.finalize();
m_executiveState.completeMine();
DebuggingContent d;
d.returnValue = m_currentExecution->out().toVector();
d.returnValue = execution.out().toVector();
d.machineStates = machineStates;
d.executionCode = code;
d.executionData = data;
@ -91,7 +91,7 @@ DebuggingContent AssemblyDebuggerModel::deployContract(bytes const& _code)
u256 gasPrice = 10000000000000;
u256 gas = 1000000;
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();
dev::bytesConstRef bytesRef = &b;
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)
{
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();
dev::bytesConstRef bytesRef = &b;
DebuggingContent d = executeTransaction(bytesRef);
@ -110,8 +110,11 @@ DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, b
return d;
}
void AssemblyDebuggerModel::resetState()
void AssemblyDebuggerModel::resetState(u256 _balance)
{
// Reset the state back to our clean premine.
m_executiveState = m_baseState;
m_executiveState = eth::State(Address(), m_overlayDB, BaseState::Empty);
m_executiveState.addBalance(m_userAccount.address(), _balance);
}
}
}

26
mix/AssemblyDebuggerModel.h

@ -26,13 +26,31 @@
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include "DebuggingStateWrapper.h"
#include "TransactionListModel.h"
namespace dev
{
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.
*/
@ -44,15 +62,13 @@ public:
DebuggingContent callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
/// Deploy the contract described by _code.
DebuggingContent deployContract(bytes const& _code);
/// Reset state to the base state.
void resetState();
/// Reset state to the empty state with given balance.
void resetState(u256 _balance);
private:
KeyPair m_userAccount;
OverlayDB m_overlayDB;
eth::State m_baseState;
eth::State m_executiveState;
std::unique_ptr<eth::Executive> m_currentExecution;
DebuggingContent executeTransaction(dev::bytesConstRef const& _rawTransaction);
};

72
mix/CodeEditorExtensionManager.cpp

@ -25,14 +25,22 @@
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickTextDocument>
#include <libevm/VM.h>
#include "ConstantCompilationControl.h"
#include "AssemblyDebuggerControl.h"
#include "TransactionListView.h"
#include "StateListView.h"
#include "AppContext.h"
#include "MixApplication.h"
#include "CodeModel.h"
#include "CodeHighlighter.h"
#include "CodeEditorExtensionManager.h"
using namespace dev::mix;
CodeEditorExtensionManager::CodeEditorExtensionManager():
m_appContext(static_cast<MixApplication*>(QApplication::instance())->context())
{
}
CodeEditorExtensionManager::~CodeEditorExtensionManager()
{
m_features.clear();
@ -42,40 +50,30 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
{
if (!_editor)
return;
try
QVariant doc = _editor->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>())
{
QVariant doc = _editor->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>())
QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>();
if (qqdoc)
{
QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>();
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());
}
}
m_doc = qqdoc->textDocument();
}
}
catch (...)
{
qDebug() << "unable to load editor: ";
}
}
void CodeEditorExtensionManager::initExtensions()
{
initExtension(std::make_shared<ConstantCompilationControl>(m_doc));
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_doc);
std::shared_ptr<TransactionListView> tr = std::make_shared<TransactionListView>(m_doc);
QObject::connect(tr->model(), &TransactionListModel::transactionStarted, debug.get(), &AssemblyDebuggerControl::runTransaction);
std::shared_ptr<ConstantCompilationControl> output = std::make_shared<ConstantCompilationControl>(m_appContext);
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
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(tr);
initExtension(stateList);
}
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
@ -103,6 +101,26 @@ void CodeEditorExtensionManager::setEditor(QQuickItem* _editor)
{
this->loadEditor(_editor);
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)

11
mix/CodeEditorExtensionManager.h

@ -22,6 +22,7 @@
#pragma once
#include <memory>
#include <QQuickItem>
#include <QTextDocument>
#include <QVector>
@ -32,6 +33,9 @@ namespace dev
namespace mix
{
class AppContext;
/**
* @brief Init and provides connection between extensions.
*/
@ -44,7 +48,7 @@ class CodeEditorExtensionManager: public QObject
Q_PROPERTY(QQuickItem* rightTabView MEMBER m_rightTabView WRITE setRightTabView)
public:
CodeEditorExtensionManager() {}
CodeEditorExtensionManager();
~CodeEditorExtensionManager();
/// Initialize all extensions.
void initExtensions();
@ -57,12 +61,17 @@ public:
/// Set current right tab view.
void setRightTabView(QQuickItem*);
private slots:
void onCodeChange();
void applyCodeHighlight();
private:
QQuickItem* m_editor;
QVector<std::shared_ptr<Extension>> m_features;
QQuickItem* m_tabView;
QQuickItem* m_rightTabView;
QTextDocument* m_doc;
AppContext* m_appContext;
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 <QDebug>
#include "ConstantCompilationControl.h"
#include "ConstantCompilationModel.h"
#include "QContractDefinition.h"
#include "AppContext.h"
#include "CodeModel.h"
using namespace dev::mix;
ConstantCompilationControl::ConstantCompilationControl(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::Tab)
ConstantCompilationControl::ConstantCompilationControl(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::Tab)
{
m_editor = _doc;
m_compilationModel = std::unique_ptr<ConstantCompilationModel>(new ConstantCompilationModel());
connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update);
connect(_context->codeModel(), &CodeModel::compilationComplete, this, &ConstantCompilationControl::update);
}
QString ConstantCompilationControl::contentUrl() const
@ -50,18 +52,25 @@ QString ConstantCompilationControl::title() 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", "");
if (codeContent.isEmpty())
resetOutPut();
auto result = m_ctx->codeModel()->code();
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
{
CompilerResult res = m_compilationModel->compile(m_editor->toPlainText().replace("\t", " "));
writeOutPut(res);
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", result->compilerMessage());
}
}
@ -73,20 +82,12 @@ void ConstantCompilationControl::resetOutPut()
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* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (_res.success)
{
status->setProperty("text", "succeeded");
status->setProperty("color", "green");
content->setProperty("text", _res.hexCode);
}
else
{
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", _res.comment);
}
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", _error);
}

11
mix/ConstantCompilationControl.h

@ -19,8 +19,6 @@
#pragma once
#include <QTextDocument>
#include "ConstantCompilationModel.h"
#include "Extension.h"
namespace dev
@ -36,21 +34,18 @@ class ConstantCompilationControl: public Extension
Q_OBJECT
public:
ConstantCompilationControl(QTextDocument* _doc);
ConstantCompilationControl(AppContext* _appContext);
~ConstantCompilationControl() {}
void start() const override;
QString title() const override;
QString contentUrl() const override;
private:
QTextDocument* m_editor;
std::unique_ptr<ConstantCompilationModel> m_compilationModel;
void writeOutPut(CompilerResult const& _res);
void resetOutPut();
public slots:
/// Compile text editor content.
void compile();
void update();
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());
}
void ContractCallDataEncoder::encode(QVariableDeclaration* _dec, bool _value)
void ContractCallDataEncoder::encode(QVariableDeclaration const* _dec, bool _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());
bytes data = padded(jsToBytes(_value.toStdString()), padding);
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());
std::ostringstream s;

6
mix/ContractCallDataEncoder.h

@ -38,11 +38,11 @@ class ContractCallDataEncoder
public:
ContractCallDataEncoder() {}
/// 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.
void encode(QVariableDeclaration* _dec, u256 _value);
void encode(QVariableDeclaration const* _dec, u256 _value);
/// 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.
void encode(int _functionIndex);
/// Decode variable in order to be sent to QML view.

21
mix/Extension.cpp

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

11
mix/Extension.h

@ -21,13 +21,16 @@
#include <QApplication>
#include <QQmlComponent>
#include "AppContext.h"
class QQmlApplicationEngine;
namespace dev
{
namespace mix
{
class AppContext;
enum ExtensionDisplayBehavior
{
Tab,
@ -41,8 +44,8 @@ class Extension: public QObject
Q_OBJECT
public:
Extension();
Extension(ExtensionDisplayBehavior _displayBehavior);
Extension(AppContext* _context);
Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior);
/// Return the QML url of the view to display.
virtual QString contentUrl() const { return ""; }
/// Return the title of this extension.
@ -65,7 +68,7 @@ protected:
QQmlApplicationEngine* m_appEngine;
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 <QQmlApplicationEngine>
#include "CodeEditorExtensionManager.h"
#include "MixApplication.h"
#include "AppContext.h"
#include <QMenuBar>
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
#include <memory>
#include <QApplication>
class QQmlApplicationEngine;
namespace dev
{
namespace mix
{
class AppContext;
class MixApplication: public QApplication
{
Q_OBJECT
public:
MixApplication(int _argc, char* _argv[]);
virtual ~MixApplication() {}
virtual bool notify(QObject* _receiver, QEvent* _event);
virtual ~MixApplication();
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::mix;
std::shared_ptr<QContractDefinition> QContractDefinition::Contract(QString _source)
QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{
CompilerStack* comp = AppContext::getInstance()->compiler();
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();
std::vector<FunctionDefinition const*> functions = _contract->getInterfaceFunctions();
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
#include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h>
#include "QFunctionDefinition.h"
#include "QBasicNodeDefinition.h"
@ -34,20 +35,18 @@ namespace mix
class QContractDefinition: public QBasicNodeDefinition
{
Q_OBJECT
Q_PROPERTY(QList<QFunctionDefinition*> functions READ functions)
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
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.
QList<QFunctionDefinition*> functions() const { return m_functions; }
/// Get the description (functions, parameters, return parameters, ...) of the contract describes by _code.
static std::shared_ptr<QContractDefinition> Contract(QString _code);
QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
private:
solidity::ContractDefinition const* m_contract;
QList<QFunctionDefinition*> m_functions;
void initQFunctions();
};
}
}

6
mix/QFunctionDefinition.cpp

@ -25,13 +25,13 @@
using namespace dev::solidity;
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++)
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++)
m_returnParameters.append(new QVariableDeclaration(returnParameters.at(i).get()));
}

13
mix/QFunctionDefinition.h

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

11
mix/QVariableDeclaration.h

@ -35,15 +35,12 @@ class QVariableDeclaration: public QBasicNodeDefinition
Q_PROPERTY(QString type READ type CONSTANT)
public:
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_variable(_v) {}
/// Get the type of this variable.
QString type() const { return QString::fromStdString(m_variable->getType()->toString()); }
QVariableDeclaration() {}
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {}
QString type() const { return m_type; }
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
{
Q_OBJECT
Q_PROPERTY(QString value READ value 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
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionListView.cpp
/** @file StateListView.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
@ -25,32 +25,24 @@
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include "TransactionListView.h"
#include "TransactionListModel.h"
using namespace dev::mix;
#include "StateListView.h"
TransactionListView::TransactionListView(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::RightTab)
{
m_editor = _doc;
m_model.reset(new TransactionListModel(this, _doc));
m_appEngine->rootContext()->setContextProperty("transactionListModel", m_model.get());
}
using namespace dev::mix;
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
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionListView.h
/** @file StateListView.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
@ -19,6 +19,7 @@
#pragma once
#include <memory>
#include <QTextDocument>
#include "Extension.h"
@ -27,26 +28,16 @@ namespace dev
namespace mix
{
class TransactionListModel;
/// Transactions list control
/// @todo This should be moved into state as a sequence
class TransactionListView: public Extension
/// State list control
class StateListView: public Extension
{
Q_OBJECT
public:
TransactionListView(QTextDocument*);
~TransactionListView();
StateListView(AppContext* _context);
void start() const override;
QString title() 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.
*/
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include "CodeEditorExtensionManager.h"
#include "AppContext.h"
#include "MixApplication.h"
using namespace dev::mix;
int main(int _argc, char *_argv[])
int main(int _argc, char* _argv[])
{
QApplication 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"));
MixApplication app(_argc, _argv);
return app.exec();
}

3
mix/qml.qrc

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

73
mix/qml/MainContent.qml

@ -29,21 +29,64 @@ Rectangle {
id: contentView
width: parent.width
height: parent.height * 0.7
TextArea {
id: codeEditor
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;
}
}
}
Item {
anchors.fill: parent
Rectangle {
id: lineColumn
property int rowHeight: codeEditor.font.pixelSize + 3
color: "#202020"
width: 50
height: parent.height
Column {
y: -codeEditor.flickableItem.contentY + 4
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 {
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 {
modality: Qt.WindowModal
width:640
height:480
visible: false
function open()
{
visible = true;
}
function close()
{
visible = false;
}
property alias focus : titleField.focus
property alias transactionTitle : titleField.text
property int transactionIndex
property alias transactionParams : paramsModel;
property alias gas : gasField.text;
property alias gasPrice : gasPriceField.text;
property alias transactionValue : valueField.text;
property alias functionId : functionComboBox.currentText;
property var model;
property var itemParams;
signal accepted;
function reset(index, m) {
model = m;
var item = model.getItem(index);
function open(index, item) {
transactionIndex = index;
transactionTitle = item.title;
gas = item.gas;
gasPrice = item.gasPrice;
transactionValue = item.value;
var functionId = item.functionId;
itemParams = item.parameters !== undefined ? item.parameters : {};
functionsModel.clear();
var functionIndex = -1;
var functions = model.getFunctions();
var functions = codeModel.code.contract.functions;
for (var f = 0; f < functions.length; f++) {
functionsModel.append({ text: functions[f] });
if (functions[f] === item.functionId)
functionsModel.append({ text: functions[f].name });
if (functions[f].name === item.functionId)
functionIndex = f;
}
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused funtion
functionComboBox.currentIndex = functionIndex;
loadParameters();
visible = true;
valueField.focus = true;
}
function loadParameters() {
@ -57,13 +49,36 @@ Window {
return;
paramsModel.clear();
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++) {
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 {
id: dialogContent
columns: 2
@ -72,19 +87,9 @@ Window {
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
}
Label {
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
Layout.fillWidth: true
@ -98,6 +103,7 @@ Window {
loadParameters();
}
}
Label {
text: qsTr("Value")
}
@ -195,7 +201,8 @@ Window {
Connections {
target: loaderEditor.item
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

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.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Window 2.1
import CodeEditorExtensionManager 1.0
ApplicationWindow {
@ -23,6 +23,11 @@ ApplicationWindow {
onTriggered: Qt.quit();
}
}
Menu {
title: qsTr("Debug")
MenuItem { action: debugRunAction }
MenuItem { action: debugResetStateAction }
}
}
Component.onCompleted: {
setX(Screen.width / 2 - width / 2);
@ -41,4 +46,21 @@ ApplicationWindow {
objectName: "alertMessageDialog"
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_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)
{
Scanner scanner(CharStream("/***/"));

Loading…
Cancel
Save