Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
572c15fb59
  1. 28
      libevmcore/Assembly.cpp
  2. 17
      libevmcore/Assembly.h
  3. 32
      libsolidity/Compiler.cpp
  4. 36
      libsolidity/CompilerContext.cpp
  5. 18
      libsolidity/CompilerContext.h
  6. 18
      libsolidity/ExpressionCompiler.cpp
  7. 89
      mix/ClientModel.cpp
  8. 2
      mix/ClientModel.h
  9. 111
      mix/CodeModel.cpp
  10. 31
      mix/CodeModel.h
  11. 7
      mix/DebuggingStateWrapper.cpp
  12. 45
      mix/DebuggingStateWrapper.h
  13. 2
      mix/MixClient.cpp
  14. 74
      mix/qml/CallStack.qml
  15. 193
      mix/qml/Debugger.qml
  16. 4
      mix/qml/StatusPane.qml
  17. 69
      mix/qml/StorageView.qml
  18. 31
      mix/qml/js/Debugger.js
  19. 2
      mix/res.qrc
  20. 40
      test/TestHelper.h

28
libevmcore/Assembly.cpp

@ -99,6 +99,8 @@ void Assembly::append(Assembly const& _a)
{ {
if (i.type() == Tag || i.type() == PushTag) if (i.type() == Tag || i.type() == PushTag)
i.m_data += m_usedTags; i.m_data += m_usedTags;
else if (i.type() == PushSub || i.type() == PushSubSize)
i.m_data += m_subs.size();
append(i); append(i);
} }
m_deposit = newDeposit; m_deposit = newDeposit;
@ -108,7 +110,7 @@ void Assembly::append(Assembly const& _a)
for (auto const& i: _a.m_strings) for (auto const& i: _a.m_strings)
m_strings.insert(i); m_strings.insert(i);
for (auto const& i: _a.m_subs) for (auto const& i: _a.m_subs)
m_subs.insert(i); m_subs.push_back(i);
assert(!_a.m_baseDeposit); assert(!_a.m_baseDeposit);
assert(!_a.m_totalDeposit); assert(!_a.m_totalDeposit);
@ -199,7 +201,6 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con
_out << _prefix << ".code:" << endl; _out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
{ {
string sourceLine = getLocationFromSources(_sourceCodes, i.getLocation());
_out << _prefix; _out << _prefix;
switch (i.m_type) switch (i.m_type)
{ {
@ -239,29 +240,30 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con
default: default:
BOOST_THROW_EXCEPTION(InvalidOpcode()); BOOST_THROW_EXCEPTION(InvalidOpcode());
} }
_out << string("\t\t") << sourceLine << endl; _out << string("\t\t") << getLocationFromSources(_sourceCodes, i.getLocation()) << endl;
} }
if (!m_data.empty() || !m_subs.empty()) if (!m_data.empty() || !m_subs.empty())
{ {
_out << _prefix << ".data:" << endl; _out << _prefix << ".data:" << endl;
for (auto const& i: m_data) for (auto const& i: m_data)
if (!m_subs.count(i.first)) if (u256(i.first) >= m_subs.size())
_out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl; _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl;
for (auto const& i: m_subs) for (size_t i = 0; i < m_subs.size(); ++i)
{ {
_out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl; _out << _prefix << " " << hex << i << ": " << endl;
i.second.streamRLP(_out, _prefix + " "); m_subs[i].streamRLP(_out, _prefix + " ", _sourceCodes);
} }
} }
return _out; return _out;
} }
AssemblyItem const& Assembly::append(AssemblyItem const& _i, SourceLocation const& _location) AssemblyItem const& Assembly::append(AssemblyItem const& _i)
{ {
m_deposit += _i.deposit(); m_deposit += _i.deposit();
m_items.push_back(_i); m_items.push_back(_i);
m_items.back().setLocation(_location); if (m_items.back().getLocation().isEmpty() && !m_currentSourceLocation.isEmpty())
m_items.back().setLocation(m_currentSourceLocation);
return back(); return back();
} }
@ -493,8 +495,8 @@ Assembly& Assembly::optimise(bool _enable)
copt << total << " optimisations done."; copt << total << " optimisations done.";
for (auto& i: m_subs) for (auto& sub: m_subs)
i.second.optimise(true); sub.optimise(true);
return *this; return *this;
} }
@ -511,8 +513,8 @@ bytes Assembly::assemble() const
unsigned bytesPerTag = dev::bytesRequired(totalBytes); unsigned bytesPerTag = dev::bytesRequired(totalBytes);
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
for (auto const& i: m_subs) for (size_t i = 0; i < m_subs.size(); ++i)
m_data[i.first] = i.second.assemble(); m_data[u256(i)] = m_subs[i].assemble();
unsigned bytesRequiredIncludingData = bytesRequired(); unsigned bytesRequiredIncludingData = bytesRequired();
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData); unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);

17
libevmcore/Assembly.h

@ -81,16 +81,16 @@ public:
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); } AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); }
AssemblyItem newSub(Assembly const& _sub) { h256 h = h256::random(s_fixedHashEngine); m_subs[h] = _sub; return AssemblyItem(PushSub, h); } AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash<std::string>()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); } AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash<std::string>()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); }
AssemblyItem newPushSubSize(h256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
AssemblyItem append() { return append(newTag()); } AssemblyItem append() { return append(newTag()); }
void append(Assembly const& _a); void append(Assembly const& _a);
void append(Assembly const& _a, int _deposit); void append(Assembly const& _a, int _deposit);
AssemblyItem const& append(AssemblyItem const& _i, SourceLocation const& _location = SourceLocation()); AssemblyItem const& append(AssemblyItem const& _i);
AssemblyItem const& append(std::string const& _data, SourceLocation const& _location = SourceLocation()) { return append(newPushString(_data), _location); } AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
AssemblyItem const& append(bytes const& _data, SourceLocation const& _location = SourceLocation()) { return append(newData(_data), _location); } AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; } AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; }
/// Pushes the final size of the current assembly itself. Use this when the code is modified /// Pushes the final size of the current assembly itself. Use this when the code is modified
/// after compilation and CODESIZE is not an option. /// after compilation and CODESIZE is not an option.
@ -119,6 +119,9 @@ public:
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
/// Changes the source location used for each appended item.
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
bytes assemble() const; bytes assemble() const;
Assembly& optimise(bool _enable); Assembly& optimise(bool _enable);
std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const; std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const;
@ -131,12 +134,14 @@ protected:
unsigned m_usedTags = 0; unsigned m_usedTags = 0;
AssemblyItems m_items; AssemblyItems m_items;
mutable std::map<h256, bytes> m_data; mutable std::map<h256, bytes> m_data;
std::map<h256, Assembly> m_subs; std::vector<Assembly> m_subs;
std::map<h256, std::string> m_strings; std::map<h256, std::string> m_strings;
int m_deposit = 0; int m_deposit = 0;
int m_baseDeposit = 0; int m_baseDeposit = 0;
int m_totalDeposit = 0; int m_totalDeposit = 0;
SourceLocation m_currentSourceLocation;
}; };
inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)

32
libsolidity/Compiler.cpp

@ -132,7 +132,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_constructor); CompilerContext::LocationSetter locationSetter(m_context, _constructor);
FunctionType constructorType(_constructor); FunctionType constructorType(_constructor);
if (!constructorType.getParameterTypes().empty()) if (!constructorType.getParameterTypes().empty())
{ {
@ -146,7 +146,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
void Compiler::appendConstructor(FunctionDefinition const& _constructor) void Compiler::appendConstructor(FunctionDefinition const& _constructor)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_constructor); CompilerContext::LocationSetter locationSetter(m_context, _constructor);
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0; unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
@ -192,10 +192,12 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
FunctionTypePointer const& functionType = it.second; FunctionTypePointer const& functionType = it.second;
solAssert(functionType->hasDeclaration(), "");
CompilerContext::LocationSetter locationSetter(m_context, functionType->getDeclaration());
m_context << callDataUnpackerEntryPoints.at(it.first); m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(functionType->getParameterTypes()); appendCalldataUnpacker(functionType->getParameterTypes());
m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration())); m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration()));
m_context << returnTag; m_context << returnTag;
appendReturnValuePacker(functionType->getReturnParameterTypes()); appendReturnValuePacker(functionType->getReturnParameterTypes());
} }
@ -286,7 +288,7 @@ void Compiler::initializeStateVariables(ContractDefinition const& _contract)
bool Compiler::visit(VariableDeclaration const& _variableDeclaration) bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
{ {
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
CompilerContext::LocationSetter locationSetter(m_context, &_variableDeclaration); CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration);
m_context.startFunction(_variableDeclaration); m_context.startFunction(_variableDeclaration);
m_breakTags.clear(); m_breakTags.clear();
@ -300,7 +302,7 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
bool Compiler::visit(FunctionDefinition const& _function) bool Compiler::visit(FunctionDefinition const& _function)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_function); CompilerContext::LocationSetter locationSetter(m_context, _function);
//@todo to simplify this, the calling convention could by changed such that //@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack // although note that this reduces the size of the visible stack
@ -384,7 +386,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
bool Compiler::visit(IfStatement const& _ifStatement) bool Compiler::visit(IfStatement const& _ifStatement)
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, &_ifStatement); CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
compileExpression(_ifStatement.getCondition()); compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump(); eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement()) if (_ifStatement.getFalseStatement())
@ -401,7 +403,7 @@ bool Compiler::visit(IfStatement const& _ifStatement)
bool Compiler::visit(WhileStatement const& _whileStatement) bool Compiler::visit(WhileStatement const& _whileStatement)
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, &_whileStatement); CompilerContext::LocationSetter locationSetter(m_context, _whileStatement);
eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart); m_continueTags.push_back(loopStart);
@ -427,7 +429,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
bool Compiler::visit(ForStatement const& _forStatement) bool Compiler::visit(ForStatement const& _forStatement)
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, &_forStatement); CompilerContext::LocationSetter locationSetter(m_context, _forStatement);
eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart); m_continueTags.push_back(loopStart);
@ -464,7 +466,7 @@ bool Compiler::visit(ForStatement const& _forStatement)
bool Compiler::visit(Continue const& _continueStatement) bool Compiler::visit(Continue const& _continueStatement)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_continueStatement); CompilerContext::LocationSetter locationSetter(m_context, _continueStatement);
if (!m_continueTags.empty()) if (!m_continueTags.empty())
m_context.appendJumpTo(m_continueTags.back()); m_context.appendJumpTo(m_continueTags.back());
return false; return false;
@ -472,7 +474,7 @@ bool Compiler::visit(Continue const& _continueStatement)
bool Compiler::visit(Break const& _breakStatement) bool Compiler::visit(Break const& _breakStatement)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_breakStatement); CompilerContext::LocationSetter locationSetter(m_context, _breakStatement);
if (!m_breakTags.empty()) if (!m_breakTags.empty())
m_context.appendJumpTo(m_breakTags.back()); m_context.appendJumpTo(m_breakTags.back());
return false; return false;
@ -480,7 +482,7 @@ bool Compiler::visit(Break const& _breakStatement)
bool Compiler::visit(Return const& _return) bool Compiler::visit(Return const& _return)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_return); CompilerContext::LocationSetter locationSetter(m_context, _return);
//@todo modifications are needed to make this work with functions returning multiple values //@todo modifications are needed to make this work with functions returning multiple values
if (Expression const* expression = _return.getExpression()) if (Expression const* expression = _return.getExpression())
{ {
@ -499,7 +501,7 @@ bool Compiler::visit(Return const& _return)
bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, &_variableDeclarationStatement); CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
if (Expression const* expression = _variableDeclarationStatement.getExpression()) if (Expression const* expression = _variableDeclarationStatement.getExpression())
{ {
compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType()); compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType());
@ -512,7 +514,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
bool Compiler::visit(ExpressionStatement const& _expressionStatement) bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, &_expressionStatement); CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement);
Expression const& expression = _expressionStatement.getExpression(); Expression const& expression = _expressionStatement.getExpression();
compileExpression(expression); compileExpression(expression);
CompilerUtils(m_context).popStackElement(*expression.getType()); CompilerUtils(m_context).popStackElement(*expression.getType());
@ -523,7 +525,7 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
bool Compiler::visit(PlaceholderStatement const& _placeholderStatement) bool Compiler::visit(PlaceholderStatement const& _placeholderStatement)
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, &_placeholderStatement); CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement);
++m_modifierDepth; ++m_modifierDepth;
appendModifierOrFunctionCode(); appendModifierOrFunctionCode();
--m_modifierDepth; --m_modifierDepth;
@ -550,7 +552,7 @@ void Compiler::appendModifierOrFunctionCode()
} }
ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName()); ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName());
CompilerContext::LocationSetter locationSetter(m_context, &modifier); CompilerContext::LocationSetter locationSetter(m_context, modifier);
solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), ""); solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), "");
for (unsigned i = 0; i < modifier.getParameters().size(); ++i) for (unsigned i = 0; i < modifier.getParameters().size(); ++i)
{ {

36
libsolidity/CompilerContext.cpp

@ -69,7 +69,7 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration) void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{ {
LocationSetter locationSetter(*this, &_declaration); LocationSetter locationSetter(*this, _declaration);
addVariable(_declaration); addVariable(_declaration);
int const size = _declaration.getType()->getSizeOnStack(); int const size = _declaration.getType()->getSizeOnStack();
for (int i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
@ -182,34 +182,7 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
stack<ASTNode const*> newStack; stack<ASTNode const*> newStack;
newStack.push(_node); newStack.push(_node);
std::swap(m_visitedNodes, newStack); std::swap(m_visitedNodes, newStack);
} updateSourceLocation();
CompilerContext& CompilerContext::operator<<(eth::AssemblyItem const& _item)
{
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
m_asm.append(_item, m_visitedNodes.top()->getLocation());
return *this;
}
CompilerContext& CompilerContext::operator<<(eth::Instruction _instruction)
{
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
m_asm.append(_instruction, m_visitedNodes.top()->getLocation());
return *this;
}
CompilerContext& CompilerContext::operator<<(u256 const& _value)
{
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
m_asm.append(_value, m_visitedNodes.top()->getLocation());
return *this;
}
CompilerContext& CompilerContext::operator<<(bytes const& _data)
{
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
m_asm.append(_data, m_visitedNodes.top()->getLocation());
return *this;
} }
vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const
@ -220,5 +193,10 @@ vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContr
return ++it; return ++it;
} }
void CompilerContext::updateSourceLocation()
{
m_asm.setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->getLocation());
}
} }
} }

18
libsolidity/CompilerContext.h

@ -108,15 +108,15 @@ public:
/// Resets the stack of visited nodes with a new stack having only @c _node /// Resets the stack of visited nodes with a new stack having only @c _node
void resetVisitedNodes(ASTNode const* _node); void resetVisitedNodes(ASTNode const* _node);
/// Pops the stack of visited nodes /// Pops the stack of visited nodes
void popVisitedNodes() { m_visitedNodes.pop(); } void popVisitedNodes() { m_visitedNodes.pop(); updateSourceLocation(); }
/// Pushes an ASTNode to the stack of visited nodes /// Pushes an ASTNode to the stack of visited nodes
void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); } void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
/// Append elements to the current instruction list and adjust @a m_stackOffset. /// Append elements to the current instruction list and adjust @a m_stackOffset.
CompilerContext& operator<<(eth::AssemblyItem const& _item); CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
CompilerContext& operator<<(eth::Instruction _instruction); CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; }
CompilerContext& operator<<(u256 const& _value); CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data); CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
eth::Assembly const& getAssembly() const { return m_asm; } eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
@ -130,12 +130,14 @@ public:
class LocationSetter: public ScopeGuard class LocationSetter: public ScopeGuard
{ {
public: public:
LocationSetter(CompilerContext& _compilerContext, ASTNode const* _node): LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node):
ScopeGuard(std::bind(&CompilerContext::popVisitedNodes, _compilerContext)) { _compilerContext.pushVisitedNodes(_node); } ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) { _compilerContext.pushVisitedNodes(&_node); }
}; };
private: private:
std::vector<ContractDefinition const*>::const_iterator getSuperContract(const ContractDefinition &_contract) const; std::vector<ContractDefinition const*>::const_iterator getSuperContract(const ContractDefinition &_contract) const;
/// Updates source location set in the assembly.
void updateSourceLocation();
eth::Assembly m_asm; eth::Assembly m_asm;
/// Magic global variables like msg, tx or this, distinguished by type. /// Magic global variables like msg, tx or this, distinguished by type.

18
libsolidity/ExpressionCompiler.cpp

@ -48,7 +48,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
if (!_varDecl.getValue()) if (!_varDecl.getValue())
return; return;
solAssert(!!_varDecl.getValue()->getType(), "Type information not available."); solAssert(!!_varDecl.getValue()->getType(), "Type information not available.");
CompilerContext::LocationSetter locationSetter(m_context, &_varDecl); CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
_varDecl.getValue()->accept(*this); _varDecl.getValue()->accept(*this);
appendTypeConversion(*_varDecl.getValue()->getType(), *_varDecl.getType(), true); appendTypeConversion(*_varDecl.getValue()->getType(), *_varDecl.getType(), true);
@ -57,7 +57,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_varDecl); CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
FunctionType accessorType(_varDecl); FunctionType accessorType(_varDecl);
unsigned length = 0; unsigned length = 0;
@ -204,7 +204,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
bool ExpressionCompiler::visit(Assignment const& _assignment) bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_assignment); CompilerContext::LocationSetter locationSetter(m_context, _assignment);
_assignment.getRightHandSide().accept(*this); _assignment.getRightHandSide().accept(*this);
if (_assignment.getType()->isValueType()) if (_assignment.getType()->isValueType())
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
@ -237,7 +237,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_unaryOperation); CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation);
//@todo type checking and creating code for an operator should be in the same place: //@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so // the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
@ -307,7 +307,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_binaryOperation); CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation);
Expression const& leftExpression = _binaryOperation.getLeftExpression(); Expression const& leftExpression = _binaryOperation.getLeftExpression();
Expression const& rightExpression = _binaryOperation.getRightExpression(); Expression const& rightExpression = _binaryOperation.getRightExpression();
Type const& commonType = _binaryOperation.getCommonType(); Type const& commonType = _binaryOperation.getCommonType();
@ -354,7 +354,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
bool ExpressionCompiler::visit(FunctionCall const& _functionCall) bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_functionCall); CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
using Location = FunctionType::Location; using Location = FunctionType::Location;
if (_functionCall.isTypeConversion()) if (_functionCall.isTypeConversion())
{ {
@ -572,7 +572,7 @@ bool ExpressionCompiler::visit(NewExpression const&)
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_memberAccess); CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
ASTString const& member = _memberAccess.getMemberName(); ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory()) switch (_memberAccess.getExpression().getType()->getCategory())
{ {
@ -707,7 +707,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
CompilerContext::LocationSetter locationSetter(m_context, &_indexAccess); CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
_indexAccess.getBaseExpression().accept(*this); _indexAccess.getBaseExpression().accept(*this);
Type const& baseType = *_indexAccess.getBaseExpression().getType(); Type const& baseType = *_indexAccess.getBaseExpression().getType();
@ -821,6 +821,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
void ExpressionCompiler::endVisit(Identifier const& _identifier) void ExpressionCompiler::endVisit(Identifier const& _identifier)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _identifier);
Declaration const* declaration = _identifier.getReferencedDeclaration(); Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{ {
@ -853,6 +854,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::endVisit(Literal const& _literal)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _literal);
switch (_literal.getType()->getCategory()) switch (_literal.getType()->getCategory())
{ {
case Type::Category::IntegerConstant: case Type::Category::IntegerConstant:

89
mix/ClientModel.cpp

@ -37,7 +37,6 @@
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
#include "CodeModel.h" #include "CodeModel.h"
#include "ClientModel.h"
#include "QEther.h" #include "QEther.h"
#include "Web3Server.h" #include "Web3Server.h"
#include "ClientModel.h" #include "ClientModel.h"
@ -318,19 +317,28 @@ 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;
QList<QHash<int, int>> codeMaps;
QList<AssemblyItems> codeItems;
QList<CompiledContract const*> contracts;
for (MachineCode const& code: _t.executionCode) for (MachineCode const& code: _t.executionCode)
{ {
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code)); QHash<int, int> codeMap;
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, codeMap));
codeMaps.push_back(std::move(codeMap));
//try to resolve contract for source level debugging //try to resolve contract for source level debugging
auto nameIter = m_contractNames.find(code.address); auto nameIter = m_contractNames.find(code.address);
if (nameIter != m_contractNames.end()) if (nameIter != m_contractNames.end())
{ {
CompiledContract const& compilerRes = m_context->codeModel()->contract(nameIter->second); CompiledContract const& compilerRes = m_context->codeModel()->contract(nameIter->second);
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems(); eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems();
QVariantList locations; codes.back()->setDocument(compilerRes.documentId());
for (eth::AssemblyItem const& item: assemblyItems) codeItems.push_back(std::move(assemblyItems));
locations.push_back(QVariant::fromValue(new QSourceLocation(debugData, item.getLocation().start, item.getLocation().end))); contracts.push_back(&compilerRes);
codes.back()->setLocations(compilerRes.documentId(), std::move(locations)); }
else
{
codeItems.push_back(AssemblyItems());
contracts.push_back(nullptr);
} }
} }
@ -339,18 +347,77 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
data.push_back(QMachineState::getDebugCallData(debugData, d)); data.push_back(QMachineState::getDebugCallData(debugData, d));
QVariantList states; QVariantList states;
QStringList solCallStack;
std::map<int, SolidityDeclaration> solLocals; //<stack pos, declaration>
QList<int> returnStack;
unsigned prevInstructionIndex = 0;
for (MachineState const& s: _t.machineStates) for (MachineState const& s: _t.machineStates)
states.append(QVariant::fromValue(new QMachineState(debugData, s, codes[s.codeIndex], data[s.dataIndex]))); {
int instructionIndex = codeMaps[s.codeIndex][static_cast<unsigned>(s.curPC)];
QSolState* solState = nullptr;
if (!codeItems[s.codeIndex].empty() && contracts[s.codeIndex])
{
CompiledContract const* contract = contracts[s.codeIndex];
AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex];
debugData->setStates(std::move(states)); if (instruction.type() == dev::eth::Push && !instruction.data())
{
//register new local variable initialization
auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end));
if (localIter != contract->locals().end())
solLocals[s.stack.size()] = localIter.value();
}
//QList<QVariableDefinition*> returnParameters; if (instruction.type() == dev::eth::Tag) //TODO: use annotations
//returnParameters = encoder.decode(f->returnParameters(), debuggingContent.returnValue); {
//track calls into functions
auto functionIter = contract->functions().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end));
if (functionIter != contract->functions().end())
{
QString functionName = functionIter.value();
solCallStack.push_back(functionName);
returnStack.push_back(prevInstructionIndex + 1);
}
else if (!returnStack.empty() && instructionIndex == returnStack.back())
{
returnStack.pop_back();
solCallStack.pop_back();
}
}
//format solidity context values
QStringList locals;
for(auto l: solLocals)
if (l.first < (int)s.stack.size())
locals.push_back(l.second.name + "\t" + formatValue(l.second.type, s.stack[l.first]));
QStringList storage;
for(auto st: s.storage)
{
if (st.first < std::numeric_limits<unsigned>::max())
{
auto storageIter = contract->storage().find(static_cast<unsigned>(st.first));
if (storageIter != contract->storage().end())
storage.push_back(storageIter.value().name + "\t" + formatValue(storageIter.value().type, st.second));
}
}
prevInstructionIndex = instructionIndex;
solState = new QSolState(debugData, storage, solCallStack, locals, instruction.getLocation().start, instruction.getLocation().end);
}
states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState)));
}
//collect states for last transaction debugData->setStates(std::move(states));
debugDataReady(debugData); debugDataReady(debugData);
} }
QString ClientModel::formatValue(SolidityType const&, dev::u256 const& _value)
{
return QString::fromStdString(prettyU256(_value));
}
void ClientModel::emptyRecord() void ClientModel::emptyRecord()
{ {
debugDataReady(new QDebugData()); debugDataReady(new QDebugData());

2
mix/ClientModel.h

@ -41,6 +41,7 @@ class QEther;
class QDebugData; class QDebugData;
class MixClient; class MixClient;
class QVariableDefinition; class QVariableDefinition;
struct SolidityType;
/// Backend transaction config class /// Backend transaction config class
struct TransactionSettings struct TransactionSettings
@ -198,6 +199,7 @@ private:
void onNewTransaction(); void onNewTransaction();
void onStateReset(); void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t); void showDebuggerForTransaction(ExecutionResult const& _t);
QString formatValue(SolidityType const& _type, dev::u256 const& _value);
AppContext* m_context; AppContext* m_context;
std::atomic<bool> m_running; std::atomic<bool> m_running;

111
mix/CodeModel.cpp

@ -25,7 +25,10 @@
#include <QDebug> #include <QDebug>
#include <QApplication> #include <QApplication>
#include <QtQml> #include <QtQml>
#include <libdevcore/Common.h>
#include <libevmcore/SourceLocation.h> #include <libevmcore/SourceLocation.h>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/InterfaceHandler.h> #include <libsolidity/InterfaceHandler.h>
@ -43,6 +46,91 @@ using namespace dev::mix;
const std::set<std::string> c_predefinedContracts = const std::set<std::string> c_predefinedContracts =
{ "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" }; { "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" };
namespace
{
using namespace dev::solidity;
class CollectDeclarationsVisitor: public ASTConstVisitor
{
public:
CollectDeclarationsVisitor(QHash<LocationPair, QString>* _functions, QHash<LocationPair, SolidityDeclaration>* _locals, QHash<unsigned, SolidityDeclaration>* _storage):
m_functions(_functions), m_locals(_locals), m_storage(_storage), m_functionScope(false), m_storageSlot(0) {}
private:
QHash<LocationPair, QString>* m_functions;
QHash<LocationPair, SolidityDeclaration>* m_locals;
QHash<unsigned, SolidityDeclaration>* m_storage;
bool m_functionScope;
uint m_storageSlot;
LocationPair nodeLocation(ASTNode const& _node)
{
return LocationPair(_node.getLocation().start, _node.getLocation().end);
}
SolidityType nodeType(Type const* _type)
{
if (!_type)
return SolidityType { SolidityType::Type::UnsignedInteger, 32 };
switch (_type->getCategory())
{
case Type::Category::Integer:
{
IntegerType const* it = dynamic_cast<IntegerType const*>(_type);
unsigned size = it->getNumBits() / 8;
SolidityType::Type typeCode = it->isAddress() ? SolidityType::Type::Address : it->isHash() ? SolidityType::Type::Hash : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger;
return SolidityType { typeCode, size };
}
case Type::Category::Bool:
return SolidityType { SolidityType::Type::Bool, _type->getSizeOnStack() * 32 };
case Type::Category::String:
{
StaticStringType const* s = dynamic_cast<StaticStringType const*>(_type);
return SolidityType { SolidityType::Type::String, static_cast<unsigned>(s->getNumBytes()) };
}
case Type::Category::Contract:
return SolidityType { SolidityType::Type::Address, _type->getSizeOnStack() * 32 };
case Type::Category::Array:
case Type::Category::Enum:
case Type::Category::Function:
case Type::Category::IntegerConstant:
case Type::Category::Magic:
case Type::Category::Mapping:
case Type::Category::Modifier:
case Type::Category::Real:
case Type::Category::Struct:
case Type::Category::TypeType:
case Type::Category::Void:
default:
return SolidityType { SolidityType::Type::UnsignedInteger, 32 };
}
}
virtual bool visit(FunctionDefinition const& _node)
{
m_functions->insert(nodeLocation(_node), QString::fromStdString(_node.getName()));
m_functionScope = true;
return true;
}
virtual void endVisit(FunctionDefinition const&)
{
m_functionScope = false;
}
virtual bool visit(VariableDeclaration const& _node)
{
SolidityDeclaration decl;
decl.type = nodeType(_node.getType().get());
decl.name = QString::fromStdString(_node.getName());
if (m_functionScope)
m_locals->insert(nodeLocation(_node), decl);
else
m_storage->insert(m_storageSlot++, decl);
return true;
}
};
}
void BackgroundWorker::queueCodeChange(int _jobId) void BackgroundWorker::queueCodeChange(int _jobId)
{ {
m_model->runCompilationJob(_jobId); m_model->runCompilationJob(_jobId);
@ -52,18 +140,23 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
QObject(nullptr), QObject(nullptr),
m_sourceHash(qHash(_source)) m_sourceHash(qHash(_source))
{ {
auto const& contractDefinition = _compiler.getContractDefinition(_contractName.toStdString()); std::string name = _contractName.toStdString();
auto const& contractDefinition = _compiler.getContractDefinition(name);
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_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(_contractName.toStdString()); m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
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())
m_contractInterface = "[]"; m_contractInterface = "[]";
if (contractDefinition.getLocation().sourceName.get()) if (contractDefinition.getLocation().sourceName.get())
m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName); m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName);
CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage);
contractDefinition.accept(visitor);
} }
QString CompiledContract::codeHex() const QString CompiledContract::codeHex() const
@ -121,12 +214,11 @@ void CodeModel::reset(QVariantMap const& _documents)
void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code) void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code)
{ {
{ CompiledContract* contract = contractByDocumentId(_documentId);
Guard l(x_contractMap); if (contract != nullptr && contract->m_sourceHash == qHash(_code))
CompiledContract* contract = m_contractMap.value(_documentId); return;
if (contract != nullptr && contract->m_sourceHash == qHash(_code))
return;
{
Guard pl(x_pendingContracts); Guard pl(x_pendingContracts);
m_pendingContracts[_documentId] = _code; m_pendingContracts[_documentId] = _code;
} }
@ -196,7 +288,8 @@ void CodeModel::runCompilationJob(int _jobId)
if (c_predefinedContracts.count(n) != 0) if (c_predefinedContracts.count(n) != 0)
continue; continue;
QString name = QString::fromStdString(n); QString name = QString::fromStdString(n);
auto sourceIter = m_pendingContracts.find(name); QString sourceName = QString::fromStdString(*cs.getContractDefinition(n).getLocation().sourceName);
auto sourceIter = m_pendingContracts.find(sourceName);
QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString(); QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString();
CompiledContract* contract = new CompiledContract(cs, name, source); CompiledContract* contract = new CompiledContract(cs, name, source);
QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership);

31
mix/CodeModel.h

@ -64,6 +64,29 @@ private:
CodeModel* m_model; CodeModel* m_model;
}; };
using LocationPair = QPair<int, int>;
struct SolidityType
{
enum class Type //TODO: arrays and structs
{
SignedInteger,
UnsignedInteger,
Hash,
Bool,
Address,
String,
};
Type type;
unsigned size; //bytes
};
struct SolidityDeclaration
{
QString name;
SolidityType type;
};
///Compilation result model. Contains all the compiled contract data required by UI ///Compilation result model. Contains all the compiled contract data required by UI
class CompiledContract: public QObject class CompiledContract: public QObject
{ {
@ -93,6 +116,10 @@ public:
/// @returns contract source Id /// @returns contract source Id
QString documentId() const { return m_documentId; } QString documentId() const { return m_documentId; }
QHash<LocationPair, QString> const& functions() const { return m_functions; }
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; }
QHash<unsigned, SolidityDeclaration> const& storage() const { return m_storage; }
private: private:
uint m_sourceHash; uint m_sourceHash;
std::shared_ptr<QContractDefinition> m_contract; std::shared_ptr<QContractDefinition> m_contract;
@ -102,11 +129,13 @@ private:
QString m_documentId; QString m_documentId;
eth::AssemblyItems m_assemblyItems; eth::AssemblyItems m_assemblyItems;
eth::AssemblyItems m_constructorAssemblyItems; eth::AssemblyItems m_constructorAssemblyItems;
QHash<LocationPair, QString> m_functions;
QHash<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclaration> m_storage;
friend class CodeModel; friend class CodeModel;
}; };
using ContractMap = QHash<QString, CompiledContract*>; using ContractMap = QHash<QString, CompiledContract*>;
/// Code compilation model. Compiles contracts in background an provides compiled contract data /// Code compilation model. Compiles contracts in background an provides compiled contract data

7
mix/DebuggingStateWrapper.cpp

@ -69,7 +69,7 @@ namespace
} }
} }
QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code) QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash<int, int>& o_codeMap)
{ {
QVariantList codeStr; QVariantList codeStr;
for (unsigned i = 0; i <= _code.size(); ++i) for (unsigned i = 0; i <= _code.size(); ++i)
@ -80,14 +80,15 @@ QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _addr
QString s = QString::fromStdString(instructionInfo((Instruction)b).name); QString s = QString::fromStdString(instructionInfo((Instruction)b).name);
std::ostringstream out; std::ostringstream out;
out << std::hex << std::setw(4) << std::setfill('0') << i; out << std::hex << std::setw(4) << std::setfill('0') << i;
int line = i; int offset = i;
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{ {
unsigned bc = getPushNumber((Instruction)b); unsigned bc = getPushNumber((Instruction)b);
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc))); s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc)));
i += bc; i += bc;
} }
codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s, line))); o_codeMap[offset] = codeStr.size();
codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s)));
} }
catch (...) catch (...)
{ {

45
mix/DebuggingStateWrapper.h

@ -26,12 +26,12 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <QStringList> #include <QStringList>
#include <QMap> #include <QHash>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include "MachineStates.h"
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
#include "MixClient.h"
#include "QBigInt.h" #include "QBigInt.h"
namespace dev namespace dev
@ -46,32 +46,39 @@ class QInstruction: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString line MEMBER m_line CONSTANT) Q_PROPERTY(QString line MEMBER m_line CONSTANT)
Q_PROPERTY(int processIndex MEMBER m_processIndex CONSTANT)
public: public:
QInstruction(QObject* _owner, QString _line, int _processIndex): QObject(_owner), m_line(_line), m_processIndex(_processIndex) {} QInstruction(QObject* _owner, QString _line): QObject(_owner), m_line(_line) {}
private: private:
QString m_line; QString m_line;
int m_processIndex;
}; };
/**
class QSourceLocation: public QObject * @brief Solidity state
*/
class QSolState: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QStringList storage MEMBER m_storage CONSTANT)
Q_PROPERTY(QStringList callStack MEMBER m_callStack CONSTANT)
Q_PROPERTY(QStringList locals MEMBER m_locals CONSTANT)
Q_PROPERTY(int start MEMBER m_start CONSTANT) Q_PROPERTY(int start MEMBER m_start CONSTANT)
Q_PROPERTY(int end MEMBER m_end CONSTANT) Q_PROPERTY(int end MEMBER m_end CONSTANT)
public: public:
QSourceLocation(QObject* _owner, int _start, int _end): QObject(_owner), m_start(_start), m_end(_end) {} QSolState(QObject* _parent, QStringList const& _storage, QStringList const& _callStack, QStringList const& _locals, int _start, int _end):
QObject(_parent), m_storage(_storage), m_callStack(_callStack), m_locals(_locals), m_start(_start), m_end(_end)
{ }
private: private:
QStringList m_storage;
QStringList m_callStack;
QStringList m_locals;
int m_start; int m_start;
int m_end; int m_end;
}; };
/** /**
* @brief Shared container for lines * @brief Shared container for lines
*/ */
@ -79,19 +86,17 @@ 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 address MEMBER m_address CONSTANT)
Q_PROPERTY(QString documentId MEMBER m_document CONSTANT) Q_PROPERTY(QString documentId MEMBER m_document CONSTANT)
public: public:
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions), m_address(_address) {} 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; } void setDocument(QString const& _documentId) { m_document = _documentId; }
private: private:
QVariantList m_instructions; QVariantList m_instructions;
QString m_address; QString m_address;
QString m_document; QString m_document;
QVariantList m_locations;
}; };
/** /**
@ -133,6 +138,7 @@ class QMachineState: public QObject
Q_OBJECT Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT) Q_PROPERTY(int step READ step CONSTANT)
Q_PROPERTY(int curPC READ curPC CONSTANT) Q_PROPERTY(int curPC READ curPC CONSTANT)
Q_PROPERTY(int instructionIndex MEMBER m_instructionIndex CONSTANT)
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)
@ -146,10 +152,11 @@ class QMachineState: public QObject
Q_PROPERTY(QVariantList levels READ levels CONSTANT) Q_PROPERTY(QVariantList levels READ levels CONSTANT)
Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT) Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT)
Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT) Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT)
Q_PROPERTY(QObject* solidity MEMBER m_solState CONSTANT)
public: public:
QMachineState(QObject* _owner, MachineState const& _state, QCode* _code, QCallData* _callData): QMachineState(QObject* _owner, int _instructionIndex, MachineState const& _state, QCode* _code, QCallData* _callData, QSolState* _solState):
QObject(_owner), m_state(_state), m_code(_code), m_callData(_callData) {} QObject(_owner), m_instructionIndex(_instructionIndex), m_state(_state), m_code(_code), m_callData(_callData), m_solState(_solState) { }
/// Get the step of this machine states. /// Get the step of this machine states.
int step() { return (int)m_state.steps; } int step() { return (int)m_state.steps; }
/// Get the proccessed code index. /// Get the proccessed code index.
@ -168,7 +175,7 @@ public:
QStringList debugStorage(); QStringList debugStorage();
/// Get memory. /// Get memory.
QVariantList debugMemory(); QVariantList debugMemory();
/// get end of debug information. /// Get end of debug information.
QString endOfDebug(); QString endOfDebug();
/// Get the new memory size. /// Get the new memory size.
QBigInt* newMemSize(); QBigInt* newMemSize();
@ -177,18 +184,18 @@ public:
/// Get all previous steps. /// Get all previous steps.
QVariantList levels(); QVariantList levels();
/// Get the current processed machine state. /// Get the current processed machine state.
MachineState state() { return m_state; } MachineState const& state() const { return m_state; }
/// Set the current processed machine 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, const Address& _address, const bytes& _code); static QCode* getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash<int, int>& o_codeMap);
/// 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);
private: private:
int m_instructionIndex;
MachineState m_state; MachineState m_state;
QCode* m_code; QCode* m_code;
QCallData* m_callData; QCallData* m_callData;
QSolState* m_solState;
}; };
} }

2
mix/MixClient.cpp

@ -41,7 +41,7 @@ namespace mix
{ {
const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
const u256 c_mixGenesisDifficulty = (u256) 1 << 4; const u256 c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow
class MixBlockChain: public dev::eth::BlockChain class MixBlockChain: public dev::eth::BlockChain
{ {

74
mix/qml/CallStack.qml

@ -0,0 +1,74 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import "."
DebugInfoList
{
id: callStack
collapsible: true
title : qsTr("Call Stack")
enableSelection: true
itemDelegate:
Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: "#4A90E2"
visible: styleData.selected;
}
RowLayout
{
id: row
anchors.fill: parent
Rectangle
{
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: 30
Layout.maximumWidth: 30
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
font.family: "monospace"
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.row;
font.pointSize: DebuggerPaneStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width - 30
Layout.maximumWidth: parent.width - 30
Text {
anchors.leftMargin: 5
width: parent.width - 5
wrapMode: Text.NoWrap
anchors.left: parent.left
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value;
elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize
}
}
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
}

193
mix/qml/Debugger.qml

@ -29,6 +29,7 @@ Rectangle {
onAssemblyModeChanged: onAssemblyModeChanged:
{ {
Debugger.updateMode(); Debugger.updateMode();
machineStates.updateHeight();
} }
function displayCompilationErrorIfAny() function displayCompilationErrorIfAny()
@ -90,6 +91,9 @@ Rectangle {
property alias memoryDumpHeightSettings: memoryRect.height property alias memoryDumpHeightSettings: memoryRect.height
property alias callDataHeightSettings: callDataRect.height property alias callDataHeightSettings: callDataRect.height
property alias transactionLogVisible: transactionLog.visible property alias transactionLogVisible: transactionLog.visible
property alias solCallStackHeightSettings: solStackRect.height
property alias solStorageHeightSettings: solStorageRect.height
property alias solLocalsHeightSettings: solLocalsRect.height
} }
Rectangle Rectangle
@ -183,8 +187,12 @@ Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
function updateHeight() { function updateHeight() {
statesLayout.height = buttonRow.childrenRect.height + assemblyCodeRow.childrenRect.height + var h = buttonRow.childrenRect.height;
callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height + 120; if (assemblyMode)
h += assemblyCodeRow.childrenRect.height + callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height;
else
h += solStackRect.childrenRect.height + solLocalsRect.childrenRect.height + solStorageRect.childrenRect.height;
statesLayout.height = h + 120;
} }
Component.onCompleted: updateHeight(); Component.onCompleted: updateHeight();
@ -546,83 +554,66 @@ Rectangle {
Rectangle Rectangle
{ {
id: callStackRect; id: solStackRect;
color: "transparent" color: "transparent"
Layout.minimumHeight: 25 Layout.minimumHeight: 25
Layout.maximumHeight: 800 Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight(); onHeightChanged: machineStates.updateHeight();
DebugInfoList visible: !assemblyMode
{ CallStack {
id: callStack
collapsible: true
anchors.fill: parent anchors.fill: parent
title : qsTr("Call Stack") id: solCallStack
enableSelection: true }
onRowActivated: Debugger.displayFrame(index); }
itemDelegate:
Item {
anchors.fill: parent
Rectangle { Rectangle
anchors.fill: parent {
color: "#4A90E2" id: solLocalsRect;
visible: styleData.selected; color: "transparent"
} Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
visible: !assemblyMode
StorageView {
title : qsTr("Locals")
anchors.fill: parent
id: solLocals
}
}
RowLayout Rectangle
{ {
id: row id: solStorageRect;
anchors.fill: parent color: "transparent"
Rectangle Layout.minimumHeight: 25
{ Layout.maximumHeight: 800
color: "#f7f7f7" onHeightChanged: machineStates.updateHeight();
Layout.fillWidth: true visible: !assemblyMode
Layout.minimumWidth: 30 StorageView {
Layout.maximumWidth: 30 title : qsTr("Members")
Text { anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter id: solStorage
anchors.left: parent.left }
font.family: "monospace" }
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.row;
font.pointSize: DebuggerPaneStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width - 30
Layout.maximumWidth: parent.width - 30
Text {
anchors.leftMargin: 5
width: parent.width - 5
wrapMode: Text.NoWrap
anchors.left: parent.left
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value;
elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize
}
}
}
Rectangle { Rectangle
anchors.top: row.bottom {
width: parent.width; id: callStackRect;
height: 1; color: "transparent"
color: "#cccccc" Layout.minimumHeight: 25
anchors.bottom: parent.bottom Layout.maximumHeight: 800
} onHeightChanged: machineStates.updateHeight();
} visible: assemblyMode
CallStack {
anchors.fill: parent
id: callStack
onRowActivated: Debugger.displayFrame(index);
} }
} }
Rectangle Rectangle
{ {
id: storageRect id: storageRect
@ -631,68 +622,10 @@ Rectangle {
Layout.minimumHeight: 25 Layout.minimumHeight: 25
Layout.maximumHeight: 800 Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight(); onHeightChanged: machineStates.updateHeight();
DebugInfoList visible: assemblyMode
{ StorageView {
id: storage
anchors.fill: parent anchors.fill: parent
collapsible: true id: storage
title : qsTr("Storage")
itemDelegate:
Item {
anchors.fill: parent
RowLayout
{
id: row
anchors.fill: parent
Rectangle
{
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
font.family: "monospace"
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.value.split('\t')[0];
font.pointSize: DebuggerPaneStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Text {
maximumLineCount: 1
clip: true
anchors.leftMargin: 5
width: parent.width - 5
wrapMode: Text.WrapAnywhere
anchors.left: parent.left
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value.split('\t')[1];
elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize
}
}
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
} }
} }
@ -704,6 +637,7 @@ Rectangle {
Layout.minimumHeight: 25 Layout.minimumHeight: 25
Layout.maximumHeight: 800 Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight(); onHeightChanged: machineStates.updateHeight();
visible: assemblyMode
DebugInfoList { DebugInfoList {
id: memoryDump id: memoryDump
anchors.fill: parent anchors.fill: parent
@ -726,6 +660,7 @@ Rectangle {
Layout.minimumHeight: 25 Layout.minimumHeight: 25
Layout.maximumHeight: 800 Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight(); onHeightChanged: machineStates.updateHeight();
visible: assemblyMode
DebugInfoList { DebugInfoList {
id: callDataDump id: callDataDump
anchors.fill: parent anchors.fill: parent

4
mix/qml/StatusPane.qml

@ -159,8 +159,8 @@ Rectangle {
color: "transparent" color: "transparent"
width: 100 width: 100
height: parent.height height: parent.height
anchors.top: statusHeader.top anchors.top: parent.top
anchors.right: statusHeader.right anchors.right: parent.right
RowLayout RowLayout
{ {
anchors.fill: parent anchors.fill: parent

69
mix/qml/StorageView.qml

@ -0,0 +1,69 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import "."
DebugInfoList
{
id: storage
collapsible: true
title : qsTr("Storage")
itemDelegate:
Item {
anchors.fill: parent
RowLayout
{
id: row
anchors.fill: parent
Rectangle
{
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
font.family: "monospace"
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.value.split('\t')[0];
font.pointSize: DebuggerPaneStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
}
Rectangle
{
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Text {
maximumLineCount: 1
clip: true
anchors.leftMargin: 5
width: parent.width - 5
wrapMode: Text.WrapAnywhere
anchors.left: parent.left
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value.split('\t')[1];
elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize
}
}
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
}

31
mix/qml/js/Debugger.js

@ -4,7 +4,6 @@
var currentSelectedState = null; var currentSelectedState = null;
var currentDisplayedState = null; var currentDisplayedState = null;
var debugData = null; var debugData = null;
var codeMap = null;
var locations = []; var locations = [];
var locationMap = {}; var locationMap = {};
var breakpoints = {}; var breakpoints = {};
@ -56,7 +55,7 @@ function initLocations()
for (var i = 0; i < debugData.states.length - 1; i++) { for (var i = 0; i < debugData.states.length - 1; i++) {
var code = debugData.states[i].code; var code = debugData.states[i].code;
var location = code.documentId ? code.locations[codeStr(i)] : nullLocation; var location = code.documentId ? debugData.states[i].solidity : nullLocation;
if (location.start !== prevLocation.start || location.end !== prevLocation.end || code.documentId !== prevLocation.documentId) 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 }; prevLocation = { start: location.start, end: location.end, documentId: code.documentId, state: i };
@ -65,6 +64,7 @@ function initLocations()
locationMap[i] = locations.length - 1; locationMap[i] = locations.length - 1;
} }
locations.push({ start: -1, end: -1, documentId: code.documentId, state: i }); locations.push({ start: -1, end: -1, documentId: code.documentId, state: i });
locationMap[debugData.states.length - 1] = locations.length - 1; locationMap[debugData.states.length - 1] = locations.length - 1;
} }
@ -93,12 +93,10 @@ function initSlider()
function setupInstructions(stateIndex) function setupInstructions(stateIndex)
{ {
var instructions = debugData.states[stateIndex].code.instructions; var instructions = debugData.states[stateIndex].code.instructions;
codeMap = {};
statesList.model.clear(); statesList.model.clear();
for (var i = 0; i < instructions.length; i++) { for (var i = 0; i < instructions.length; i++)
statesList.model.append(instructions[i]); statesList.model.append(instructions[i]);
codeMap[instructions[i].processIndex] = i;
}
callDataDump.listModel = debugData.states[stateIndex].callData.items; callDataDump.listModel = debugData.states[stateIndex].callData.items;
} }
@ -129,14 +127,14 @@ function display(stateIndex)
setupInstructions(stateIndex); setupInstructions(stateIndex);
if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex) if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex)
setupCallData(stateIndex); setupCallData(stateIndex);
var codeLine = codeStr(stateIndex);
var state = debugData.states[stateIndex]; var state = debugData.states[stateIndex];
var codeLine = state.instructionIndex;
highlightSelection(codeLine); highlightSelection(codeLine);
completeCtxInformation(state); completeCtxInformation(state);
currentDisplayedState = stateIndex; currentDisplayedState = stateIndex;
var docId = debugData.states[stateIndex].code.documentId; var docId = debugData.states[stateIndex].code.documentId;
if (docId) if (docId)
debugExecuteLocation(docId, locations[locationMap[stateIndex]]); debugExecuteLocation(docId, debugData.states[stateIndex].solidity);
} }
function displayFrame(frameIndex) function displayFrame(frameIndex)
@ -183,12 +181,6 @@ function selectState(stateIndex)
statesSlider.value = stateIndex; statesSlider.value = stateIndex;
} }
function codeStr(stateIndex)
{
var state = debugData.states[stateIndex];
return codeMap[state.curPC];
}
function highlightSelection(index) function highlightSelection(index)
{ {
statesList.positionViewAtRow(index, ListView.Center); statesList.positionViewAtRow(index, ListView.Center);
@ -206,6 +198,15 @@ function completeCtxInformation(state)
stack.listModel = state.debugStack; stack.listModel = state.debugStack;
storage.listModel = state.debugStorage; storage.listModel = state.debugStorage;
memoryDump.listModel = state.debugMemory; memoryDump.listModel = state.debugMemory;
if (state.solidity) {
solLocals.listModel = state.solidity.locals;
solStorage.listModel = state.solidity.storage;
solCallStack.listModel = state.solidity.callStack;
} else {
solLocals.listModel = [];
solStorage.listModel = [];
solCallStack.listModel = [];
}
} }
function isCallInstruction(index) function isCallInstruction(index)
@ -229,7 +230,7 @@ function breakpointHit(i)
{ {
var bpLocations = breakpoints[debugData.states[i].code.documentId]; var bpLocations = breakpoints[debugData.states[i].code.documentId];
if (bpLocations) { if (bpLocations) {
var location = locations[locationMap[i]]; var location = debugData.states[i].solidity;
if (location.start >= 0 && location.end >= location.start) if (location.start >= 0 && location.end >= location.start)
for (var b = 0; b < bpLocations.length; b++) for (var b = 0; b < bpLocations.length; b++)
if (locationsIntersect(location, bpLocations[b])) if (locationsIntersect(location, bpLocations[b]))

2
mix/res.qrc

@ -103,6 +103,8 @@
<file>qml/img/available_updates.png</file> <file>qml/img/available_updates.png</file>
<file>qml/DeploymentDialog.qml</file> <file>qml/DeploymentDialog.qml</file>
<file>qml/img/search_filled.png</file> <file>qml/img/search_filled.png</file>
<file>qml/StorageView.qml</file>
<file>qml/CallStack.qml</file>
<file>qml/img/help.png</file> <file>qml/img/help.png</file>
<file>qml/img/openedfolder.png</file> <file>qml/img/openedfolder.png</file>
<file>qml/img/b64.png</file> <file>qml/img/b64.png</file>

40
test/TestHelper.h

@ -46,39 +46,55 @@ namespace test
/// Make sure that no Exception is thrown during testing. If one is thrown show its info and fail the test. /// Make sure that no Exception is thrown during testing. If one is thrown show its info and fail the test.
/// Our version of BOOST_REQUIRE_NO_THROW() /// Our version of BOOST_REQUIRE_NO_THROW()
/// @param _expression The expression for which to make sure no exceptions are thrown /// @param _statenent The statement for which to make sure no exceptions are thrown
/// @param _message A message to act as a prefix to the expression's error information /// @param _message A message to act as a prefix to the expression's error information
#define ETH_TEST_REQUIRE_NO_THROW(_expression, _message) \ #define ETH_TEST_REQUIRE_NO_THROW(_statement, _message) \
do \ do \
{ \ { \
try \ try \
{ \ { \
_expression; \ BOOST_TEST_PASSPOINT(); \
_statement; \
} \ } \
catch (boost::exception const& _e) \ catch (boost::exception const& _e) \
{ \ { \
auto msg = std::string(_message"\n") + boost::diagnostic_information(_e); \ auto msg = std::string(_message " due to an exception thrown by " \
BOOST_FAIL(msg); \ BOOST_STRINGIZE(_statement) "\n") + boost::diagnostic_information(_e); \
BOOST_CHECK_IMPL(false, msg, REQUIRE, CHECK_MSG); \
} \ } \
} while (0) catch (...) \
{ \
BOOST_CHECK_IMPL(false, "Unknown exception thrown by " \
BOOST_STRINGIZE(_statement), REQUIRE, CHECK_MSG); \
} \
} \
while (0)
/// Check if an Exception is thrown during testing. If one is thrown show its info and continue the test /// Check if an Exception is thrown during testing. If one is thrown show its info and continue the test
/// Our version of BOOST_CHECK_NO_THROW() /// Our version of BOOST_CHECK_NO_THROW()
/// @param _expression The expression for which to make sure no exceptions are thrown /// @param _statement The statement for which to make sure no exceptions are thrown
/// @param _message A message to act as a prefix to the expression's error information /// @param _message A message to act as a prefix to the expression's error information
#define ETH_TEST_CHECK_NO_THROW(_expression, _message) \ #define ETH_TEST_CHECK_NO_THROW(_statement, _message) \
do \ do \
{ \ { \
try \ try \
{ \ { \
_expression; \ BOOST_TEST_PASSPOINT(); \
_statement; \
} \ } \
catch (boost::exception const& _e) \ catch (boost::exception const& _e) \
{ \ { \
auto msg = std::string(_message"\n") + boost::diagnostic_information(_e); \ auto msg = std::string(_message " due to an exception thrown by " \
BOOST_MESSAGE(msg); \ BOOST_STRINGIZE(_statement) "\n") + boost::diagnostic_information(_e); \
BOOST_CHECK_IMPL(false, msg, CHECK, CHECK_MSG); \
} \
catch (...) \
{ \
BOOST_CHECK_IMPL(false, "Unknown exception thrown by " \
BOOST_STRINGIZE(_statement), CHECK, CHECK_MSG ); \
} \ } \
} while (0) } \
while (0)
class ImportTest class ImportTest

Loading…
Cancel
Save