diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 93d73b991..a41743d0b 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -49,6 +49,7 @@ void BasicBlock::LocalStack::push(llvm::Value* _value) assert(_value->getType() == Type::Word); m_bblock.m_currentStack.push_back(_value); m_bblock.m_tosOffset += 1; + m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size()); } llvm::Value* BasicBlock::LocalStack::pop() diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7469b7b69..5e19235a7 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -33,6 +33,9 @@ public: /// @param _index Index of value to be swaped. Must be > 0. void swap(size_t _index); + size_t getMaxSize() const { return m_maxSize; } + int getDiff() const { return m_bblock.m_tosOffset; } + private: LocalStack(BasicBlock& _owner); LocalStack(LocalStack const&) = delete; @@ -49,6 +52,7 @@ public: private: BasicBlock& m_bblock; + size_t m_maxSize = 0; ///< Max size reached by the stack. }; explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index d3cda1098..47a6386e9 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,36 @@ void Cache::clear() fs::remove(it->path()); } +void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache) +{ + // TODO: Cache dir should be in one place + using namespace llvm::sys; + llvm::SmallString<256> cachePath; + path::system_temp_directory(false, cachePath); + path::append(cachePath, "evm_objs"); + + // Disable listener + auto listener = g_listener; + g_listener = nullptr; + + std::error_code err; + for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) + { + auto name = it->path().substr(cachePath.size() + 1); + if (auto module = getObject(name)) + { + DLOG(cache) << "Preload: " << name << "\n"; + _ee.addModule(module.get()); + module.release(); + auto addr = _ee.getFunctionAddress(name); + assert(addr); + _funcCache[std::move(name)] = addr; + } + } + + g_listener = listener; +} + std::unique_ptr Cache::getObject(std::string const& id) { if (g_mode != CacheMode::on && g_mode != CacheMode::read) diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index b0d26d080..f6a0a3400 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -1,9 +1,15 @@ #pragma once #include +#include #include +namespace llvm +{ + class ExecutionEngine; +} + namespace dev { namespace eth @@ -18,7 +24,8 @@ enum class CacheMode off, read, write, - clear + clear, + preload }; class ObjectCache : public llvm::ObjectCache @@ -43,6 +50,9 @@ public: /// Clears cache storage static void clear(); + + /// Loads all available cached objects to ExecutionEngine + static void preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache); }; } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index be3670874..1e1f8fe93 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -838,6 +838,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti // Block may have no terminator if the next instruction is a jump destination. if (!_basicBlock.llvm()->getTerminator()) m_builder.CreateBr(_nextBasicBlock); + + m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); + _runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff()); } diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index ce1f530d7..0ed4a65b5 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -76,6 +76,7 @@ cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), + clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), clEnumValEnd)}; cl::opt g_stats{"st", cl::desc{"Statistics"}}; cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; @@ -111,8 +112,15 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) std::unique_ptr listener{new ExecStats}; listener->stateChanged(ExecState::Started); + bool preloadCache = g_cache == CacheMode::preload; + if (preloadCache) + g_cache = CacheMode::on; + + // TODO: Do not pseudo-init the cache every time auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; + static std::unordered_map funcCache; + static std::unique_ptr ee; if (!ee) { @@ -138,6 +146,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) return ReturnCode::LLVMConfigError; module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module ee->setObjectCache(objectCache); + + if (preloadCache) + Cache::preload(*ee, funcCache); } static StatsCollector statsCollector; @@ -146,10 +157,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) m_runtime.init(_data, _env); EntryFuncPtr entryFuncPtr = nullptr; - static std::unordered_map funcCache; auto it = funcCache.find(mainFuncName); if (it != funcCache.end()) - entryFuncPtr = it->second; + entryFuncPtr = (EntryFuncPtr) it->second; if (!entryFuncPtr) { @@ -177,7 +187,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - funcCache[mainFuncName] = entryFuncPtr; + if (it == funcCache.end()) + funcCache[mainFuncName] = (uint64_t) entryFuncPtr; listener->stateChanged(ExecState::Execution); auto returnCode = entryFuncPtr(&m_runtime); diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index d1ccaea8a..dc7bc24a3 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -78,7 +78,7 @@ llvm::Twine getName(RuntimeData::Index _index) case RuntimeData::CodeSize: return "code"; case RuntimeData::CallDataSize: return "callDataSize"; case RuntimeData::Gas: return "gas"; - case RuntimeData::Number: return "number"; + case RuntimeData::Number: return "number"; case RuntimeData::Timestamp: return "timestamp"; } } @@ -101,6 +101,48 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB assert(m_memPtr->getType() == Array::getType()->getPointerTo()); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); assert(m_envPtr->getType() == Type::EnvPtr); + + m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); + m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); + + llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; + m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); + m_checkStackLimit->setDoesNotThrow(); + m_checkStackLimit->setDoesNotCapture(1); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit); + auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit); + + auto currSizePtr = &m_checkStackLimit->getArgumentList().front(); + currSizePtr->setName("currSize"); + auto max = currSizePtr->getNextNode(); + max->setName("max"); + auto diff = max->getNextNode(); + diff->setName("diff"); + auto jmpBuf = diff->getNextNode(); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(checkBB); + auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); + auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize"); + auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok"); + m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); + + m_builder.SetInsertPoint(updateBB); + auto newSize = m_builder.CreateNSWAdd(currSize, diff); + m_builder.CreateStore(newSize, currSizePtr); + m_builder.CreateRetVoid(); + + m_builder.SetInsertPoint(outOfStackBB); + abort(jmpBuf); + m_builder.CreateUnreachable(); +} + +void RuntimeManager::checkStackLimit(size_t _max, int _diff) +{ + createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); } llvm::Value* RuntimeManager::getRuntimePtr() diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index eb8dadf6f..c430a1098 100644 --- a/evmjit/libevmjit/RuntimeManager.h +++ b/evmjit/libevmjit/RuntimeManager.h @@ -48,6 +48,8 @@ public: static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeDataType(); + void checkStackLimit(size_t _max, int _diff); + private: llvm::Value* getPtr(RuntimeData::Index _index); void set(RuntimeData::Index _index, llvm::Value* _value); @@ -59,6 +61,9 @@ private: llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; + llvm::Value* m_stackSize = nullptr; + llvm::Function* m_checkStackLimit = nullptr; + code_iterator m_codeBegin = {}; code_iterator m_codeEnd = {}; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 457676fba..6f95bd529 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -296,6 +296,11 @@ bool WebThreeStubServerBase::net_listening() return network()->isNetworkStarted(); } +string WebThreeStubServerBase::eth_protocolVersion() +{ + return toJS(eth::c_protocolVersion); +} + string WebThreeStubServerBase::eth_coinbase() { return toJS(client()->address()); diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 40265ac10..b57a54c87 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -77,6 +77,7 @@ public: virtual std::string net_peerCount(); virtual bool net_listening(); + virtual std::string eth_protocolVersion(); virtual std::string eth_coinbase(); virtual bool eth_mining(); virtual std::string eth_gasPrice(); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 516817d5c..6cc5de3e6 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -17,6 +17,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("net_version", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_versionI); this->bindAndAddMethod(jsonrpc::Procedure("net_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_peerCountI); this->bindAndAddMethod(jsonrpc::Procedure("net_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::net_listeningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_protocolVersion", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_protocolVersionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); this->bindAndAddMethod(jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI); this->bindAndAddMethod(jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI); @@ -92,6 +93,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServernet_listening(); } + inline virtual void eth_protocolVersionI(const Json::Value &request, Json::Value &response) + { + (void)request; + response = this->eth_protocolVersion(); + } inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response) { (void)request; @@ -302,6 +308,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer const& _seque emit runStateChanged(); //run sequence - QtConcurrent::run([=]() + m_runFuture = QtConcurrent::run([=]() { try { diff --git a/mix/ClientModel.h b/mix/ClientModel.h index a5d89d859..9b35af4fd 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "MachineStates.h" namespace dev @@ -203,6 +204,7 @@ private: std::atomic m_running; std::atomic m_mining; + QFuture m_runFuture; std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 028f1cb0b..80929dc49 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef ETH_HAVE_WEBENGINE #include #endif @@ -37,21 +38,49 @@ using namespace dev::mix; +ApplicationService::ApplicationService() +{ +#ifdef ETH_HAVE_WEBENGINE + QtWebEngine::initialize(); +#endif + QFont f; + m_systemPointSize = f.pointSize(); +} + MixApplication::MixApplication(int& _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) { + m_engine->load(QUrl("qrc:/qml/Application.qml")); + if (!m_engine->rootObjects().empty()) + { + QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); + if (window) + window->setIcon(QIcon(":/res/mix_256x256x32.png")); + } +} + + +void MixApplication::initialize() +{ +#if __linux + //work around ubuntu appmenu-qt5 bug + //https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853 + putenv((char*)"QT_QPA_PLATFORMTHEME="); + putenv((char*)"QSG_RENDER_LOOP=threaded"); +#endif +#if (defined(_WIN32) || defined(_WIN64)) + if (!getenv("OPENSSL_CONF")) + putenv((char*)"OPENSSL_CONF=c:\\"); +#endif + setOrganizationName(tr("Ethereum")); setOrganizationDomain(tr("ethereum.org")); setApplicationName(tr("Mix")); setApplicationVersion("0.1"); -#ifdef ETH_HAVE_WEBENGINE - QtWebEngine::initialize(); -#endif - QFont f; - m_engine->rootContext()->setContextProperty("systemPointSize", f.pointSize()); qmlRegisterType("org.ethereum.qml.CodeModel", 1, 0, "CodeModel"); qmlRegisterType("org.ethereum.qml.ClientModel", 1, 0, "ClientModel"); + qmlRegisterType("org.ethereum.qml.ApplicationService", 1, 0, "ApplicationService"); qmlRegisterType("org.ethereum.qml.FileIo", 1, 0, "FileIo"); qmlRegisterType("org.ethereum.qml.QEther", 1, 0, "QEther"); qmlRegisterType("org.ethereum.qml.QBigInt", 1, 0, "QBigInt"); @@ -63,10 +92,7 @@ MixApplication::MixApplication(int& _argc, char* _argv[]): qmlRegisterType("HttpServer", 1, 0, "HttpServer"); qRegisterMetaType("CodeModel*"); qRegisterMetaType("ClientModel*"); - - m_engine->load(QUrl("qrc:/qml/main.qml")); - QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); - window->setIcon(QIcon(":/res/mix_256x256x32.png")); + qRegisterMetaType("ClientModel*"); } MixApplication::~MixApplication() diff --git a/mix/MixApplication.h b/mix/MixApplication.h index 49a6e0047..9756f1b89 100644 --- a/mix/MixApplication.h +++ b/mix/MixApplication.h @@ -33,12 +33,33 @@ namespace dev namespace mix { +class ApplicationService: public QObject +{ + Q_OBJECT + Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT) + Q_PROPERTY(bool haveWebEngine READ haveWebEngine CONSTANT) + +public: + ApplicationService(); + int systemPointSize() const { return m_systemPointSize; } +#ifdef ETH_HAVE_WEBENGINE + bool haveWebEngine() const { return true; } +#else + bool haveWebEngine() const { return false; } +#endif + +private: + int m_systemPointSize = 0; +}; + + class MixApplication: public QApplication { Q_OBJECT public: MixApplication(int& _argc, char* _argv[]); + static void initialize(); virtual ~MixApplication(); QQmlApplicationEngine* engine() { return m_engine.get(); } diff --git a/mix/QBasicNodeDefinition.cpp b/mix/QBasicNodeDefinition.cpp new file mode 100644 index 000000000..8905caef6 --- /dev/null +++ b/mix/QBasicNodeDefinition.cpp @@ -0,0 +1,41 @@ +/* + 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 . +*/ +/** @file QBasicNodeDefinition.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include "QBasicNodeDefinition.h" +#include + +namespace dev +{ +namespace mix +{ + +QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): + QObject(_parent), m_name(QString::fromStdString(_d->getName())) +{ +} + +QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, std::string const& _name): + QObject(_parent), m_name(QString::fromStdString(_name)) +{ +} + +} +} diff --git a/mix/QBasicNodeDefinition.h b/mix/QBasicNodeDefinition.h index 42f8ac15d..eb81d4f85 100644 --- a/mix/QBasicNodeDefinition.h +++ b/mix/QBasicNodeDefinition.h @@ -21,11 +21,17 @@ #pragma once +#include #include -#include namespace dev { + +namespace solidity +{ +class Declaration; +} + namespace mix { @@ -37,8 +43,8 @@ class QBasicNodeDefinition: public QObject public: QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {} ~QBasicNodeDefinition() {} - QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): QObject(_parent), m_name(QString::fromStdString(_d->getName())) {} - QBasicNodeDefinition(QObject* _parent, std::string const& _name): QObject(_parent), m_name(QString::fromStdString(_name)) {} + QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d); + QBasicNodeDefinition(QObject* _parent, std::string const& _name); /// Get the name of the node. QString name() const { return m_name; } diff --git a/mix/QVariableDeclaration.cpp b/mix/QVariableDeclaration.cpp index 391d26c04..3bbb0d523 100644 --- a/mix/QVariableDeclaration.cpp +++ b/mix/QVariableDeclaration.cpp @@ -21,6 +21,7 @@ */ #include "QVariableDeclaration.h" +#include #include "CodeModel.h" namespace dev diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index cf9345061..4309550b2 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -26,14 +26,15 @@ #pragma once +namespace dev +{ + namespace solidity { class Type; class VariableDeclaration; } -namespace dev -{ namespace mix { diff --git a/mix/QVariableDefinition.cpp b/mix/QVariableDefinition.cpp new file mode 100644 index 000000000..4553ffad6 --- /dev/null +++ b/mix/QVariableDefinition.cpp @@ -0,0 +1,37 @@ +/* + 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 . +*/ +/** @file QVariableDefinition.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include "QVariableDefinition.h" +#include + +namespace dev +{ +namespace mix +{ + +QString QVariableDefinition::encodeValueAsString() +{ + return QString::fromStdString(dev::toHex(encodeValue())); +} + +} +} + diff --git a/mix/QVariableDefinition.h b/mix/QVariableDefinition.h index 70a663ee3..e6c38f971 100644 --- a/mix/QVariableDefinition.h +++ b/mix/QVariableDefinition.h @@ -21,14 +21,14 @@ #pragma once -#include -#include "QBigInt.h" -#include "QVariableDeclaration.h" +#include +#include namespace dev { namespace mix { +class QVariableDeclaration; class QVariableDefinition: public QObject { @@ -54,7 +54,7 @@ public: /// Decode the return value @a _rawValue. virtual void decodeValue(dev::bytes const& _rawValue) = 0; /// returns String representation of the encoded value. - Q_INVOKABLE QString encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); } + Q_INVOKABLE QString encodeValueAsString(); protected: QString m_value; diff --git a/mix/main.cpp b/mix/main.cpp index 798520e39..78b5261ac 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -22,27 +22,17 @@ #include #include +#include +#include #include "MixApplication.h" -#include "Exceptions.h" + using namespace dev::mix; int main(int _argc, char* _argv[]) { -#ifdef ETH_HAVE_WEBENGINE - Q_INIT_RESOURCE(js); -#endif -#if __linux - //work around ubuntu appmenu-qt5 bug - //https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853 - putenv((char*)"QT_QPA_PLATFORMTHEME="); - putenv((char*)"QSG_RENDER_LOOP=threaded"); -#endif -#if (defined(_WIN32) || defined(_WIN64)) - if (!getenv("OPENSSL_CONF")) - putenv((char*)"OPENSSL_CONF=c:\\"); -#endif try { + MixApplication::initialize(); MixApplication app(_argc, _argv); return app.exec(); } diff --git a/mix/qml.qrc b/mix/qml.qrc index 5ceaaced6..47d9d3bb9 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -1,6 +1,7 @@ qml/AlertMessageDialog.qml + qml/Application.qml qml/BasicMessage.qml qml/BigIntValue.qml qml/CallStack.qml @@ -47,7 +48,8 @@ qml/StatusPaneStyle.qml qml/StepActionImage.qml qml/StorageView.qml - qml/StructView.qml + qml/StatesComboBox.qml + qml/StructView.qml qml/Style.qml qml/TabStyle.qml qml/TransactionDialog.qml @@ -59,8 +61,5 @@ qml/js/ProjectModel.js qml/js/QEtherHelper.js qml/js/TransactionHelper.js - qml/main.qml - qml/qmldir - qml/StatesComboBox.qml diff --git a/mix/qml/main.qml b/mix/qml/Application.qml similarity index 95% rename from mix/qml/main.qml rename to mix/qml/Application.qml index 5544d4639..b7a065cdf 100644 --- a/mix/qml/main.qml +++ b/mix/qml/Application.qml @@ -11,6 +11,7 @@ import org.ethereum.qml.CodeModel 1.0 import org.ethereum.qml.ClientModel 1.0 import org.ethereum.qml.FileIo 1.0 import org.ethereum.qml.Clipboard 1.0 +import org.ethereum.qml.ApplicationService 1.0 ApplicationWindow { @@ -22,6 +23,16 @@ ApplicationWindow { minimumWidth: 400 minimumHeight: 300 title: qsTr("Mix") + property alias systemPointSize: appService.systemPointSize; + property alias mainContent: mainContent; + property alias codeModel: codeModel; + property alias clientModel: clientModel; + property alias projectModel: projectModel; + property alias appService: appService; + + ApplicationService { + id: appService + } CodeModel { id: codeModel @@ -44,6 +55,10 @@ ApplicationWindow { id: clipboard } + Style { + id: appStyle + } + Connections { target: mainApplication onClosing: diff --git a/mix/qml/CallStack.qml b/mix/qml/CallStack.qml index c9e22532d..9a938078c 100644 --- a/mix/qml/CallStack.qml +++ b/mix/qml/CallStack.qml @@ -2,7 +2,6 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import "." DebugInfoList { @@ -37,7 +36,7 @@ DebugInfoList anchors.leftMargin: 5 color: "#4a4a4a" text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize width: parent.width - 5 elide: Text.ElideRight } @@ -58,7 +57,7 @@ DebugInfoList color: "#4a4a4a" text: styleData.value; elide: Text.ElideRight - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/CodeEditor.qml b/mix/qml/CodeEditor.qml index 9d3df69e5..25f76b701 100644 --- a/mix/qml/CodeEditor.qml +++ b/mix/qml/CodeEditor.qml @@ -24,31 +24,36 @@ Item { id: contentView width: parent.width height: parent.height * 0.7 - 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 - } + + CodeEditorStyle { + id: style + } + + 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 @@ -66,7 +71,7 @@ Item { height: parent.height font.family: "Monospace" - font.pointSize: CodeEditorStyle.general.basicFontSize + font.pointSize: style.general.basicFontSize width: parent.width tabChangesFocus: false diff --git a/mix/qml/CodeEditorStyle.qml b/mix/qml/CodeEditorStyle.qml index c9740957c..999054d55 100644 --- a/mix/qml/CodeEditorStyle.qml +++ b/mix/qml/CodeEditorStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 25ecbc98c..c04776c25 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -7,13 +7,15 @@ import QtQuick.Dialogs 1.1 Item { id: codeEditorView property string currentDocumentId: "" + property int openDocCount: 0 signal documentEdit(string documentId) signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) + signal loadComplete function getDocumentText(documentId) { - for (var i = 0; i < editorListModel.count; i++) { + for (var i = 0; i < openDocCount; i++) { if (editorListModel.get(i).documentId === documentId) { return editors.itemAt(i).item.getText(); } @@ -22,7 +24,7 @@ Item { } function isDocumentOpen(documentId) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === documentId && editors.itemAt(i).item) return true; @@ -35,15 +37,27 @@ Item { } function loadDocument(document) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === document.documentId) return; //already open - editorListModel.append(document); + if (editorListModel.count <= openDocCount) + editorListModel.append(document); + else + { + editorListModel.set(openDocCount, document); + editors.itemAt(openDocCount).visible = true; + doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount)) + } + openDocCount++; + } function doLoadDocument(editor, document) { var data = fileIo.readFile(document.path); + editor.onLoadComplete.connect(function() { + loadComplete(); + }); editor.onEditorTextChanged.connect(function() { documentEdit(document.documentId); if (document.isContract) @@ -60,7 +74,7 @@ Item { } function getEditor(documentId) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { if (editorListModel.get(i).documentId === documentId) return editors.itemAt(i).item; @@ -91,7 +105,7 @@ Item { } function editingContract() { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === currentDocumentId) return editorListModel.get(i).isContract; return false; @@ -99,7 +113,7 @@ Item { function getBreakpoints() { var bpMap = {}; - for (var i = 0; i < editorListModel.count; i++) { + for (var i = 0; i < openDocCount; i++) { var documentId = editorListModel.get(i).documentId; var editor = editors.itemAt(i).item; if (editor) { @@ -130,7 +144,7 @@ Item { } onProjectSaving: { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { var doc = editorListModel.get(i); var editor = editors.itemAt(i).item; @@ -142,7 +156,7 @@ Item { onProjectSaved: { if (projectModel.appIsClosing || projectModel.projectIsClosing) return; - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { var doc = editorListModel.get(i); resetEditStatus(doc.documentId); @@ -152,8 +166,9 @@ Item { onProjectClosed: { for (var i = 0; i < editorListModel.count; i++) editors.itemAt(i).visible = false; - editorListModel.clear(); + //editorListModel.clear(); currentDocumentId = ""; + openDocCount = 0; } onDocumentSaved: { @@ -177,6 +192,11 @@ Item { } } + CodeEditorStyle + { + id: style; + } + MessageDialog { id: messageDialog @@ -194,12 +214,15 @@ Item { Repeater { id: editors model: editorListModel + onItemRemoved: { + item.item.unloaded = true; + } delegate: Loader { id: loader active: false asynchronous: true anchors.fill: parent - source: "CodeEditor.qml" + source: appService.haveWebEngine ? "WebCodeEditor.qml" : "CodeEditor.qml" visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) property bool changed: false onVisibleChanged: { diff --git a/mix/qml/CommonSeparator.qml b/mix/qml/CommonSeparator.qml index c81a81f63..6644802c7 100644 --- a/mix/qml/CommonSeparator.qml +++ b/mix/qml/CommonSeparator.qml @@ -4,6 +4,6 @@ import "." Rectangle { height: 1 - color: Style.generic.layout.separatorColor + color: appStyle.generic.layout.separatorColor } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 836bbaf94..ad03f1456 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -65,6 +65,10 @@ Rectangle { Debugger.setBreakpoints(bp); } + DebuggerPaneStyle { + id: dbgStyle + } + Connections { target: clientModel onDebugDataReady: { @@ -449,7 +453,7 @@ Rectangle { color: "#b2b3ae" text: styleData.value.split(' ')[0] font.family: "monospace" - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize wrapMode: Text.NoWrap id: id } @@ -459,7 +463,7 @@ Rectangle { color: styleData.selected ? "white" : "black" font.family: "monospace" text: styleData.value.replace(styleData.value.split(' ')[0], '') - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } @@ -532,7 +536,7 @@ Rectangle { font.family: "monospace" color: "#4a4a4a" text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } @@ -550,7 +554,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" text: styleData.value - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/DebuggerPaneStyle.qml b/mix/qml/DebuggerPaneStyle.qml index 1078df2f9..db8bbe253 100644 --- a/mix/qml/DebuggerPaneStyle.qml +++ b/mix/qml/DebuggerPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/DefaultLabel.qml b/mix/qml/DefaultLabel.qml index d8ef1faff..5d00df137 100644 --- a/mix/qml/DefaultLabel.qml +++ b/mix/qml/DefaultLabel.qml @@ -1,6 +1,5 @@ import QtQuick 2.0 import QtQuick.Controls 1.1 -import "." Label { text: text diff --git a/mix/qml/DeploymentDialog.qml b/mix/qml/DeploymentDialog.qml index c194977cf..1d6771b4a 100644 --- a/mix/qml/DeploymentDialog.qml +++ b/mix/qml/DeploymentDialog.qml @@ -31,7 +31,7 @@ Window { property string currentAccount property alias gasToUse: gasToUseInput.text - color: Style.generic.layout.backgroundColor + color: appStyle.generic.layout.backgroundColor function close() { @@ -329,7 +329,7 @@ Window { anchors.verticalCenter: parent.verticalCenter; anchors.left: applicationUrlEth.right font.italic: true - font.pointSize: Style.absoluteSize(-1) + font.pointSize: appStyle.absoluteSize(-1) } } } diff --git a/mix/qml/FilesSection.qml b/mix/qml/FilesSection.qml index d9f664894..d89875583 100644 --- a/mix/qml/FilesSection.qml +++ b/mix/qml/FilesSection.qml @@ -18,21 +18,21 @@ Rectangle property string sectionName; property variant selManager; property int index; - color: index % 2 === 0 ? "transparent" : ProjectFilesStyle.title.background + color: index % 2 === 0 ? "transparent" : projectFilesStyle.title.background function hiddenHeightTopLevel() { - return section.state === "hidden" ? ProjectFilesStyle.documentsList.height : ProjectFilesStyle.documentsList.fileNameHeight * model.count + ProjectFilesStyle.documentsList.height; + return section.state === "hidden" ? projectFilesStyle.documentsList.height : projectFilesStyle.documentsList.fileNameHeight * model.count + projectFilesStyle.documentsList.height; } function hiddenHeightRepeater() { - return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; + return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; } function hiddenHeightElement() { - return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight; + return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight; } function getDocumentIndex(documentId) @@ -68,7 +68,7 @@ Rectangle { anchors.top: parent.top id: rowCol - height: ProjectFilesStyle.documentsList.height + height: projectFilesStyle.documentsList.height Layout.fillWidth: true @@ -88,15 +88,15 @@ Rectangle id: section text: sectionName anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin - color: ProjectFilesStyle.documentsList.sectionColor + anchors.leftMargin: projectFilesStyle.general.leftMargin + color: projectFilesStyle.documentsList.sectionColor font.family: boldFont.name - font.pointSize: ProjectFilesStyle.documentsList.sectionFontSize + font.pointSize: projectFilesStyle.documentsList.sectionFontSize states: [ State { name: "hidden" PropertyChanges { target: filesList; visible: false; } - PropertyChanges { target: rowCol; Layout.minimumHeight: ProjectFilesStyle.documentsList.height; Layout.maximumHeight: ProjectFilesStyle.documentsList.height; height: ProjectFilesStyle.documentsList.height; } + PropertyChanges { target: rowCol; Layout.minimumHeight: projectFilesStyle.documentsList.height; Layout.maximumHeight: projectFilesStyle.documentsList.height; height: projectFilesStyle.documentsList.height; } PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } } ] @@ -138,7 +138,7 @@ Rectangle Layout.preferredHeight: wrapperItem.hiddenHeightElement() Layout.maximumHeight: wrapperItem.hiddenHeightElement() height: wrapperItem.hiddenHeightElement() - color: isSelected ? ProjectFilesStyle.documentsList.highlightColor : "transparent" + color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent" property bool isSelected property bool renameMode @@ -147,15 +147,15 @@ Rectangle anchors.verticalCenter: parent.verticalCenter anchors.fill: parent anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + 2 + anchors.leftMargin: projectFilesStyle.general.leftMargin + 2 Text { id: nameText height: parent.height visible: !renameMode - color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color + color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color text: name; font.family: fileNameFont.name - font.pointSize: ProjectFilesStyle.documentsList.fontSize + font.pointSize: projectFilesStyle.documentsList.fontSize verticalAlignment: Text.AlignVCenter Connections @@ -182,7 +182,7 @@ Rectangle DefaultLabel { id: editStatusLabel visible: false - color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color + color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color verticalAlignment: Text.AlignVCenter text: "*" width: 10 @@ -196,7 +196,7 @@ Rectangle visible: renameMode anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + anchors.leftMargin: projectFilesStyle.general.leftMargin MouseArea { id: textMouseArea anchors.fill: parent diff --git a/mix/qml/ItemDelegateDataDump.qml b/mix/qml/ItemDelegateDataDump.qml index 9aa6d00db..2c1419e61 100644 --- a/mix/qml/ItemDelegateDataDump.qml +++ b/mix/qml/ItemDelegateDataDump.qml @@ -28,7 +28,7 @@ Rectangle { font.bold: true color: "#4a4a4a" text: modelData[0] - font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize; + font.pointSize: dbgStyle.general.dataDumpFontSize; } } @@ -47,7 +47,7 @@ Rectangle { anchors.leftMargin: 4 color: "#4a4a4a" text: modelData[1] - font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize + font.pointSize: dbgStyle.general.dataDumpFontSize } } } diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index ca123f6a0..7adff8785 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -3,7 +3,6 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 import org.ethereum.qml.SortFilterProxyModel 1.0 -import "." Rectangle { @@ -28,6 +27,10 @@ Rectangle } } + LogsPaneStyle { + id: logStyle + } + anchors.fill: parent radius: 10 color: "transparent" @@ -99,15 +102,15 @@ Rectangle Rectangle { - width: LogsPaneStyle.generic.layout.dateWidth + LogsPaneStyle.generic.layout.contentWidth + LogsPaneStyle.generic.layout.typeWidth + width: logStyle.generic.layout.dateWidth + logStyle.generic.layout.contentWidth + logStyle.generic.layout.typeWidth height: 30 color: { var cl; if (level === "warning" || level === "error") - cl = LogsPaneStyle.generic.layout.errorColor; + cl = logStyle.generic.layout.errorColor; else - cl = index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; + cl = index % 2 === 0 ? "transparent" : logStyle.generic.layout.logAlternateColor; if (index === 0) logsRepeater.frontColor = cl; return cl; @@ -137,9 +140,9 @@ Rectangle DefaultLabel { text: date; - font.family: LogsPaneStyle.generic.layout.logLabelFont - width: LogsPaneStyle.generic.layout.dateWidth - font.pointSize: Style.absoluteSize(-1) + font.family: logStyle.generic.layout.logLabelFont + width: logStyle.generic.layout.dateWidth + font.pointSize: appStyle.absoluteSize(-1) anchors.left: parent.left anchors.leftMargin: 15 anchors.verticalCenter: parent.verticalCenter @@ -150,9 +153,9 @@ Rectangle DefaultLabel { text: type; - font.family: LogsPaneStyle.generic.layout.logLabelFont - width: LogsPaneStyle.generic.layout.typeWidth - font.pointSize: Style.absoluteSize(-1) + font.family: logStyle.generic.layout.logLabelFont + width: logStyle.generic.layout.typeWidth + font.pointSize: appStyle.absoluteSize(-1) anchors.left: parent.left anchors.leftMargin: 100 anchors.verticalCenter: parent.verticalCenter @@ -164,9 +167,9 @@ Rectangle Text { id: logContent text: content; - font.family: LogsPaneStyle.generic.layout.logLabelFont - width: LogsPaneStyle.generic.layout.contentWidth - font.pointSize: Style.absoluteSize(-1) + font.family: logStyle.generic.layout.logLabelFont + width: logStyle.generic.layout.contentWidth + font.pointSize: appStyle.absoluteSize(-1) anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight anchors.left: parent.left @@ -194,8 +197,8 @@ Rectangle id: itemDelegate DefaultLabel { text: styleData.value; - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-1) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-1) color: { if (proxyModel.get(styleData.row).level === "error") return "red"; @@ -214,16 +217,16 @@ Rectangle GradientStop { position: 0.0; color: "#f1f1f1" } GradientStop { position: 1.0; color: "#d9d7da" } } - Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight - height: LogsPaneStyle.generic.layout.headerHeight + Layout.preferredHeight: logStyle.generic.layout.headerHeight + height: logStyle.generic.layout.headerHeight width: logsPane.width anchors.bottom: parent.bottom Row { id: rowAction - anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin + anchors.leftMargin: logStyle.generic.layout.leftMargin anchors.left: parent.left - spacing: LogsPaneStyle.generic.layout.headerButtonSpacing + spacing: logStyle.generic.layout.headerButtonSpacing height: parent.height Rectangle { @@ -233,9 +236,9 @@ Rectangle DefaultLabel { anchors.verticalCenter: parent.verticalCenter - color: LogsPaneStyle.generic.layout.logLabelColor - font.pointSize: Style.absoluteSize(-3) - font.family: LogsPaneStyle.generic.layout.logLabelFont + color: logStyle.generic.layout.logLabelColor + font.pointSize: appStyle.absoluteSize(-3) + font.family: logStyle.generic.layout.logLabelFont text: qsTr("Show:") } } @@ -244,20 +247,20 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 + color: logStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 + color: logStyle.generic.layout.buttonSeparatorColor2 } ToolButton { id: javascriptButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight width: 20 anchors.verticalCenter: parent.verticalCenter checked: true @@ -270,16 +273,16 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("JS") } } background: Rectangle { - color: javascriptButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" + color: javascriptButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" } } } @@ -288,20 +291,20 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 + color: logStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 + color: logStyle.generic.layout.buttonSeparatorColor2 } ToolButton { id: runButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight width: 30 anchors.verticalCenter: parent.verticalCenter checked: true @@ -314,16 +317,16 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("Run") } } background: Rectangle { - color: runButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" + color: runButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" } } } @@ -332,20 +335,20 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 + color: logStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 + color: logStyle.generic.layout.buttonSeparatorColor2 } ToolButton { id: stateButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter width: 35 checked: true @@ -358,16 +361,16 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("State") } } background: Rectangle { - color: stateButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" + color: stateButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" } } } @@ -376,14 +379,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 + color: logStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 + color: logStyle.generic.layout.buttonSeparatorColor2 } } @@ -395,7 +398,7 @@ Rectangle spacing: 10 Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter color: "transparent" width: 20 @@ -410,8 +413,8 @@ Rectangle ButtonStyle { background: Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight - implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight + implicitHeight: logStyle.generic.layout.headerButtonHeight color: "transparent" } } @@ -438,7 +441,7 @@ Rectangle Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter color: "transparent" width: 20 @@ -453,8 +456,8 @@ Rectangle ButtonStyle { background: Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight - implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight + implicitHeight: logStyle.generic.layout.headerButtonHeight color: "transparent" } } @@ -510,8 +513,8 @@ Rectangle width: 100 anchors.left: searchImg.right anchors.leftMargin: -7 - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) font.italic: true text: qsTr(" - Search - ") onFocusChanged: @@ -540,7 +543,7 @@ Rectangle Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter color: "transparent" width: 20 @@ -555,8 +558,8 @@ Rectangle ButtonStyle { background: Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight - implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight + implicitHeight: logStyle.generic.layout.headerButtonHeight color: "transparent" } } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 0bbdfcee4..fe50610c8 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 67034f108..ca08485c3 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -22,6 +22,7 @@ Rectangle { property alias rightViewVisible: rightView.visible property alias webViewVisible: webPreview.visible + property alias webView: webPreview property alias projectViewVisible: projectList.visible property alias runOnProjectLoad: mainSettings.runOnProjectLoad property alias rightPane: rightView diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml index 1ec53e1d9..3ea2a5a4b 100644 --- a/mix/qml/NewProjectDialog.qml +++ b/mix/qml/NewProjectDialog.qml @@ -15,6 +15,7 @@ Window { property alias projectTitle: titleField.text readonly property string projectPath: "file://" + pathField.text + property alias pathFieldText: pathField.text signal accepted function open() { diff --git a/mix/qml/ProjectFilesStyle.qml b/mix/qml/ProjectFilesStyle.qml index f4b83c728..ca4499196 100644 --- a/mix/qml/ProjectFilesStyle.qml +++ b/mix/qml/ProjectFilesStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 6ca6e6d1f..a98c2587b 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -8,6 +8,11 @@ import "." Item { property bool renameMode: false; + + ProjectFilesStyle { + id: projectFilesStyle + } + ColumnLayout { anchors.fill: parent id: filesCol @@ -20,8 +25,8 @@ Item { Rectangle { - color: ProjectFilesStyle.title.background - height: ProjectFilesStyle.title.height + color: projectFilesStyle.title.background + height: projectFilesStyle.title.height Layout.fillWidth: true Image { id: projectIcon @@ -37,14 +42,14 @@ Item { Text { id: projectTitle - color: ProjectFilesStyle.title.color + color: projectFilesStyle.title.color text: projectModel.projectTitle anchors.verticalCenter: parent.verticalCenter visible: !projectModel.isEmpty; anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + anchors.leftMargin: projectFilesStyle.general.leftMargin font.family: srcSansProLight.name - font.pointSize: ProjectFilesStyle.title.fontSize + font.pointSize: projectFilesStyle.title.fontSize font.weight: Font.Light } @@ -54,7 +59,7 @@ Item { anchors.right: parent.right anchors.rightMargin: 15 font.family: srcSansProLight.name - font.pointSize: ProjectFilesStyle.title.fontSize + font.pointSize: projectFilesStyle.title.fontSize anchors.verticalCenter: parent.verticalCenter font.weight: Font.Light } @@ -64,14 +69,14 @@ Item { { Layout.fillWidth: true height: 3 - color: ProjectFilesStyle.documentsList.background + color: projectFilesStyle.documentsList.background } Rectangle { Layout.fillWidth: true Layout.fillHeight: true - color: ProjectFilesStyle.documentsList.background + color: projectFilesStyle.documentsList.background ColumnLayout { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index ec7681f16..275557d95 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -43,8 +43,10 @@ Item { property string deploymentDir property var listModel: projectListModel property var stateListModel: projectStateListModel.model + property alias stateDialog: projectStateListModel.stateDialog property CodeEditorView codeEditor: null property var unsavedFiles: [] + property alias newProjectDialog: newProjectDialog //interface function saveAll() { ProjectModelCode.saveAll(); } diff --git a/mix/qml/Splitter.qml b/mix/qml/Splitter.qml index 96e823795..012379700 100644 --- a/mix/qml/Splitter.qml +++ b/mix/qml/Splitter.qml @@ -9,6 +9,3 @@ SplitView color: "#cccccc" } } - - - diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index b229ed433..9dc1549c9 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -17,10 +17,12 @@ Window { height: 480 title: qsTr("Edit State") visible: false - color: StateDialogStyle.generic.backgroundColor + color: stateDialogStyle.generic.backgroundColor property alias stateTitle: titleField.text property alias isDefault: defaultCheckBox.checked + property alias model: transactionsModel + property alias transactionDialog: transactionDialog property int stateIndex property var stateTransactions: [] property var stateAccounts: [] @@ -56,6 +58,11 @@ Window { forceActiveFocus(); } + function acceptAndClose() { + close(); + accepted(); + } + function close() { visible = false; } @@ -71,6 +78,10 @@ Window { return item; } + StateDialogStyle { + id: stateDialogStyle + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 @@ -292,8 +303,7 @@ Window { Button { text: qsTr("OK"); onClicked: { - close(); - accepted(); + acceptAndClose(); } } Button { diff --git a/mix/qml/StateDialogStyle.qml b/mix/qml/StateDialogStyle.qml index 39214312a..993e6a1c3 100644 --- a/mix/qml/StateDialogStyle.qml +++ b/mix/qml/StateDialogStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 682c4b52c..c90692572 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -12,6 +12,7 @@ Item { property alias model: stateListModel property var stateList: [] + property alias stateDialog: stateDialog property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 6d8042add..ef7167b43 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -54,6 +54,10 @@ Rectangle { currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" } } + StatusPaneStyle { + id: statusPaneStyle + } + Connections { target: webPreview onJavaScriptMessage: @@ -123,7 +127,7 @@ Rectangle { Text { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font.pointSize: Style.absoluteSize(-1) + font.pointSize: appStyle.absoluteSize(-1) height: 15 font.family: "sans serif" objectName: "status" diff --git a/mix/qml/StatusPaneStyle.qml b/mix/qml/StatusPaneStyle.qml index e6a1c9910..1eb11b48e 100644 --- a/mix/qml/StatusPaneStyle.qml +++ b/mix/qml/StatusPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/StorageView.qml b/mix/qml/StorageView.qml index f4831e3c3..ecd64ccf8 100644 --- a/mix/qml/StorageView.qml +++ b/mix/qml/StorageView.qml @@ -29,7 +29,7 @@ DebugInfoList anchors.leftMargin: 5 color: "#4a4a4a" text: styleData.value.split('\t')[0]; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize width: parent.width - 5 elide: Text.ElideRight } @@ -52,7 +52,7 @@ DebugInfoList color: "#4a4a4a" text: styleData.value.split('\t')[1]; elide: Text.ElideRight - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/Style.qml b/mix/qml/Style.qml index c317177a3..422831c78 100644 --- a/mix/qml/Style.qml +++ b/mix/qml/Style.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 33ab8c5d0..62a799b26 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -13,7 +13,7 @@ Window { width: 520 height: 500; visible: false - color: StateDialogStyle.generic.backgroundColor + color: transactionDialogStyle.generic.backgroundColor title: qsTr("Edit Transaction") property int transactionIndex property alias gas: gasValueEdit.gasValue; @@ -59,16 +59,7 @@ Window { contractComboBox.currentIndex = contractIndex; loadFunctions(contractComboBox.currentValue()); - - var functionIndex = -1; - for (var f = 0; f < functionsModel.count; f++) - if (functionsModel.get(f).text === item.functionId) - functionIndex = f; - - if (functionIndex == -1 && functionsModel.count > 0) - functionIndex = 0; //@todo suggest unused function - - functionComboBox.currentIndex = functionIndex; + selectFunction(functionId); paramsModel = []; if (functionId !== contractComboBox.currentValue()) @@ -87,9 +78,6 @@ Window { visible = true; valueField.focus = true; - modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300); - paramLabel.visible = paramsModel.length > 0; - paramScroll.visible = paramsModel.length > 0; } function loadFunctions(contractId) @@ -107,6 +95,19 @@ Window { } + function selectFunction(functionId) + { + var functionIndex = -1; + for (var f = 0; f < functionsModel.count; f++) + if (functionsModel.get(f).text === functionId) + functionIndex = f; + + if (functionIndex == -1 && functionsModel.count > 0) + functionIndex = 0; //@todo suggest unused function + + functionComboBox.currentIndex = functionIndex; + } + function loadParameter(parameter) { var type = parameter.type; @@ -136,6 +137,15 @@ Window { typeLoader.members = [] typeLoader.value = paramValues; typeLoader.members = paramsModel; + paramLabel.visible = paramsModel.length > 0; + paramScroll.visible = paramsModel.length > 0; + modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300); + } + + function acceptAndClose() + { + close(); + accepted(); } function close() @@ -169,6 +179,10 @@ Window { return item; } + StateDialogStyle { + id: transactionDialogStyle + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 @@ -365,8 +379,7 @@ Window { Button { text: qsTr("OK"); onClicked: { - close(); - accepted(); + acceptAndClose(); } } Button { diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 39c840dca..6ab0b398a 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -4,23 +4,27 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 import QtWebEngine 1.0 import QtWebEngine.experimental 1.0 +import org.ethereum.qml.Clipboard 1.0 import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { - signal editorTextChanged signal breakpointsChanged + signal editorTextChanged + signal loadComplete property bool isClean: true property string currentText: "" property string currentMode: "" property bool initialized: false + property bool unloaded: false property var currentBreakpoints: []; function setText(text, mode) { currentText = text; - currentMode = mode; + if (mode !== undefined) + currentMode = mode; if (initialized && editorBrowser) { editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")"); - editorBrowser.runJavaScript("setMode(\"" + mode + "\")"); + editorBrowser.runJavaScript("setMode(\"" + currentMode + "\")"); } setFocus(); } @@ -65,6 +69,11 @@ Item { editorBrowser.runJavaScript("changeGeneration()", function(result) {}); } + Clipboard + { + id: clipboard + } + Connections { target: clipboard onClipboardChanged: syncClipboard() @@ -133,7 +142,7 @@ Item { if (!editorBrowser) return; editorBrowser.runJavaScript("getTextChanged()", function(result) { - if (result === true) { + if (result === true && editorBrowser) { editorBrowser.runJavaScript("getText()" , function(textValue) { currentText = textValue; editorTextChanged(); @@ -141,7 +150,7 @@ Item { } }); editorBrowser.runJavaScript("getBreakpointsChanged()", function(result) { - if (result === true) { + if (result === true && editorBrowser) { editorBrowser.runJavaScript("getBreakpoints()" , function(bp) { if (currentBreakpoints !== bp) { currentBreakpoints = bp; diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 768d188cb..44b7e6c23 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,6 +12,7 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false + property alias urlInput: urlInput signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) function setPreviewUrl(url) { @@ -60,6 +61,10 @@ Item { setPreviewUrl(urlInput.text); } + WebPreviewStyle { + id: webPreviewStyle + } + Connections { target: mainApplication onLoaded: { @@ -183,7 +188,7 @@ Item { Rectangle { anchors.leftMargin: 4 - color: WebPreviewStyle.general.headerBackgroundColor + color: webPreviewStyle.general.headerBackgroundColor Layout.preferredWidth: parent.width Layout.preferredHeight: 32 Row { @@ -230,7 +235,7 @@ Item { { width: 1 height: parent.height - 10 - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor anchors.verticalCenter: parent.verticalCenter } @@ -251,7 +256,7 @@ Item { { width: 1 height: parent.height - 10 - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor anchors.verticalCenter: parent.verticalCenter } @@ -285,7 +290,7 @@ Item { { Layout.preferredHeight: 1 Layout.preferredWidth: parent.width - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor } Splitter @@ -355,9 +360,9 @@ Item { id: expressionInput width: parent.width - 15 height: 20 - font.family: WebPreviewStyle.general.fontName + font.family: webPreviewStyle.general.fontName font.italic: true - font.pointSize: Style.absoluteSize(-3) + font.pointSize: appStyle.absoluteSize(-3) anchors.verticalCenter: parent.verticalCenter property var history: [] @@ -417,8 +422,8 @@ Item { id: resultTextArea width: expressionPanel.width wrapMode: Text.Wrap - font.family: WebPreviewStyle.general.fontName - font.pointSize: Style.absoluteSize(-3) + font.family: webPreviewStyle.general.fontName + font.pointSize: appStyle.absoluteSize(-3) backgroundVisible: true style: TextAreaStyle { backgroundColor: "#f0f0f0" diff --git a/mix/qml/WebPreviewStyle.qml b/mix/qml/WebPreviewStyle.qml index 231bbe16c..1fc0c8b99 100644 --- a/mix/qml/WebPreviewStyle.qml +++ b/mix/qml/WebPreviewStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/qmldir b/mix/qml/qmldir deleted file mode 100644 index 73c117941..000000000 --- a/mix/qml/qmldir +++ /dev/null @@ -1,8 +0,0 @@ -singleton Style 1.0 Style.qml -singleton StateDialogStyle 1.0 StateDialogStyle.qml -singleton ProjectFilesStyle 1.0 ProjectFilesStyle.qml -singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml -singleton StateStyle 1.0 StateStyle.qml -singleton StatusPaneStyle 1.0 StatusPaneStyle.qml -singleton WebPreviewStyle 1.0 WebPreviewStyle.qml -singleton LogsPaneStyle 1.0 LogsPaneStyle.qml diff --git a/mix/test.qrc b/mix/test.qrc new file mode 100644 index 000000000..e7d2271a4 --- /dev/null +++ b/mix/test.qrc @@ -0,0 +1,6 @@ + + + test/TestMain.qml + test/TestTransactionDebug.qml + + diff --git a/mix/test/TestMain.cpp b/mix/test/TestMain.cpp new file mode 100644 index 000000000..f21319004 --- /dev/null +++ b/mix/test/TestMain.cpp @@ -0,0 +1,50 @@ +/* + 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 . +*/ +/** @file main.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include "MixApplication.h" +#include "Exceptions.h" +#include "TestService.h" + +using namespace dev::mix; + +int main(int _argc, char* _argv[]) +{ + try + { + MixApplication::initialize(); + qmlRegisterType("org.ethereum.qml.TestService", 1, 0, "TestService"); + + return quick_test_main(_argc, _argv, "mix", _argv[1]); + } + catch (boost::exception const& _e) + { + std::cerr << boost::diagnostic_information(_e); + } + catch (std::exception const& _e) + { + std::cerr << _e.what(); + } +} diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp new file mode 100644 index 000000000..7ad2bc13d --- /dev/null +++ b/mix/test/TestService.cpp @@ -0,0 +1,194 @@ +/* + 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 . +*/ +/** @file TestService.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include "TestService.h" +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace mix +{ + +enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; + +static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay = -1) +{ + if (_delay == -1 || _delay < 30) + _delay = 30; + if (_delay > 0) + QTest::qWait(_delay); + + if (_action == MouseClick) + { + mouseEvent(MousePress, _window, _item, _button, _stateKey, _pos); + mouseEvent(MouseRelease, _window, _item, _button, _stateKey, _pos); + return; + } + + QPoint pos = _pos.toPoint(); + QQuickItem* sgitem = qobject_cast(_item); + if (sgitem) + pos = sgitem->mapToScene(_pos).toPoint(); + + _stateKey &= static_cast(Qt::KeyboardModifierMask); + + QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, _button, _stateKey); + switch (_action) + { + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseMove: + // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons + me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); + break; + default: + break; + } + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(_window, &me)) + { + static const char* mouseActionNames[] = { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving window"); + QWARN(warning.arg(QString::fromLatin1(mouseActionNames[static_cast(_action)])).toLatin1().data()); + } +} + +bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) +{ + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QMetaObject const* mo = _item->metaObject(); + + QStringList methods; + + for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) + if (mo->method(i).methodType() == QMetaMethod::Signal) + methods << QString::fromLatin1(mo->method(i).methodSignature()); + + QElapsedTimer timer; + timer.start(); + + while (!spy.size()) + { + int remaining = _timeout - int(timer.elapsed()); + if (remaining <= 0) + break; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QTest::qSleep(10); + } + + return spy.size(); +} + +bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay) +{ + QWindow* window = eventWindow(_item); + QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyRelease(QObject* _item, int _key, int _modifiers, int _delay) +{ + QWindow* window = eventWindow(_item); + QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyClick(QObject* _item, int _key, int _modifiers, int _delay) +{ + QWindow* window = eventWindow(_item); + QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay) +{ + QWindow* window = eventWindow(_item); + QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay) +{ + QWindow* window = eventWindow(_item); + QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay) +{ + QWindow* window = eventWindow(_item); + QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay) +{ + QWindow* window = qobject_cast(_item); + if (!window) + window = eventWindow(_item); + mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay); + return true; +} + +void TestService::setTargetWindow(QObject* _window) +{ + QQuickWindow* window = qobject_cast(_window); + if (window) + m_targetWindow = window; + window->requestActivate(); +} + +QWindow* TestService::eventWindow(QObject* _item) +{ + QQuickItem* item = qobject_cast(_item); + if (item && item->window()) + return item->window(); + + QQuickWindow* window = qobject_cast(_item); + if (!window) + window = qobject_cast(m_targetWindow); + if (window) + { + window->requestActivate(); + return window; + } + item = qobject_cast(m_targetWindow); + if (item) + return item->window(); + return 0; +} + +} +} diff --git a/mix/test/TestService.h b/mix/test/TestService.h new file mode 100644 index 000000000..fbf6533e6 --- /dev/null +++ b/mix/test/TestService.h @@ -0,0 +1,59 @@ +/* + 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 . +*/ +/** @file TestService.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +class QWindow; + +namespace dev +{ +namespace mix +{ + +class TestService: public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject* targetWindow READ targetWindow WRITE setTargetWindow) + +public: + QObject* targetWindow() const { return m_targetWindow; } + void setTargetWindow(QObject* _window); + +public slots: + bool waitForSignal(QObject* _item, QString _signalName, int _timeout); + bool keyPress(QObject* _item, int _key, int _modifiers, int _delay); + bool keyRelease(QObject* _item, int _key, int _modifiers, int _delay); + bool keyClick(QObject* _item, int _key, int _modifiers, int _delay); + bool keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay); + +private: + QWindow* eventWindow(QObject* _item); + QObject* m_targetWindow; +}; + +} +} diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml new file mode 100644 index 000000000..65b5a5076 --- /dev/null +++ b/mix/test/qml/TestMain.qml @@ -0,0 +1,142 @@ +import QtQuick 2.2 +import QtTest 1.1 +import org.ethereum.qml.TestService 1.0 +import "../../qml" + +TestCase +{ + id: tc + TestService + { + id: ts + targetWindow: mainApplication + function typeString(str, el) + { + if (el === undefined) + el = mainApplication; + + for (var c in str) + { + ts.keyPressChar(el, str[c], Qt.NoModifier, 0); + ts.keyReleaseChar(el, str[c], Qt.NoModifier, 0); + } + } + } + + function newProject() + { + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = mainApplication.projectModel.newProjectDialog; + wait(30); + projectDlg.projectTitle = "TestProject"; + projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path + projectDlg.acceptAndClose(); + wait(30); + } + + function editContract(c) + { + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c); + ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + } + + function clickElement(el, x, y) + { + ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10) + } + + function test_defaultTransactionSequence() + { + newProject(); + editContract( + "contract Contract {\r" + + " function Contract() {\r" + + " uint x = 69;\r" + + " uint y = 5;\r" + + " for (uint i = 0; i < y; ++i) {\r" + + " x += 42;\r" + + " z += x;\r" + + " }\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + } + + function test_transactionWithParameter() + { + newProject(); + editContract( + "contract Contract {\r" + + " function setZ(uint256 x) {\r" + + " z = x;\r" + + " }\r" + + " function getZ() returns(uint256) {\r" + + " return z;\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + mainApplication.projectModel.stateListModel.editState(0); + mainApplication.projectModel.stateDialog.model.addTransaction(); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + transactionDialog.selectFunction("setZ"); + clickElement(transactionDialog, 140, 300); + ts.typeString("442", transactionDialog); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog.selectFunction("getZ"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + mainApplication.mainContent.startQuickDebugging(); + wait(1); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)"); + } + + function test_constructorParameters() + { + newProject(); + editContract( + "contract Contract {\r" + + " function Contract(uint256 x) {\r" + + " z = x;\r" + + " }\r" + + " function getZ() returns(uint256) {\r" + + " return z;\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + mainApplication.projectModel.stateListModel.editState(0); + mainApplication.projectModel.stateDialog.model.editTransaction(2); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + clickElement(transactionDialog, 140, 300); + ts.typeString("442", transactionDialog); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog.selectFunction("getZ"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + wait(1); + mainApplication.mainContent.startQuickDebugging(); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)"); + } + + Application + { + id: mainApplication + } +} + diff --git a/mix/web.qrc b/mix/web.qrc index 63a3a668f..6870411c5 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -1,44 +1,44 @@ - - qml/WebPreview.qml - qml/html/WebContainer.html - qml/html/cm/active-line.js - qml/html/codeeditor.html - qml/html/cm/codemirror.css - qml/html/cm/codemirror.js - qml/html/cm/javascript.js - qml/html/cm/matchbrackets.js - qml/WebCodeEditor.qml - qml/html/codeeditor.js - qml/html/cm/fullscreen.css - qml/html/cm/fullscreen.js - qml/html/cm/solarized.css - qml/html/cm/xml.js - qml/html/cm/htmlmixed.js - qml/html/cm/css.js - qml/html/cm/solidity.js - qml/html/cm/dialog.css - qml/html/cm/dialog.js - qml/html/cm/search.js - qml/html/cm/searchcursor.js - qml/html/cm/anyword-hint.js - qml/html/cm/show-hint.js - qml/html/cm/show-hint.css - qml/html/cm/closebrackets.js - qml/html/cm/solidityToken.js - qml/html/cm/javascript-hint.js - qml/html/cm/errorannotation.js - qml/html/cm/tern.js - qml/html/cm/ecma5spec.js - qml/html/cm/comment.js - qml/html/cm/def.js - qml/html/cm/doc_comment.js - qml/html/cm/infer.js - qml/html/cm/signal.js - qml/html/cm/ternserver.js - qml/html/cm/acorn.js - qml/html/cm/acorn_loose.js - qml/html/cm/walk.js - qml/html/cm/mark-selection.js - + + qml/WebCodeEditor.qml + qml/WebPreview.qml + qml/html/WebContainer.html + qml/html/cm/acorn.js + qml/html/cm/acorn_loose.js + qml/html/cm/active-line.js + qml/html/cm/anyword-hint.js + qml/html/cm/closebrackets.js + qml/html/cm/codemirror.css + qml/html/cm/codemirror.js + qml/html/cm/comment.js + qml/html/cm/css.js + qml/html/cm/def.js + qml/html/cm/dialog.css + qml/html/cm/dialog.js + qml/html/cm/doc_comment.js + qml/html/cm/ecma5spec.js + qml/html/cm/errorannotation.js + qml/html/cm/fullscreen.css + qml/html/cm/fullscreen.js + qml/html/cm/htmlmixed.js + qml/html/cm/infer.js + qml/html/cm/javascript-hint.js + qml/html/cm/javascript.js + qml/html/cm/mark-selection.js + qml/html/cm/matchbrackets.js + qml/html/cm/search.js + qml/html/cm/searchcursor.js + qml/html/cm/show-hint.css + qml/html/cm/show-hint.js + qml/html/cm/signal.js + qml/html/cm/solarized.css + qml/html/cm/solidity.js + qml/html/cm/solidityToken.js + qml/html/cm/tern.js + qml/html/cm/ternserver.js + qml/html/cm/walk.js + qml/html/cm/xml.js + qml/html/codeeditor.html + qml/html/codeeditor.js + diff --git a/test/Stats.cpp b/test/Stats.cpp index fa615cb50..e76d1ee00 100644 --- a/test/Stats.cpp +++ b/test/Stats.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace dev { @@ -31,6 +32,11 @@ Stats& Stats::get() return instance; } +void Stats::suiteStarted(std::string const& _name) +{ + m_currentSuite = _name; +} + void Stats::testStarted(std::string const& _name) { m_currentTest = _name; @@ -39,7 +45,7 @@ void Stats::testStarted(std::string const& _name) void Stats::testFinished() { - m_stats[clock::now() - m_tp] = std::move(m_currentTest); + m_stats.push_back({clock::now() - m_tp, m_currentSuite + "/" + m_currentTest}); } std::ostream& operator<<(std::ostream& out, Stats::clock::duration const& d) @@ -52,31 +58,42 @@ Stats::~Stats() if (m_stats.empty()) return; + std::sort(m_stats.begin(), m_stats.end(), [](Stats::Item const& a, Stats::Item const& b){ + return a.duration < b.duration; + }); + auto& out = std::cout; auto itr = m_stats.begin(); auto min = *itr; auto max = *m_stats.rbegin(); std::advance(itr, m_stats.size() / 2); auto med = *itr; - auto tot = std::accumulate(m_stats.begin(), m_stats.end(), clock::duration{}, [](clock::duration const& a, stats_t::value_type const& v) + auto tot = std::accumulate(m_stats.begin(), m_stats.end(), clock::duration{}, [](clock::duration const& a, Stats::Item const& v) { - return a + v.first; + return a + v.duration; }); out << "\nSTATS:\n\n" << std::setfill(' '); - if (Options::get().statsFull) + if (Options::get().statsOutFile == "out") { for (auto&& s: m_stats) - out << " " << std::setw(40) << std::left << s.second.substr(0, 40) << s.first << " \n"; + out << " " << std::setw(40) << std::left << s.name.substr(0, 40) << s.duration << " \n"; out << "\n"; } + else if (!Options::get().statsOutFile.empty()) + { + // Output stats to file + std::ofstream file{Options::get().statsOutFile}; + for (auto&& s: m_stats) + file << s.name << "\t" << std::chrono::duration_cast(s.duration).count() << "\n"; + } out << " tot: " << tot << "\n" << " avg: " << (tot / m_stats.size()) << "\n\n" - << " min: " << min.first << " (" << min.second << ")\n" - << " med: " << med.first << " (" << med.second << ")\n" - << " max: " << max.first << " (" << max.second << ")\n"; + << " min: " << min.duration << " (" << min.name << ")\n" + << " med: " << med.duration << " (" << med.name << ")\n" + << " max: " << max.duration << " (" << max.name << ")\n"; } } diff --git a/test/Stats.h b/test/Stats.h index 7692a2b30..9b40c5fce 100644 --- a/test/Stats.h +++ b/test/Stats.h @@ -18,7 +18,7 @@ #pragma once #include -#include +#include #include "TestHelper.h" @@ -31,19 +31,26 @@ class Stats: public Listener { public: using clock = std::chrono::high_resolution_clock; - using stats_t = std::map; + + struct Item + { + clock::duration duration; + std::string name; + }; static Stats& get(); ~Stats(); + void suiteStarted(std::string const& _name) override; void testStarted(std::string const& _name) override; void testFinished() override; private: clock::time_point m_tp; + std::string m_currentSuite; std::string m_currentTest; - stats_t m_stats; + std::vector m_stats; }; } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 7dc001498..295b759f3 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "Stats.h" using namespace std; using namespace dev::eth; @@ -431,6 +432,9 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun string testPath = getTestPath(); testPath += _testPathAppendix; + if (Options::get().stats) + Listener::registerListener(Stats::get()); + if (Options::get().fillTests) { try @@ -462,6 +466,7 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun string s = asString(dev::contents(testPath + "/" + _name + ".json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + testPath + "/" + _name + ".json is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + Listener::notifySuiteStarted(_name); doTests(v, false); } catch (Exception const& _e) @@ -535,10 +540,12 @@ Options::Options() vmtrace = true; else if (arg == "--filltests") fillTests = true; - else if (arg == "--stats") + else if (arg.compare(0, 7, "--stats") == 0) + { stats = true; - else if (arg == "--stats=full") - stats = statsFull = true; + if (arg.size() > 7) + statsOutFile = arg.substr(8); // skip '=' char + } else if (arg == "--performance") performance = true; else if (arg == "--quadratic") @@ -586,6 +593,12 @@ void Listener::registerListener(Listener& _listener) g_listener = &_listener; } +void Listener::notifySuiteStarted(std::string const& _name) +{ + if (g_listener) + g_listener->suiteStarted(_name); +} + void Listener::notifyTestStarted(std::string const& _name) { if (g_listener) diff --git a/test/TestHelper.h b/test/TestHelper.h index 022c715f8..e5f96f51d 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -164,7 +164,7 @@ public: bool vmtrace = false; ///< Create EVM execution tracer // TODO: Link with log verbosity? bool fillTests = false; ///< Create JSON test files from execution results bool stats = false; ///< Execution time stats - bool statsFull = false; ///< Output full stats - execution times for every test + std::string statsOutFile; ///< Stats output file. "out" for standard output /// Test selection /// @{ @@ -191,10 +191,12 @@ class Listener public: virtual ~Listener() = default; + virtual void suiteStarted(std::string const&) {} virtual void testStarted(std::string const& _name) = 0; virtual void testFinished() = 0; static void registerListener(Listener& _listener); + static void notifySuiteStarted(std::string const& _name); static void notifyTestStarted(std::string const& _name); static void notifyTestFinished(); diff --git a/test/state.cpp b/test/state.cpp index 7c586ec7d..e4bf06bdc 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -31,7 +31,6 @@ #include #include #include "TestHelper.h" -#include "Stats.h" using namespace std; using namespace json_spirit; @@ -42,9 +41,6 @@ namespace dev { namespace test { void doStateTests(json_spirit::mValue& v, bool _fillin) { - if (Options::get().stats) - Listener::registerListener(Stats::get()); - for (auto& i: v.get_obj()) { std::cout << " " << i.first << "\n"; @@ -254,6 +250,7 @@ BOOST_AUTO_TEST_CASE(stRandom) string s = asString(dev::contents(path.string())); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + path.string() + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + test::Listener::notifySuiteStarted(path.filename().string()); dev::test::doStateTests(v, false); } catch (Exception const& _e) diff --git a/test/vm.cpp b/test/vm.cpp index 2bdafb270..cffbaa64d 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -25,7 +25,6 @@ #include #include #include "vm.h" -#include "Stats.h" using namespace std; using namespace json_spirit; @@ -311,9 +310,6 @@ namespace dev { namespace test { void doVMTests(json_spirit::mValue& v, bool _fillin) { - if (Options::get().stats) - Listener::registerListener(Stats::get()); - for (auto& i: v.get_obj()) { std::cout << " " << i.first << "\n"; @@ -549,6 +545,7 @@ BOOST_AUTO_TEST_CASE(vmRandom) string s = asString(dev::contents(path.string())); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + path.string() + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + test::Listener::notifySuiteStarted(path.filename().string()); doVMTests(v, false); } catch (Exception const& _e) diff --git a/test/webthreestubclient.h b/test/webthreestubclient.h index 4754bbbef..a460ddda4 100644 --- a/test/webthreestubclient.h +++ b/test/webthreestubclient.h @@ -62,6 +62,16 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } + std::string eth_protocolVersion() throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p = Json::nullValue; + Json::Value result = this->CallMethod("eth_protocolVersion",p); + if (result.isString()) + return result.asString(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } std::string eth_coinbase() throw (jsonrpc::JsonRpcException) { Json::Value p;