Browse Source

Merge remote-tracking branch 'up/develop' into accountManagement

cl-refactor
yann300 10 years ago
parent
commit
e322289c04
  1. 9
      alethzero/MainWin.cpp
  2. 20
      docker/Dockerfile
  3. 10
      libsolidity/CompilerStack.cpp
  4. 18
      libsolidity/CompilerStack.h
  5. 13
      libsolidity/ExpressionCompiler.cpp
  6. 6
      libsolidity/LValue.cpp
  7. 4
      libwhisper/WhisperHost.cpp
  8. 18
      mix/ClientModel.cpp
  9. 2
      mix/CodeModel.cpp
  10. 10
      mix/CodeModel.h
  11. 9
      mix/DebuggingStateWrapper.cpp
  12. 30
      mix/DebuggingStateWrapper.h
  13. 13
      mix/MachineStates.h
  14. 6
      mix/MixClient.cpp
  15. 43
      mix/qml/CodeEditorView.qml
  16. 48
      mix/qml/Debugger.qml
  17. 25
      mix/qml/MainContent.qml
  18. 28
      mix/qml/WebCodeEditor.qml
  19. 4
      mix/qml/html/cm/codemirror.css
  20. 6
      mix/qml/html/cm/solarized.css
  21. 50
      mix/qml/html/codeeditor.js
  22. 147
      mix/qml/js/Debugger.js
  23. 4
      mix/qml/js/ProjectModel.js
  24. 24
      mix/qml/main.qml
  25. 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>";
else
s << "<br/>Pre: <i>Nothing is before Phil</i>";
BlockReceipts receipts = ethereum()->blockChain().receipts(h);
unsigned ii = 0;
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/>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
{

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 automake unzip libgmp-dev libtool libleveldb-dev yasm libminiupnpc-dev libreadline-dev scons
RUN apt-get install -qy libjsoncpp-dev libargtable2-dev
# NCurses based GUI (not optional though for a succesful compilation, see https://github.com/ethereum/cpp-ethereum/issues/452 )
RUN apt-get install -qy libncurses5-dev
# Qt-based GUI
# RUN apt-get install -qy qtbase5-dev qt5-default qtdeclarative5-dev libqt5webkit5-dev
RUN apt-get install -qy libncurses5-dev libcurl4-openssl-dev wget
RUN apt-get install -qy libjsoncpp-dev libargtable2-dev libmicrohttpd-dev
# Ethereum PPA
RUN apt-get install -qy software-properties-common
RUN add-apt-repository ppa:ethereum/ethereum
RUN add-apt-repository ppa:ethereum/ethereum-dev
RUN apt-get update
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)
RUN git clone --depth=1 https://github.com/ethereum/cpp-ethereum
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
ENTRYPOINT ["/usr/local/bin/eth"]

10
libsolidity/CompilerStack.cpp

@ -155,6 +155,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
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
{
return getContract(_contractName).bytecode;

18
libsolidity/CompilerStack.h

@ -26,12 +26,22 @@
#include <ostream>
#include <string>
#include <memory>
#include <vector>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
namespace dev {
namespace solidity {
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
}
namespace solidity
{
// forward declarations
class Scanner;
@ -85,6 +95,10 @@ public:
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.
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.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;

13
libsolidity/ExpressionCompiler.cpp

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

6
libsolidity/LValue.cpp

@ -240,14 +240,14 @@ StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const
solAssert(m_arrayType.isDynamicallySized(), "");
}
void StorageArrayLength::retrieveValue(SourceLocation const& _location, bool _remove) const
void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const
{
if (!_remove)
m_context << eth::Instruction::DUP1;
m_context << eth::Instruction::SLOAD;
}
void StorageArrayLength::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _move) const
{
if (_move)
m_context << eth::Instruction::SWAP1;
@ -256,7 +256,7 @@ void StorageArrayLength::storeValue(Type const& _sourceType, SourceLocation cons
ArrayUtils(m_context).resizeDynamicArray(m_arrayType);
}
void StorageArrayLength::setToZero(SourceLocation const& _location, bool _removeReference) const
void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const
{
if (!_removeReference)
m_context << eth::Instruction::DUP1;

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());
if (_m.expiry() <= time(0))
if (_m.expiry() <= (unsigned)time(0))
return;
auto h = _m.sha3();
@ -171,7 +171,7 @@ void WhisperHost::cleanup()
{
// remove old messages.
// should be called every now and again.
auto now = time(0);
unsigned now = (unsigned)time(0);
WriteGuard l(x_messages);
for (auto it = m_expiryQueue.begin(); it != m_expiryQueue.end() && it->first <= now; it = m_expiryQueue.erase(it))
m_messages.erase(it->second);

18
mix/ClientModel.cpp

@ -319,8 +319,21 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QDebugData* debugData = new QDebugData();
QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership);
QList<QCode*> codes;
for (bytes const& code: _t.executionCode)
codes.push_back(QMachineState::getHumanReadableCode(debugData, code));
for (MachineCode const& code: _t.executionCode)
{
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;
for (bytes const& d: _t.transactionData)
@ -344,7 +357,6 @@ void ClientModel::emptyRecord()
debugDataReady(new QDebugData());
}
void ClientModel::debugRecord(unsigned _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));
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership);
m_bytes = _compiler.getBytecode(_contractName.toStdString());
m_assemblyItems = _compiler.getRuntimeAssemblyItems(_contractName.toStdString());
m_constructorAssemblyItems = _compiler.getAssemblyItems(_contractName.toStdString());
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty())

10
mix/CodeModel.h

@ -30,6 +30,7 @@
#include <QHash>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libevmcore/Assembly.h>
class QTextDocument;
@ -70,7 +71,7 @@ class CompiledContract: public QObject
Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT)
Q_PROPERTY(QString codeHex READ codeHex CONSTANT)
Q_PROPERTY(QString documentId MEMBER m_documentId CONSTANT)
Q_PROPERTY(QString documentId READ documentId CONSTANT)
public:
/// Successful compilation result constructor
@ -86,6 +87,11 @@ public:
QString codeHex() const;
/// @returns contract definition in JSON format
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:
uint m_sourceHash;
@ -94,6 +100,8 @@ private:
dev::bytes m_bytes;
QString m_contractInterface;
QString m_documentId;
eth::AssemblyItems m_assemblyItems;
eth::AssemblyItems m_constructorAssemblyItems;
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;
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
}
}
return new QCode(_owner, std::move(codeStr));
return new QCode(_owner, QString::fromStdString(toString(_address)), std::move(codeStr));
}
QBigInt* QMachineState::gasCost()
@ -152,11 +152,6 @@ QVariantList QMachineState::levels()
return levelList;
}
QString QMachineState::address()
{
return QString::fromStdString(toString(m_state.address));
}
QString QMachineState::instruction()
{
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name);

30
mix/DebuggingStateWrapper.h

@ -53,6 +53,22 @@ private:
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
*/
@ -60,12 +76,19 @@ class QCode: public QObject
{
Q_OBJECT
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:
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:
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* gas READ gas 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 debugStorage READ debugStorage CONSTANT)
Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT)
@ -133,8 +155,6 @@ public:
unsigned codeIndex() { return m_state.codeIndex; }
/// Get the call data id
unsigned dataIndex() { return m_state.dataIndex; }
/// Get address for call stack
QString address();
/// Get gas cost.
QBigInt* gasCost();
/// Get gas used.
@ -158,7 +178,7 @@ public:
/// Set the current processed machine state.
void setState(MachineState _state) { m_state = _state; }
/// 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
static QCallData* getDebugCallData(QObject* _owner, bytes const& _data);

13
mix/MachineStates.h

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

6
mix/MixClient.cpp

@ -115,7 +115,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
execution.setup(&rlp);
std::vector<MachineState> machineStates;
std::vector<unsigned> levels;
std::vector<bytes> codes;
std::vector<MachineCode> codes;
std::map<bytes const*, unsigned> codeIndexes;
std::vector<bytes> data;
std::map<bytesConstRef const*, unsigned> dataIndexes;
@ -135,7 +135,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
else
{
codeIndex = codes.size();
codes.push_back(ext.code);
codes.push_back(MachineCode({ext.myAddress, ext.code}));
codeIndexes[&ext.code] = codeIndex;
}
lastCode = &ext.code;
@ -160,7 +160,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
else
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}));
};

43
mix/qml/CodeEditorView.qml

@ -7,6 +7,7 @@ Item {
id: codeEditorView
property string currentDocumentId: ""
signal documentEdit(string documentId)
signal breakpointsChanged(string documentId)
function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) {
@ -45,9 +46,51 @@ Item {
if (document.isContract)
codeModel.registerCodeChange(document.documentId, editor.getText());
});
editor.onBreakpointsChanged.connect(function() {
if (document.isContract)
breakpointsChanged(document.documentId);
});
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;
Connections {

48
mix/qml/Debugger.qml

@ -12,7 +12,10 @@ Rectangle {
id: debugPanel
property alias transactionLog: transactionLog
signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage
property bool assemblyMode: false
objectName: "debugPanel"
color: "#ededed"
clip: true
@ -23,6 +26,11 @@ Rectangle {
forceActiveFocus();
}
onAssemblyModeChanged:
{
Debugger.updateMode();
}
function displayCompilationErrorIfAny()
{
debugScrollArea.visible = false;
@ -51,6 +59,11 @@ Rectangle {
forceActiveFocus();
}
function setBreakpoints(bp)
{
Debugger.setBreakpoints(bp);
}
Connections {
target: clientModel
onDebugDataReady: {
@ -60,10 +73,8 @@ Rectangle {
Connections {
target: codeModel
onCompilationComplete:
{
onCompilationComplete: {
debugPanel.compilationErrorMessage = "";
update(null, false);
}
onCompilationError: {
@ -199,11 +210,23 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
color: "transparent"
width: stateListContainer.width
width: parent.width * 0.4
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
id: jumpButtons
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
{
id: jumpOutBackAction;
@ -275,6 +298,20 @@ Rectangle {
buttonShortcut: "Shift+F11"
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.bottom: parent.bottom
anchors.right: parent.right
width: debugInfoContainer.width
width: parent.width * 0.6
color: "transparent"
Slider {
id: statesSlider
@ -317,6 +354,7 @@ Rectangle {
height: 405
implicitHeight: 405
color: "transparent"
visible: assemblyMode
Rectangle
{

25
mix/qml/MainContent.qml

@ -26,6 +26,7 @@ Rectangle {
property alias projectViewVisible: projectList.visible
property alias runOnProjectLoad: mainSettings.runOnProjectLoad
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 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()
{
ensureRightView();
@ -75,6 +90,11 @@ Rectangle {
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()
{
rightView.visible = true;
@ -174,6 +194,7 @@ Rectangle {
anchors.fill: parent
orientation: Qt.Vertical
CodeEditorView {
id: codeEditor
height: parent.height * 0.6
anchors.top: parent.top
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.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
import QtWebEngine 1.0
import QtWebEngine.experimental 1.0
Item {
signal editorTextChanged
signal editorTextChanged;
signal breakpointsChanged;
property string currentText: ""
property string currentMode: ""
property bool initialized: false
property var currentBreakpoints: [];
function setText(text, mode) {
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 {
target: appContext
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
}
/* BREAKPOINTS */
.breakpoints {width: .8em;}
.breakpoint { color: #822; }
/* PADDING */
.CodeMirror-lines {

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

@ -163,3 +163,9 @@ view-port
.cm-s-solarized.cm-s-light .CodeMirror-activeline-background {
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,
matchBrackets: true,
autofocus: true,
gutters: ["CodeMirror-linenumbers", "breakpoints"]
});
@ -13,6 +14,7 @@ editor.setOption("indentWithTabs", true);
editor.setOption("fullScreen", true);
editor.changeRegistered = false;
editor.breakpointsChangeRegistered = false;
editor.on("change", function(eMirror, object) {
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() {
return editor.changeRegistered;
};
@ -42,6 +66,25 @@ getText = function() {
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) {
editor.setValue(window.atob(text));
@ -60,3 +103,10 @@ setMode = function(mode) {
setClipboardBase64 = function(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 debugData = null;
var codeMap = null;
var locations = [];
var locationMap = {};
var breakpoints = {};
function init(data)
{
@ -22,6 +25,8 @@ function init(data)
currentSelectedState = null;
currentDisplayedState = null;
debugData = null;
locations = [];
locationMap = {};
return;
}
@ -30,9 +35,59 @@ function init(data)
currentDisplayedState = 0;
setupInstructions(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;
select(currentSelectedState);
}
function setupInstructions(stateIndex)
@ -54,11 +109,13 @@ function setupCallData(stateIndex)
function moveSelection(incr)
{
var prevState = currentSelectedState;
if (currentSelectedState + incr >= 0)
{
if (currentSelectedState + incr < debugData.states.length)
select(currentSelectedState + incr);
if (srcMode()) {
var locationIndex = locationMap[currentSelectedState];
if (locationIndex + incr >= 0 && locationIndex + incr < locations.length)
selectState(locations[locationIndex + incr].state);
} else {
if (currentSelectedState + incr >= 0 && currentSelectedState + incr < debugData.states.length)
selectState(currentSelectedState + incr);
}
}
@ -67,7 +124,7 @@ function display(stateIndex)
if (stateIndex < 0)
stateIndex = 0;
if (stateIndex >= debugData.states.length)
stateIndex = debugData.state.length - 1;
stateIndex = debugData.states.length - 1;
if (debugData.states[stateIndex].codeIndex !== debugData.states[currentDisplayedState].codeIndex)
setupInstructions(stateIndex);
if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex)
@ -77,6 +134,9 @@ function display(stateIndex)
highlightSelection(codeLine);
completeCtxInformation(state);
currentDisplayedState = stateIndex;
var docId = debugData.states[stateIndex].code.documentId;
if (docId)
debugExecuteLocation(docId, locations[locationMap[stateIndex]]);
}
function displayFrame(frameIndex)
@ -88,26 +148,39 @@ function displayFrame(frameIndex)
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);
currentSelectedState = stateIndex;
var state = debugData.states[stateIndex];
statesSlider.value = stateIndex;
jumpIntoForwardAction.enabled(stateIndex < debugData.states.length - 1)
jumpIntoBackAction.enabled(stateIndex > 0);
jumpOverForwardAction.enabled(stateIndex < debugData.states.length - 1);
jumpOverBackAction.enabled(stateIndex > 0);
jumpOutBackAction.enabled(state.levels.length > 1);
jumpOutForwardAction.enabled(state.levels.length > 1);
runForwardAction.enabled(stateIndex < debugData.states.length - 1)
runBackAction.enabled(stateIndex > 0);
var callStackData = [];
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(debugData.states[0].address);
callStackData.push(debugData.states[0].code.address);
callStack.listModel = callStackData;
if (srcMode())
statesSlider.value = locationMap[stateIndex];
else
statesSlider.value = stateIndex;
}
function codeStr(stateIndex)
@ -147,6 +220,24 @@ function isReturnInstruction(index)
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()
{
moveSelection(-1);
@ -173,25 +264,48 @@ function stepIntoForward()
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()
{
var i = currentSelectedState - 1;
var depth = 0;
while (--i >= 0)
while (--i >= 0) {
if (breakpointHit(i))
break;
if (isCallInstruction(i))
if (depth == 0)
break;
else depth--;
else if (isReturnInstruction(i))
depth++;
select(i);
}
selectState(i);
}
function stepOutForward()
{
var i = currentSelectedState;
var depth = 0;
while (++i < debugData.states.length)
while (++i < debugData.states.length) {
if (breakpointHit(i))
break;
if (isReturnInstruction(i))
if (depth == 0)
break;
@ -199,7 +313,8 @@ function stepOutForward()
depth--;
else if (isCallInstruction(i))
depth++;
select(i + 1);
}
selectState(i + 1);
}
function jumpTo(value)

4
mix/qml/js/ProjectModel.js

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

24
mix/qml/main.qml

@ -37,7 +37,6 @@ ApplicationWindow {
}
Menu {
title: qsTr("Deploy")
MenuItem { action: debugRunAction }
MenuItem { action: mineAction }
MenuSeparator {}
MenuItem { action: editStatesAction }
@ -46,6 +45,12 @@ ApplicationWindow {
MenuSeparator {}
MenuItem { action: toggleRunOnLoadAction }
}
Menu {
title: qsTr("Debug")
MenuItem { action: debugRunAction }
MenuSeparator {}
MenuItem { action: toggleAssemblyDebuggingAction }
}
Menu {
title: qsTr("Windows")
MenuItem { action: openNextDocumentAction }
@ -129,6 +134,15 @@ ApplicationWindow {
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 {
id: toggleWebPreviewAction
text: qsTr("Show Web View")
@ -294,6 +308,14 @@ ApplicationWindow {
onTriggered: projectModel.openPrevDocument();
}
Action {
id: toggleBreakpointAction
text: qsTr("Toggle Breakpoint")
shortcut: "F9"
enabled: mainContent.codeEditor.editingContract();
onTriggered: mainContent.toggleBreakpoint();
}
Action {
id: deployViaRpcAction
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) {
return h.getBalance.value(amount)();
}
function outOfGas() returns (bool flagBefore, bool flagAfter, uint myBal) {
flagBefore = h.getFlag();
h.setFlag.gas(2)(); // should fail due to OOG, return value can be garbage
function outOfGas() returns (bool ret) {
h.setFlag.gas(2)(); // should fail due to OOG
return true;
}
function checkState() returns (bool flagAfter, uint myBal) {
flagAfter = h.getFlag();
myBal = this.balance;
}
@ -1630,7 +1632,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
// 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)

Loading…
Cancel
Save