Browse Source

Merge branch 'develop' into event_sha3

cl-refactor
Marek Kotewicz 10 years ago
parent
commit
3b99b7c4d9
  1. 9
      alethzero/MainWin.cpp
  2. 20
      docker/Dockerfile
  3. 10
      libsolidity/CompilerStack.cpp
  4. 18
      libsolidity/CompilerStack.h
  5. 17
      libsolidity/ExpressionCompiler.cpp
  6. 4
      libwhisper/WhisperHost.cpp
  7. 18
      mix/ClientModel.cpp
  8. 2
      mix/CodeModel.cpp
  9. 10
      mix/CodeModel.h
  10. 9
      mix/DebuggingStateWrapper.cpp
  11. 30
      mix/DebuggingStateWrapper.h
  12. 13
      mix/MachineStates.h
  13. 6
      mix/MixClient.cpp
  14. 43
      mix/qml/CodeEditorView.qml
  15. 50
      mix/qml/Debugger.qml
  16. 25
      mix/qml/MainContent.qml
  17. 28
      mix/qml/WebCodeEditor.qml
  18. 4
      mix/qml/html/cm/codemirror.css
  19. 6
      mix/qml/html/cm/solarized.css
  20. 50
      mix/qml/html/codeeditor.js
  21. 147
      mix/qml/js/Debugger.js
  22. 4
      mix/qml/js/ProjectModel.js
  23. 24
      mix/qml/main.qml
  24. 11
      test/SolidityEndToEndTest.cpp

9
alethzero/MainWin.cpp

@ -1382,11 +1382,16 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Pre: <b>" << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << "</b>"; s << "<br/>Pre: <b>" << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << "</b>";
else else
s << "<br/>Pre: <i>Nothing is before Phil</i>"; s << "<br/>Pre: <i>Nothing is before Phil</i>";
BlockReceipts receipts = ethereum()->blockChain().receipts(h);
unsigned ii = 0;
for (auto const& i: block[1]) for (auto const& i: block[1])
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]"; {
s << "<br/>" << sha3(i.data()).abridged() << ": <b>" << receipts.receipts[ii].stateRoot() << "</b> [<b>" << receipts.receipts[ii].gasUsed() << "</b> used]";
++ii;
}
s << "<br/>Post: <b>" << info.stateRoot << "</b>"; s << "<br/>Post: <b>" << info.stateRoot << "</b>";
s << "<br/>Dump: " Span(Mono) << toHex(block[0].data()) << "</span>"; s << "<br/>Dump: " Span(Mono) << toHex(block[0].data()) << "</span>";
s << "<div>Receipts-Hex: " Span(Mono) << toHex(ethereum()->blockChain().receipts(h).rlp()) << "</span></div>"; s << "<div>Receipts-Hex: " Span(Mono) << toHex(receipts.rlp()) << "</span></div>";
} }
else else
{ {

20
docker/Dockerfile

@ -8,23 +8,29 @@ RUN apt-get upgrade -y
RUN apt-get install -qy build-essential g++-4.8 git cmake libboost-all-dev libcurl4-openssl-dev wget RUN apt-get install -qy build-essential g++-4.8 git cmake libboost-all-dev libcurl4-openssl-dev wget
RUN apt-get install -qy automake unzip libgmp-dev libtool libleveldb-dev yasm libminiupnpc-dev libreadline-dev scons RUN apt-get install -qy automake unzip libgmp-dev libtool libleveldb-dev yasm libminiupnpc-dev libreadline-dev scons
RUN apt-get install -qy libjsoncpp-dev libargtable2-dev RUN apt-get install -qy libjsoncpp-dev libargtable2-dev
RUN apt-get install -qy libncurses5-dev libcurl4-openssl-dev wget
# NCurses based GUI (not optional though for a succesful compilation, see https://github.com/ethereum/cpp-ethereum/issues/452 ) RUN apt-get install -qy libjsoncpp-dev libargtable2-dev libmicrohttpd-dev
RUN apt-get install -qy libncurses5-dev
# Qt-based GUI
# RUN apt-get install -qy qtbase5-dev qt5-default qtdeclarative5-dev libqt5webkit5-dev
# Ethereum PPA # Ethereum PPA
RUN apt-get install -qy software-properties-common RUN apt-get install -qy software-properties-common
RUN add-apt-repository ppa:ethereum/ethereum RUN add-apt-repository ppa:ethereum/ethereum
RUN add-apt-repository ppa:ethereum/ethereum-dev
RUN apt-get update RUN apt-get update
RUN apt-get install -qy libcryptopp-dev libjson-rpc-cpp-dev RUN apt-get install -qy libcryptopp-dev libjson-rpc-cpp-dev
# LLVM-3.5
RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
RUN echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.5 main\ndeb-src http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.5 main" > /etc/apt/sources.list.d/llvm-trusty.list
RUN apt-get update
RUN apt-get install -qy llvm-3.5 libedit-dev
# Fix llvm-3.5 cmake paths
RUN mkdir -p /usr/lib/llvm-3.5/share/llvm && ln -s /usr/share/llvm-3.5/cmake /usr/lib/llvm-3.5/share/llvm/cmake
# Build Ethereum (HEADLESS) # Build Ethereum (HEADLESS)
RUN git clone --depth=1 https://github.com/ethereum/cpp-ethereum RUN git clone --depth=1 https://github.com/ethereum/cpp-ethereum
RUN mkdir -p cpp-ethereum/build RUN mkdir -p cpp-ethereum/build
RUN cd cpp-ethereum/build && cmake .. -DCMAKE_BUILD_TYPE=Release -DHEADLESS=1 && make -j $(cat /proc/cpuinfo | grep processor | wc -l) && make install RUN cd cpp-ethereum/build && cmake .. -DHEADLESS=1 -DLLVM_DIR=/usr/share/llvm-3.5/cmake -DEVMJIT=1 && make -j $(cat /proc/cpuinfo | grep processor | wc -l) && make install
RUN ldconfig RUN ldconfig
ENTRYPOINT ["/usr/local/bin/eth"] ENTRYPOINT ["/usr/local/bin/eth"]

10
libsolidity/CompilerStack.cpp

@ -155,6 +155,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
return getBytecode(); return getBytecode();
} }
eth::AssemblyItems const& CompilerStack::getAssemblyItems(string const& _contractName) const
{
return getContract(_contractName).compiler->getAssemblyItems();
}
eth::AssemblyItems const& CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
{
return getContract(_contractName).compiler->getRuntimeAssemblyItems();
}
bytes const& CompilerStack::getBytecode(string const& _contractName) const bytes const& CompilerStack::getBytecode(string const& _contractName) const
{ {
return getContract(_contractName).bytecode; return getContract(_contractName).bytecode;

18
libsolidity/CompilerStack.h

@ -26,12 +26,22 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <memory> #include <memory>
#include <vector>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
namespace dev { namespace dev
namespace solidity { {
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
}
namespace solidity
{
// forward declarations // forward declarations
class Scanner; class Scanner;
@ -85,6 +95,10 @@ public:
bytes const& getBytecode(std::string const& _contractName = "") const; bytes const& getBytecode(std::string const& _contractName = "") const;
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor. /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns normal contract assembly items
eth::AssemblyItems const& getAssemblyItems(std::string const& _contractName = "") const;
/// @returns runtime contract assembly items
eth::AssemblyItems const& getRuntimeAssemblyItems(std::string const& _contractName = "") const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor. /// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const; dev::h256 getContractCodeHash(std::string const& _contractName = "") const;

17
libsolidity/ExpressionCompiler.cpp

@ -984,9 +984,10 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else else
// send all gas except for the 21 needed to execute "SUB" and "CALL" // send all gas except for the 21 needed to execute "SUB" and "CALL"
m_context << u256(21) << eth::Instruction::GAS << eth::Instruction::SUB; m_context << u256(_functionType.valueSet() ? 6741 : 41) << eth::Instruction::GAS << eth::Instruction::SUB;
m_context << eth::Instruction::CALL m_context << eth::Instruction::CALL;
<< eth::Instruction::POP; // @todo do not ignore failure indicator auto tag = m_context.appendConditionalJump();
m_context << eth::Instruction::STOP << tag; // STOP if CALL leaves 0.
if (_functionType.valueSet()) if (_functionType.valueSet())
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
if (_functionType.gasSet()) if (_functionType.gasSet())
@ -999,10 +1000,12 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true); CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true);
} }
void ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments, void ExpressionCompiler::appendArgumentsCopyToMemory(
TypePointers const& _types, vector<ASTPointer<Expression const>> const& _arguments,
bool _padToWordBoundaries, TypePointers const& _types,
bool _padExceptionIfFourBytes) bool _padToWordBoundaries,
bool _padExceptionIfFourBytes
)
{ {
solAssert(_types.empty() || _types.size() == _arguments.size(), ""); solAssert(_types.empty() || _types.size() == _arguments.size(), "");
for (size_t i = 0; i < _arguments.size(); ++i) for (size_t i = 0; i < _arguments.size(); ++i)

4
libwhisper/WhisperHost.cpp

@ -58,7 +58,7 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
{ {
cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data()); cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data());
if (_m.expiry() <= time(0)) if (_m.expiry() <= (unsigned)time(0))
return; return;
auto h = _m.sha3(); auto h = _m.sha3();
@ -171,7 +171,7 @@ void WhisperHost::cleanup()
{ {
// remove old messages. // remove old messages.
// should be called every now and again. // should be called every now and again.
auto now = time(0); unsigned now = (unsigned)time(0);
WriteGuard l(x_messages); WriteGuard l(x_messages);
for (auto it = m_expiryQueue.begin(); it != m_expiryQueue.end() && it->first <= now; it = m_expiryQueue.erase(it)) for (auto it = m_expiryQueue.begin(); it != m_expiryQueue.end() && it->first <= now; it = m_expiryQueue.erase(it))
m_messages.erase(it->second); m_messages.erase(it->second);

18
mix/ClientModel.cpp

@ -305,8 +305,21 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QDebugData* debugData = new QDebugData(); QDebugData* debugData = new QDebugData();
QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership);
QList<QCode*> codes; QList<QCode*> codes;
for (bytes const& code: _t.executionCode) for (MachineCode const& code: _t.executionCode)
codes.push_back(QMachineState::getHumanReadableCode(debugData, code)); {
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code));
//try to resolve contract for source level debugging
auto nameIter = m_contractNames.find(code.address);
if (nameIter != m_contractNames.end())
{
CompiledContract const& compilerRes = m_context->codeModel()->contract(nameIter->second);
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems();
QVariantList locations;
for (eth::AssemblyItem const& item: assemblyItems)
locations.push_back(QVariant::fromValue(new QSourceLocation(debugData, item.getLocation().start, item.getLocation().end)));
codes.back()->setLocations(compilerRes.documentId(), std::move(locations));
}
}
QList<QCallData*> data; QList<QCallData*> data;
for (bytes const& d: _t.transactionData) for (bytes const& d: _t.transactionData)
@ -330,7 +343,6 @@ void ClientModel::emptyRecord()
debugDataReady(new QDebugData()); debugDataReady(new QDebugData());
} }
void ClientModel::debugRecord(unsigned _index) void ClientModel::debugRecord(unsigned _index)
{ {
ExecutionResult const& e = m_client->executions().at(_index); ExecutionResult const& e = m_client->executions().at(_index);

2
mix/CodeModel.cpp

@ -56,6 +56,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
m_contract.reset(new QContractDefinition(&contractDefinition)); m_contract.reset(new QContractDefinition(&contractDefinition));
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership); QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership);
m_bytes = _compiler.getBytecode(_contractName.toStdString()); m_bytes = _compiler.getBytecode(_contractName.toStdString());
m_assemblyItems = _compiler.getRuntimeAssemblyItems(_contractName.toStdString());
m_constructorAssemblyItems = _compiler.getAssemblyItems(_contractName.toStdString());
dev::solidity::InterfaceHandler interfaceHandler; dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition)); m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty()) if (m_contractInterface.isEmpty())

10
mix/CodeModel.h

@ -30,6 +30,7 @@
#include <QHash> #include <QHash>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libevmcore/Assembly.h>
class QTextDocument; class QTextDocument;
@ -70,7 +71,7 @@ class CompiledContract: public QObject
Q_PROPERTY(QContractDefinition* contract READ contract) Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT) Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT)
Q_PROPERTY(QString codeHex READ codeHex CONSTANT) Q_PROPERTY(QString codeHex READ codeHex CONSTANT)
Q_PROPERTY(QString documentId MEMBER m_documentId CONSTANT) Q_PROPERTY(QString documentId READ documentId CONSTANT)
public: public:
/// Successful compilation result constructor /// Successful compilation result constructor
@ -86,6 +87,11 @@ public:
QString codeHex() const; QString codeHex() const;
/// @returns contract definition in JSON format /// @returns contract definition in JSON format
QString contractInterface() const { return m_contractInterface; } QString contractInterface() const { return m_contractInterface; }
/// @return assebly item locations
eth::AssemblyItems const& assemblyItems() const { return m_assemblyItems; }
eth::AssemblyItems const& constructorAssemblyItems() const { return m_constructorAssemblyItems; }
/// @returns contract source Id
QString documentId() const { return m_documentId; }
private: private:
uint m_sourceHash; uint m_sourceHash;
@ -94,6 +100,8 @@ private:
dev::bytes m_bytes; dev::bytes m_bytes;
QString m_contractInterface; QString m_contractInterface;
QString m_documentId; QString m_documentId;
eth::AssemblyItems m_assemblyItems;
eth::AssemblyItems m_constructorAssemblyItems;
friend class CodeModel; friend class CodeModel;
}; };

9
mix/DebuggingStateWrapper.cpp

@ -69,7 +69,7 @@ namespace
} }
} }
QCode* QMachineState::getHumanReadableCode(QObject* _owner, const bytes& _code) QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code)
{ {
QVariantList codeStr; QVariantList codeStr;
for (unsigned i = 0; i <= _code.size(); ++i) for (unsigned i = 0; i <= _code.size(); ++i)
@ -96,7 +96,7 @@ QCode* QMachineState::getHumanReadableCode(QObject* _owner, const bytes& _code)
break; // probably hit data segment break; // probably hit data segment
} }
} }
return new QCode(_owner, std::move(codeStr)); return new QCode(_owner, QString::fromStdString(toString(_address)), std::move(codeStr));
} }
QBigInt* QMachineState::gasCost() QBigInt* QMachineState::gasCost()
@ -152,11 +152,6 @@ QVariantList QMachineState::levels()
return levelList; return levelList;
} }
QString QMachineState::address()
{
return QString::fromStdString(toString(m_state.address));
}
QString QMachineState::instruction() QString QMachineState::instruction()
{ {
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name); return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name);

30
mix/DebuggingStateWrapper.h

@ -53,6 +53,22 @@ private:
int m_processIndex; int m_processIndex;
}; };
class QSourceLocation: public QObject
{
Q_OBJECT
Q_PROPERTY(int start MEMBER m_start CONSTANT)
Q_PROPERTY(int end MEMBER m_end CONSTANT)
public:
QSourceLocation(QObject* _owner, int _start, int _end): QObject(_owner), m_start(_start), m_end(_end) {}
private:
int m_start;
int m_end;
};
/** /**
* @brief Shared container for lines * @brief Shared container for lines
*/ */
@ -60,12 +76,19 @@ class QCode: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT) Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT)
Q_PROPERTY(QVariantList locations MEMBER m_locations CONSTANT)
Q_PROPERTY(QString address MEMBER m_address CONSTANT)
Q_PROPERTY(QString documentId MEMBER m_document CONSTANT)
public: public:
QCode(QObject* _owner, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions) {} QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions), m_address(_address) {}
void setLocations(QString const& _document, QVariantList&& _locations) { m_document = _document; m_locations = _locations; }
private: private:
QVariantList m_instructions; QVariantList m_instructions;
QString m_address;
QString m_document;
QVariantList m_locations;
}; };
/** /**
@ -110,7 +133,6 @@ class QMachineState: public QObject
Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT) Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT)
Q_PROPERTY(QBigInt* gas READ gas CONSTANT) Q_PROPERTY(QBigInt* gas READ gas CONSTANT)
Q_PROPERTY(QString instruction READ instruction CONSTANT) Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QString address READ address CONSTANT)
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT) Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT) Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT) Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT)
@ -133,8 +155,6 @@ public:
unsigned codeIndex() { return m_state.codeIndex; } unsigned codeIndex() { return m_state.codeIndex; }
/// Get the call data id /// Get the call data id
unsigned dataIndex() { return m_state.dataIndex; } unsigned dataIndex() { return m_state.dataIndex; }
/// Get address for call stack
QString address();
/// Get gas cost. /// Get gas cost.
QBigInt* gasCost(); QBigInt* gasCost();
/// Get gas used. /// Get gas used.
@ -158,7 +178,7 @@ public:
/// Set the current processed machine state. /// Set the current processed machine state.
void setState(MachineState _state) { m_state = _state; } void setState(MachineState _state) { m_state = _state; }
/// Convert all machine states in human readable code. /// Convert all machine states in human readable code.
static QCode* getHumanReadableCode(QObject* _owner, bytes const& _code); static QCode* getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code);
/// Convert call data into human readable form /// Convert call data into human readable form
static QCallData* getDebugCallData(QObject* _owner, bytes const& _data); static QCallData* getDebugCallData(QObject* _owner, bytes const& _data);

13
mix/MachineStates.h

@ -42,7 +42,6 @@ namespace mix
struct MachineState struct MachineState
{ {
uint64_t steps; uint64_t steps;
dev::Address address;
dev::u256 curPC; dev::u256 curPC;
dev::eth::Instruction inst; dev::eth::Instruction inst;
dev::bigint newMemSize; dev::bigint newMemSize;
@ -56,6 +55,15 @@ namespace mix
unsigned dataIndex; unsigned dataIndex;
}; };
/**
* @brief Executed conract code info
*/
struct MachineCode
{
dev::Address address;
bytes code;
};
/** /**
* @brief Store information about a machine states. * @brief Store information about a machine states.
*/ */
@ -65,7 +73,7 @@ namespace mix
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<bytes> transactionData; std::vector<bytes> transactionData;
std::vector<bytes> executionCode; std::vector<MachineCode> executionCode;
bytes returnValue; bytes returnValue;
dev::Address address; dev::Address address;
dev::Address sender; dev::Address sender;
@ -74,6 +82,7 @@ namespace mix
unsigned transactionIndex; unsigned transactionIndex;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); } bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
bool isConstructor() const { return !isCall() && !address; }
}; };
using ExecutionResults = std::vector<ExecutionResult>; using ExecutionResults = std::vector<ExecutionResult>;

6
mix/MixClient.cpp

@ -107,7 +107,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
execution.setup(&rlp); execution.setup(&rlp);
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<unsigned> levels; std::vector<unsigned> levels;
std::vector<bytes> codes; std::vector<MachineCode> codes;
std::map<bytes const*, unsigned> codeIndexes; std::map<bytes const*, unsigned> codeIndexes;
std::vector<bytes> data; std::vector<bytes> data;
std::map<bytesConstRef const*, unsigned> dataIndexes; std::map<bytesConstRef const*, unsigned> dataIndexes;
@ -127,7 +127,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
else else
{ {
codeIndex = codes.size(); codeIndex = codes.size();
codes.push_back(ext.code); codes.push_back(MachineCode({ext.myAddress, ext.code}));
codeIndexes[&ext.code] = codeIndex; codeIndexes[&ext.code] = codeIndex;
} }
lastCode = &ext.code; lastCode = &ext.code;
@ -152,7 +152,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
else else
levels.resize(ext.depth); levels.resize(ext.depth);
machineStates.emplace_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex})); vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex}));
}; };

43
mix/qml/CodeEditorView.qml

@ -7,6 +7,7 @@ Item {
id: codeEditorView id: codeEditorView
property string currentDocumentId: "" property string currentDocumentId: ""
signal documentEdit(string documentId) signal documentEdit(string documentId)
signal breakpointsChanged(string documentId)
function getDocumentText(documentId) { function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) { for (var i = 0; i < editorListModel.count; i++) {
@ -45,9 +46,51 @@ Item {
if (document.isContract) if (document.isContract)
codeModel.registerCodeChange(document.documentId, editor.getText()); codeModel.registerCodeChange(document.documentId, editor.getText());
}); });
editor.onBreakpointsChanged.connect(function() {
if (document.isContract)
breakpointsChanged(document.documentId);
});
editor.setText(data, document.syntaxMode); editor.setText(data, document.syntaxMode);
} }
function getEditor(documentId) {
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === documentId)
return editors.itemAt(i).item;
return null;
}
function highlightExecution(documentId, location) {
var editor = getEditor(documentId);
if (editor)
editor.highlightExecution(location);
}
function editingContract() {
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === currentDocumentId)
return editorListModel.get(i).isContract;
return false;
}
function getBreakpoints() {
var bpMap = {};
for (var i = 0; i < editorListModel.count; i++) {
var documentId = editorListModel.get(i).documentId;
var editor = editors.itemAt(i).item;
if (editor) {
bpMap[documentId] = editor.getBreakpoints();
}
}
return bpMap;
}
function toggleBreakpoint() {
var editor = getEditor(currentDocumentId);
if (editor)
editor.toggleBreakpoint();
}
Component.onCompleted: projectModel.codeEditor = codeEditorView; Component.onCompleted: projectModel.codeEditor = codeEditorView;
Connections { Connections {

50
mix/qml/Debugger.qml

@ -11,8 +11,11 @@ import "."
Rectangle { Rectangle {
id: debugPanel id: debugPanel
property alias transactionLog : transactionLog property alias transactionLog: transactionLog
signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage property string compilationErrorMessage
property bool assemblyMode: false
objectName: "debugPanel" objectName: "debugPanel"
color: "#ededed" color: "#ededed"
clip: true clip: true
@ -23,6 +26,11 @@ Rectangle {
forceActiveFocus(); forceActiveFocus();
} }
onAssemblyModeChanged:
{
Debugger.updateMode();
}
function displayCompilationErrorIfAny() function displayCompilationErrorIfAny()
{ {
debugScrollArea.visible = false; debugScrollArea.visible = false;
@ -51,6 +59,11 @@ Rectangle {
forceActiveFocus(); forceActiveFocus();
} }
function setBreakpoints(bp)
{
Debugger.setBreakpoints(bp);
}
Connections { Connections {
target: clientModel target: clientModel
onDebugDataReady: { onDebugDataReady: {
@ -60,10 +73,8 @@ Rectangle {
Connections { Connections {
target: codeModel target: codeModel
onCompilationComplete: onCompilationComplete: {
{
debugPanel.compilationErrorMessage = ""; debugPanel.compilationErrorMessage = "";
update(null, false);
} }
onCompilationError: { onCompilationError: {
@ -199,11 +210,23 @@ Rectangle {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
color: "transparent" color: "transparent"
width: stateListContainer.width width: parent.width * 0.4
RowLayout { RowLayout {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
id: jumpButtons id: jumpButtons
spacing: 3 spacing: 3
StepActionImage
{
id: runBackAction;
enabledStateImg: "qrc:/qml/img/jumpoutback.png"
disableStateImg: "qrc:/qml/img/jumpoutbackdisabled.png"
onClicked: Debugger.runBack()
width: 30
height: 30
buttonShortcut: "Ctrl+Shift+F5"
buttonTooltip: qsTr("Run Back")
}
StepActionImage StepActionImage
{ {
id: jumpOutBackAction; id: jumpOutBackAction;
@ -275,6 +298,20 @@ Rectangle {
buttonShortcut: "Shift+F11" buttonShortcut: "Shift+F11"
buttonTooltip: qsTr("Step Out Forward") buttonTooltip: qsTr("Step Out Forward")
} }
StepActionImage
{
id: runForwardAction
enabledStateImg: "qrc:/qml/img/jumpoutforward.png"
disableStateImg: "qrc:/qml/img/jumpoutforwarddisabled.png"
onClicked: Debugger.runForward()
width: 30
height: 30
buttonShortcut: "Ctrl+F5"
buttonTooltip: qsTr("Run Forward")
}
} }
} }
@ -282,7 +319,7 @@ Rectangle {
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
width: debugInfoContainer.width width: parent.width * 0.6
color: "transparent" color: "transparent"
Slider { Slider {
id: statesSlider id: statesSlider
@ -317,6 +354,7 @@ Rectangle {
height: 405 height: 405
implicitHeight: 405 implicitHeight: 405
color: "transparent" color: "transparent"
visible: assemblyMode
Rectangle Rectangle
{ {

25
mix/qml/MainContent.qml

@ -26,6 +26,7 @@ Rectangle {
property alias projectViewVisible: projectList.visible property alias projectViewVisible: projectList.visible
property alias runOnProjectLoad: mainSettings.runOnProjectLoad property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: rightView property alias rightPane: rightView
property alias codeEditor: codeEditor
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property bool firstCompile: true property bool firstCompile: true
@ -40,6 +41,20 @@ Rectangle {
} }
} }
Connections {
target: rightView
onDebugExecuteLocation: {
codeEditor.highlightExecution(documentId, location);
}
}
Connections {
target: codeEditor
onBreakpointsChanged: {
rightPane.setBreakpoints(codeEditor.getBreakpoints());
}
}
function startQuickDebugging() function startQuickDebugging()
{ {
ensureRightView(); ensureRightView();
@ -75,6 +90,11 @@ Rectangle {
codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical); codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical);
} }
//TODO: move this to debugger.js after refactoring, introduce events
function toggleBreakpoint() {
codeEditor.toggleBreakpoint();
}
function displayCompilationErrorIfAny() function displayCompilationErrorIfAny()
{ {
rightView.visible = true; rightView.visible = true;
@ -174,6 +194,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
orientation: Qt.Vertical orientation: Qt.Vertical
CodeEditorView { CodeEditorView {
id: codeEditor
height: parent.height * 0.6 height: parent.height * 0.6
anchors.top: parent.top anchors.top: parent.top
Layout.fillWidth: true Layout.fillWidth: true
@ -202,7 +223,3 @@ Rectangle {
} }
} }
} }

28
mix/qml/WebCodeEditor.qml

@ -2,15 +2,16 @@ import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0 import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
import QtWebEngine 1.0 import QtWebEngine 1.0
import QtWebEngine.experimental 1.0 import QtWebEngine.experimental 1.0
Item { Item {
signal editorTextChanged signal editorTextChanged;
signal breakpointsChanged;
property string currentText: "" property string currentText: ""
property string currentMode: "" property string currentMode: ""
property bool initialized: false property bool initialized: false
property var currentBreakpoints: [];
function setText(text, mode) { function setText(text, mode) {
currentText = text; currentText = text;
@ -37,6 +38,18 @@ Item {
} }
} }
function highlightExecution(location) {
editorBrowser.runJavaScript("highlightExecution(" + location.start + "," + location.end + ")");
}
function getBreakpoints() {
return currentBreakpoints;
}
function toggleBreakpoint() {
editorBrowser.runJavaScript("toggleBreakpoint()");
}
Connections { Connections {
target: appContext target: appContext
onClipboardChanged: syncClipboard() onClipboardChanged: syncClipboard()
@ -80,6 +93,17 @@ Item {
}); });
} }
}); });
editorBrowser.runJavaScript("getBreakpointsChanged()", function(result) {
if (result === true) {
editorBrowser.runJavaScript("getBreakpoints()" , function(bp) {
if (currentBreakpoints !== bp) {
currentBreakpoints = bp;
breakpointsChanged();
}
});
}
});
} }
} }
} }

4
mix/qml/html/cm/codemirror.css

@ -8,6 +8,10 @@
font-size:12px font-size:12px
} }
/* BREAKPOINTS */
.breakpoints {width: .8em;}
.breakpoint { color: #822; }
/* PADDING */ /* PADDING */
.CodeMirror-lines { .CodeMirror-lines {

6
mix/qml/html/cm/solarized.css

@ -163,3 +163,9 @@ view-port
.cm-s-solarized.cm-s-light .CodeMirror-activeline-background { .cm-s-solarized.cm-s-light .CodeMirror-activeline-background {
background: rgba(0, 0, 0, 0.10); background: rgba(0, 0, 0, 0.10);
} }
/* Code execution */
.CodeMirror-exechighlight {
background: rgba(255, 255, 255, 0.10);
}

50
mix/qml/html/codeeditor.js

@ -4,6 +4,7 @@ var editor = CodeMirror(document.body, {
//styleActiveLine: true, //styleActiveLine: true,
matchBrackets: true, matchBrackets: true,
autofocus: true, autofocus: true,
gutters: ["CodeMirror-linenumbers", "breakpoints"]
}); });
@ -13,6 +14,7 @@ editor.setOption("indentWithTabs", true);
editor.setOption("fullScreen", true); editor.setOption("fullScreen", true);
editor.changeRegistered = false; editor.changeRegistered = false;
editor.breakpointsChangeRegistered = false;
editor.on("change", function(eMirror, object) { editor.on("change", function(eMirror, object) {
editor.changeRegistered = true; editor.changeRegistered = true;
@ -33,6 +35,28 @@ editor.setOption("extraKeys", {
}}); }});
} }
makeMarker = function() {
var marker = document.createElement("div");
marker.style.color = "#822";
marker.innerHTML = "●";
return marker;
};
toggleBreakpointLine = function(n) {
var info = editor.lineInfo(n);
editor.setGutterMarker(n, "breakpoints", info.gutterMarkers ? null : makeMarker());
editor.breakpointsChangeRegistered = true;
}
editor.on("gutterClick", function(cm, n) {
toggleBreakpointLine(n);
});
toggleBreakpoint = function() {
var line = editor.getCursor().line;
toggleBreakpointLine(line);
}
getTextChanged = function() { getTextChanged = function() {
return editor.changeRegistered; return editor.changeRegistered;
}; };
@ -42,6 +66,25 @@ getText = function() {
return editor.getValue(); return editor.getValue();
}; };
getBreakpointsChanged = function() {
return editor.changeRegistered || editor.breakpointsChangeRegistered; //TODO: track new lines
};
getBreakpoints = function() {
var locations = [];
editor.breakpointsChangeRegistered = false;
var doc = editor.doc;
doc.iter(function(line) {
if (line.gutterMarkers && line.gutterMarkers["breakpoints"]) {
var l = doc.getLineNumber(line);
locations.push({
start: editor.indexFromPos({ line: l, ch: 0}),
end: editor.indexFromPos({ line: l + 1, ch: 0})
});;
}
});
return locations;
};
setTextBase64 = function(text) { setTextBase64 = function(text) {
editor.setValue(window.atob(text)); editor.setValue(window.atob(text));
@ -60,3 +103,10 @@ setMode = function(mode) {
setClipboardBase64 = function(text) { setClipboardBase64 = function(text) {
clipboard = window.atob(text); clipboard = window.atob(text);
}; };
var executionMark;
highlightExecution = function(start, end) {
if (executionMark)
executionMark.clear();
executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" });
}

147
mix/qml/js/Debugger.js

@ -5,6 +5,9 @@ var currentSelectedState = null;
var currentDisplayedState = null; var currentDisplayedState = null;
var debugData = null; var debugData = null;
var codeMap = null; var codeMap = null;
var locations = [];
var locationMap = {};
var breakpoints = {};
function init(data) function init(data)
{ {
@ -22,6 +25,8 @@ function init(data)
currentSelectedState = null; currentSelectedState = null;
currentDisplayedState = null; currentDisplayedState = null;
debugData = null; debugData = null;
locations = [];
locationMap = {};
return; return;
} }
@ -30,9 +35,59 @@ function init(data)
currentDisplayedState = 0; currentDisplayedState = 0;
setupInstructions(currentSelectedState); setupInstructions(currentSelectedState);
setupCallData(currentSelectedState); setupCallData(currentSelectedState);
statesSlider.maximumValue = data.states.length - 1; initLocations();
initSlider();
selectState(currentSelectedState);
}
function updateMode()
{
initSlider();
}
function initLocations()
{
locations = [];
if (debugData.states.length === 0)
return;
var nullLocation = { start: -1, end: -1, documentId: "", state: 0 };
var prevLocation = nullLocation;
for (var i = 0; i < debugData.states.length - 1; i++) {
var code = debugData.states[i].code;
var location = code.documentId ? code.locations[codeStr(i)] : nullLocation;
if (location.start !== prevLocation.start || location.end !== prevLocation.end || code.documentId !== prevLocation.documentId)
{
prevLocation = { start: location.start, end: location.end, documentId: code.documentId, state: i };
locations.push(prevLocation);
}
locationMap[i] = locations.length - 1;
}
locations.push({ start: -1, end: -1, documentId: code.documentId, state: i });
locationMap[debugData.states.length - 1] = locations.length - 1;
}
function setBreakpoints(bp)
{
breakpoints = bp;
}
function srcMode()
{
return !assemblyMode && locations.length;
}
function initSlider()
{
if (!debugData)
statesSlider.maximumValue = 0;
else if (srcMode()) {
statesSlider.maximumValue = locations.length - 1;
} else {
statesSlider.maximumValue = debugData.states.length - 1;
}
statesSlider.value = 0; statesSlider.value = 0;
select(currentSelectedState);
} }
function setupInstructions(stateIndex) function setupInstructions(stateIndex)
@ -54,11 +109,13 @@ function setupCallData(stateIndex)
function moveSelection(incr) function moveSelection(incr)
{ {
var prevState = currentSelectedState; if (srcMode()) {
if (currentSelectedState + incr >= 0) var locationIndex = locationMap[currentSelectedState];
{ if (locationIndex + incr >= 0 && locationIndex + incr < locations.length)
if (currentSelectedState + incr < debugData.states.length) selectState(locations[locationIndex + incr].state);
select(currentSelectedState + incr); } else {
if (currentSelectedState + incr >= 0 && currentSelectedState + incr < debugData.states.length)
selectState(currentSelectedState + incr);
} }
} }
@ -67,7 +124,7 @@ function display(stateIndex)
if (stateIndex < 0) if (stateIndex < 0)
stateIndex = 0; stateIndex = 0;
if (stateIndex >= debugData.states.length) if (stateIndex >= debugData.states.length)
stateIndex = debugData.state.length - 1; stateIndex = debugData.states.length - 1;
if (debugData.states[stateIndex].codeIndex !== debugData.states[currentDisplayedState].codeIndex) if (debugData.states[stateIndex].codeIndex !== debugData.states[currentDisplayedState].codeIndex)
setupInstructions(stateIndex); setupInstructions(stateIndex);
if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex) if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex)
@ -77,6 +134,9 @@ function display(stateIndex)
highlightSelection(codeLine); highlightSelection(codeLine);
completeCtxInformation(state); completeCtxInformation(state);
currentDisplayedState = stateIndex; currentDisplayedState = stateIndex;
var docId = debugData.states[stateIndex].code.documentId;
if (docId)
debugExecuteLocation(docId, locations[locationMap[stateIndex]]);
} }
function displayFrame(frameIndex) function displayFrame(frameIndex)
@ -88,26 +148,39 @@ function displayFrame(frameIndex)
display(state.levels[frameIndex - 1]); display(state.levels[frameIndex - 1]);
} }
function select(stateIndex) function select(index)
{
if (srcMode())
selectState(locations[index].state);
else
selectState(index);
}
function selectState(stateIndex)
{ {
display(stateIndex); display(stateIndex);
currentSelectedState = stateIndex; currentSelectedState = stateIndex;
var state = debugData.states[stateIndex]; var state = debugData.states[stateIndex];
statesSlider.value = stateIndex;
jumpIntoForwardAction.enabled(stateIndex < debugData.states.length - 1) jumpIntoForwardAction.enabled(stateIndex < debugData.states.length - 1)
jumpIntoBackAction.enabled(stateIndex > 0); jumpIntoBackAction.enabled(stateIndex > 0);
jumpOverForwardAction.enabled(stateIndex < debugData.states.length - 1); jumpOverForwardAction.enabled(stateIndex < debugData.states.length - 1);
jumpOverBackAction.enabled(stateIndex > 0); jumpOverBackAction.enabled(stateIndex > 0);
jumpOutBackAction.enabled(state.levels.length > 1); jumpOutBackAction.enabled(state.levels.length > 1);
jumpOutForwardAction.enabled(state.levels.length > 1); jumpOutForwardAction.enabled(state.levels.length > 1);
runForwardAction.enabled(stateIndex < debugData.states.length - 1)
runBackAction.enabled(stateIndex > 0);
var callStackData = []; var callStackData = [];
for (var l = 0; l < state.levels.length; l++) { for (var l = 0; l < state.levels.length; l++) {
var address = debugData.states[state.levels[l] + 1].address; var address = debugData.states[state.levels[l] + 1].code.address;
callStackData.push(address); callStackData.push(address);
} }
callStackData.push(debugData.states[0].address); callStackData.push(debugData.states[0].code.address);
callStack.listModel = callStackData; callStack.listModel = callStackData;
if (srcMode())
statesSlider.value = locationMap[stateIndex];
else
statesSlider.value = stateIndex;
} }
function codeStr(stateIndex) function codeStr(stateIndex)
@ -147,6 +220,24 @@ function isReturnInstruction(index)
return state.instruction === "RETURN" return state.instruction === "RETURN"
} }
function locationsIntersect(l1, l2)
{
return l1.start <= l2.end && l1.end >= l2.start;
}
function breakpointHit(i)
{
var bpLocations = breakpoints[debugData.states[i].code.documentId];
if (bpLocations) {
var location = locations[locationMap[i]];
if (location.start >= 0 && location.end >= location.start)
for (var b = 0; b < bpLocations.length; b++)
if (locationsIntersect(location, bpLocations[b]))
return true;
}
return false;
}
function stepIntoBack() function stepIntoBack()
{ {
moveSelection(-1); moveSelection(-1);
@ -173,25 +264,48 @@ function stepIntoForward()
moveSelection(1); moveSelection(1);
} }
function runBack()
{
var i = currentSelectedState - 1;
while (i > 0 && !breakpointHit(i)) {
--i;
}
selectState(i);
}
function runForward()
{
var i = currentSelectedState + 1;
while (i < debugData.states.length - 1 && !breakpointHit(i)) {
++i;
}
selectState(i);
}
function stepOutBack() function stepOutBack()
{ {
var i = currentSelectedState - 1; var i = currentSelectedState - 1;
var depth = 0; var depth = 0;
while (--i >= 0) while (--i >= 0) {
if (breakpointHit(i))
break;
if (isCallInstruction(i)) if (isCallInstruction(i))
if (depth == 0) if (depth == 0)
break; break;
else depth--; else depth--;
else if (isReturnInstruction(i)) else if (isReturnInstruction(i))
depth++; depth++;
select(i); }
selectState(i);
} }
function stepOutForward() function stepOutForward()
{ {
var i = currentSelectedState; var i = currentSelectedState;
var depth = 0; var depth = 0;
while (++i < debugData.states.length) while (++i < debugData.states.length) {
if (breakpointHit(i))
break;
if (isReturnInstruction(i)) if (isReturnInstruction(i))
if (depth == 0) if (depth == 0)
break; break;
@ -199,7 +313,8 @@ function stepOutForward()
depth--; depth--;
else if (isCallInstruction(i)) else if (isCallInstruction(i))
depth++; depth++;
select(i + 1); }
selectState(i + 1);
} }
function jumpTo(value) function jumpTo(value)

4
mix/qml/js/ProjectModel.js

@ -66,7 +66,7 @@ function saveProject() {
function loadProject(path) { function loadProject(path) {
closeProject(); closeProject();
console.log("loading project at " + path); console.log("Loading project at " + path);
var projectFile = path + projectFileName; var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile); var json = fileIo.readFile(projectFile);
var projectData = JSON.parse(json); var projectData = JSON.parse(json);
@ -189,7 +189,7 @@ function doCloseProject() {
function doCreateProject(title, path) { function doCreateProject(title, path) {
closeProject(); closeProject();
console.log("creating project " + title + " at " + path); console.log("Creating project " + title + " at " + path);
if (path[path.length - 1] !== "/") if (path[path.length - 1] !== "/")
path += "/"; path += "/";
var dirPath = path + title + "/"; var dirPath = path + title + "/";

24
mix/qml/main.qml

@ -37,7 +37,6 @@ ApplicationWindow {
} }
Menu { Menu {
title: qsTr("Deploy") title: qsTr("Deploy")
MenuItem { action: debugRunAction }
MenuItem { action: mineAction } MenuItem { action: mineAction }
MenuSeparator {} MenuSeparator {}
MenuItem { action: editStatesAction } MenuItem { action: editStatesAction }
@ -46,6 +45,12 @@ ApplicationWindow {
MenuSeparator {} MenuSeparator {}
MenuItem { action: toggleRunOnLoadAction } MenuItem { action: toggleRunOnLoadAction }
} }
Menu {
title: qsTr("Debug")
MenuItem { action: debugRunAction }
MenuSeparator {}
MenuItem { action: toggleAssemblyDebuggingAction }
}
Menu { Menu {
title: qsTr("Windows") title: qsTr("Windows")
MenuItem { action: openNextDocumentAction } MenuItem { action: openNextDocumentAction }
@ -129,6 +134,15 @@ ApplicationWindow {
enabled: codeModel.hasContract && !clientModel.running enabled: codeModel.hasContract && !clientModel.running
} }
Action {
id: toggleAssemblyDebuggingAction
text: qsTr("Show VM Code")
shortcut: "Ctrl+Alt+V"
onTriggered: mainContent.rightPane.assemblyMode = !mainContent.rightPane.assemblyMode;
checked: mainContent.rightPane.assemblyMode;
enabled: true
}
Action { Action {
id: toggleWebPreviewAction id: toggleWebPreviewAction
text: qsTr("Show Web View") text: qsTr("Show Web View")
@ -294,6 +308,14 @@ ApplicationWindow {
onTriggered: projectModel.openPrevDocument(); onTriggered: projectModel.openPrevDocument();
} }
Action {
id: toggleBreakpointAction
text: qsTr("Toggle Breakpoint")
shortcut: "F9"
enabled: mainContent.codeEditor.editingContract();
onTriggered: mainContent.toggleBreakpoint();
}
Action { Action {
id: deployViaRpcAction id: deployViaRpcAction
text: qsTr("Deploy to Network") text: qsTr("Deploy to Network")

11
test/SolidityEndToEndTest.cpp

@ -1619,9 +1619,11 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
function sendAmount(uint amount) returns (uint256 bal) { function sendAmount(uint amount) returns (uint256 bal) {
return h.getBalance.value(amount)(); return h.getBalance.value(amount)();
} }
function outOfGas() returns (bool flagBefore, bool flagAfter, uint myBal) { function outOfGas() returns (bool ret) {
flagBefore = h.getFlag(); h.setFlag.gas(2)(); // should fail due to OOG
h.setFlag.gas(2)(); // should fail due to OOG, return value can be garbage return true;
}
function checkState() returns (bool flagAfter, uint myBal) {
flagAfter = h.getFlag(); flagAfter = h.getFlag();
myBal = this.balance; myBal = this.balance;
} }
@ -1630,7 +1632,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
compileAndRun(sourceCode, 20); compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5)); BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
// call to helper should not succeed but amount should be transferred anyway // call to helper should not succeed but amount should be transferred anyway
BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == encodeArgs(false, false, 20 - 5)); BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == bytes());
BOOST_REQUIRE(callContractFunction("checkState()", 5) == encodeArgs(false, 20 - 5));
} }
BOOST_AUTO_TEST_CASE(value_complex) BOOST_AUTO_TEST_CASE(value_complex)

Loading…
Cancel
Save