Browse Source

Merge pull request #1797 from arkpar/mix_state

Mix: Bugfixes
cl-refactor
Arkadiy Paronyan 10 years ago
parent
commit
ddd8b192dc
  1. 14
      mix/ClientModel.cpp
  2. 94
      mix/CodeModel.cpp
  3. 20
      mix/CodeModel.h
  4. 2
      mix/qml/StateDialog.qml
  5. 5
      mix/qml/html/codeeditor.js
  6. 3
      mix/qml/js/Debugger.js
  7. 2
      mix/test/qml/js/TestTutorial.js

14
mix/ClientModel.cpp

@ -422,7 +422,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
//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);
CompiledContract const* compilerRes = nullptr; CompiledContract const* compilerRes = nullptr;
if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second))) if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second))) //returned object is guaranteed to live till the end of event handler in main thread
{ {
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems(); eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems();
codes.back()->setDocument(compilerRes->documentId()); codes.back()->setDocument(compilerRes->documentId());
@ -467,9 +467,9 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{ {
//track calls into functions //track calls into functions
AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex]; AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex];
auto functionIter = contract->functions().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end)); QString functionName = m_codeModel->resolveFunctionName(instruction.getLocation());
if (functionIter != contract->functions().end() && ((prevInstruction.getJumpType() == AssemblyItem::JumpType::IntoFunction) || solCallStack.empty())) if (!functionName.isEmpty() && ((prevInstruction.getJumpType() == AssemblyItem::JumpType::IntoFunction) || solCallStack.empty()))
solCallStack.push_front(QVariant::fromValue(functionIter.value())); solCallStack.push_front(QVariant::fromValue(functionName));
else if (prevInstruction.getJumpType() == AssemblyItem::JumpType::OutOfFunction && !solCallStack.empty()) else if (prevInstruction.getJumpType() == AssemblyItem::JumpType::OutOfFunction && !solCallStack.empty())
solCallStack.pop_front(); solCallStack.pop_front();
} }
@ -521,11 +521,13 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
prevInstructionIndex = instructionIndex; prevInstructionIndex = instructionIndex;
// filter out locations that match whole function or contract
SourceLocation location = instruction.getLocation(); SourceLocation location = instruction.getLocation();
if (contract->contract()->location() == location || contract->functions().contains(LocationPair(location.start, location.end))) QString source = QString::fromUtf8(location.sourceName->c_str());
if (m_codeModel->isContractOrFunctionLocation(location))
location = dev::SourceLocation(-1, -1, location.sourceName); location = dev::SourceLocation(-1, -1, location.sourceName);
solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), location.start, location.end, QString::fromUtf8(location.sourceName->c_str())); solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), location.start, location.end, source);
} }
states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState))); states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState)));

94
mix/CodeModel.cpp

@ -51,30 +51,31 @@ const std::set<std::string> c_predefinedContracts =
namespace namespace
{ {
using namespace dev::solidity; using namespace dev::solidity;
class CollectDeclarationsVisitor: public ASTConstVisitor
class CollectLocalsVisitor: public ASTConstVisitor
{ {
public: public:
CollectDeclarationsVisitor(QHash<LocationPair, QString>* _functions, QHash<LocationPair, SolidityDeclaration>* _locals): CollectLocalsVisitor(QHash<LocationPair, SolidityDeclaration>* _locals):
m_functions(_functions), m_locals(_locals), m_functionScope(false) {} m_locals(_locals), m_functionScope(false) {}
private: private:
LocationPair nodeLocation(ASTNode const& _node) LocationPair nodeLocation(ASTNode const& _node)
{ {
return LocationPair(_node.getLocation().start, _node.getLocation().end); return LocationPair(_node.getLocation().start, _node.getLocation().end);
} }
virtual bool visit(FunctionDefinition const& _node) virtual bool visit(FunctionDefinition const&)
{ {
m_functions->insert(nodeLocation(_node), QString::fromStdString(_node.getName()));
m_functionScope = true; m_functionScope = true;
return true; return true;
} }
virtual void endVisit(FunctionDefinition const&) virtual void endVisit(FunctionDefinition const&) override
{ {
m_functionScope = false; m_functionScope = false;
} }
virtual bool visit(VariableDeclaration const& _node) virtual bool visit(VariableDeclaration const& _node) override
{ {
SolidityDeclaration decl; SolidityDeclaration decl;
decl.type = CodeModel::nodeType(_node.getType().get()); decl.type = CodeModel::nodeType(_node.getType().get());
@ -87,11 +88,38 @@ private:
} }
private: private:
QHash<LocationPair, QString>* m_functions;
QHash<LocationPair, SolidityDeclaration>* m_locals; QHash<LocationPair, SolidityDeclaration>* m_locals;
bool m_functionScope; bool m_functionScope;
}; };
class CollectLocationsVisitor: public ASTConstVisitor
{
public:
CollectLocationsVisitor(SourceMap* _sourceMap):
m_sourceMap(_sourceMap) {}
private:
LocationPair nodeLocation(ASTNode const& _node)
{
return LocationPair(_node.getLocation().start, _node.getLocation().end);
}
virtual bool visit(FunctionDefinition const& _node) override
{
m_sourceMap->functions.insert(nodeLocation(_node), QString::fromStdString(_node.getName()));
return true;
}
virtual bool visit(ContractDefinition const& _node) override
{
m_sourceMap->contracts.insert(nodeLocation(_node), QString::fromStdString(_node.getName()));
return true;
}
private:
SourceMap* m_sourceMap;
};
QHash<unsigned, SolidityDeclarations> collectStorage(dev::solidity::ContractDefinition const& _contract) QHash<unsigned, SolidityDeclarations> collectStorage(dev::solidity::ContractDefinition const& _contract)
{ {
QHash<unsigned, SolidityDeclarations> result; QHash<unsigned, SolidityDeclarations> result;
@ -132,7 +160,7 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
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); CollectLocalsVisitor visitor(&m_locals);
m_storage = collectStorage(contractDefinition); m_storage = collectStorage(contractDefinition);
contractDefinition.accept(visitor); contractDefinition.accept(visitor);
m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name); m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name);
@ -243,6 +271,7 @@ void CodeModel::releaseContracts()
for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); ++c) for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); ++c)
c.value()->deleteLater(); c.value()->deleteLater();
m_contractMap.clear(); m_contractMap.clear();
m_sourceMaps.clear();
} }
void CodeModel::runCompilationJob(int _jobId) void CodeModel::runCompilationJob(int _jobId)
@ -253,13 +282,17 @@ void CodeModel::runCompilationJob(int _jobId)
try try
{ {
cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})"); cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})");
std::vector<std::string> sourceNames;
{ {
Guard l(x_pendingContracts); Guard l(x_pendingContracts);
for (auto const& c: m_pendingContracts) for (auto const& c: m_pendingContracts)
{
cs.addSource(c.first.toStdString(), c.second.toStdString()); cs.addSource(c.first.toStdString(), c.second.toStdString());
sourceNames.push_back(c.first.toStdString());
}
} }
cs.compile(false); cs.compile(false);
collectContracts(cs); collectContracts(cs, sourceNames);
} }
catch (dev::Exception const& _exception) catch (dev::Exception const& _exception)
{ {
@ -281,11 +314,20 @@ void CodeModel::runCompilationJob(int _jobId)
emit stateChanged(); emit stateChanged();
} }
void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs) void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames)
{ {
Guard pl(x_pendingContracts); Guard pl(x_pendingContracts);
Guard l(x_contractMap); Guard l(x_contractMap);
ContractMap result; ContractMap result;
SourceMaps sourceMaps;
for (std::string const& sourceName: _sourceNames)
{
dev::solidity::SourceUnit const& source = _cs.getAST(sourceName);
SourceMap sourceMap;
CollectLocationsVisitor collector(&sourceMap);
source.accept(collector);
sourceMaps.insert(QString::fromStdString(sourceName), std::move(sourceMap));
}
for (std::string n: _cs.getContractNames()) for (std::string n: _cs.getContractNames())
{ {
if (c_predefinedContracts.count(n) != 0) if (c_predefinedContracts.count(n) != 0)
@ -326,6 +368,7 @@ void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs)
} }
releaseContracts(); releaseContracts();
m_contractMap.swap(result); m_contractMap.swap(result);
m_sourceMaps.swap(sourceMaps);
emit codeChanged(); emit codeChanged();
emit compilationComplete(); emit compilationComplete();
} }
@ -431,3 +474,32 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
return r; return r;
} }
bool CodeModel::isContractOrFunctionLocation(dev::SourceLocation const& _location)
{
if (!_location.sourceName)
return false;
Guard l(x_contractMap);
auto sourceMapIter = m_sourceMaps.find(QString::fromStdString(*_location.sourceName));
if (sourceMapIter != m_sourceMaps.cend())
{
LocationPair location(_location.start, _location.end);
return sourceMapIter.value().contracts.contains(location) || sourceMapIter.value().functions.contains(location);
}
return false;
}
QString CodeModel::resolveFunctionName(dev::SourceLocation const& _location)
{
if (!_location.sourceName)
return QString();
Guard l(x_contractMap);
auto sourceMapIter = m_sourceMaps.find(QString::fromStdString(*_location.sourceName));
if (sourceMapIter != m_sourceMaps.cend())
{
LocationPair location(_location.start, _location.end);
auto functionNameIter = sourceMapIter.value().functions.find(location);
if (functionNameIter != sourceMapIter.value().functions.cend())
return functionNameIter.value();
}
return QString();
}

20
mix/CodeModel.h

@ -97,7 +97,6 @@ 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<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; }
QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; } QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; }
@ -110,7 +109,6 @@ 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<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclarations> m_storage; QHash<unsigned, SolidityDeclarations> m_storage;
@ -119,6 +117,17 @@ private:
using ContractMap = QMap<QString, CompiledContract*>; //needs to be sorted using ContractMap = QMap<QString, CompiledContract*>; //needs to be sorted
/// Source map
using LocationMap = QHash<LocationPair, QString>;
struct SourceMap
{
LocationMap contracts;
LocationMap functions;
};
using SourceMaps = QMap<QString, SourceMap>; //by source id
/// Code compilation model. Compiles contracts in background an provides compiled contract data /// Code compilation model. Compiles contracts in background an provides compiled contract data
class CodeModel: public QObject class CodeModel: public QObject
{ {
@ -153,6 +162,10 @@ public:
Q_INVOKABLE void reset() { reset(QVariantMap()); } Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Convert solidity type info to mix type /// Convert solidity type info to mix type
static SolidityType nodeType(dev::solidity::Type const* _type); static SolidityType nodeType(dev::solidity::Type const* _type);
/// Check if given location belongs to contract or function
bool isContractOrFunctionLocation(dev::SourceLocation const& _location);
/// Get funciton name by location
QString resolveFunctionName(dev::SourceLocation const& _location);
signals: signals:
/// Emited on compilation state change /// Emited on compilation state change
@ -182,11 +195,12 @@ private:
void runCompilationJob(int _jobId); void runCompilationJob(int _jobId);
void stop(); void stop();
void releaseContracts(); void releaseContracts();
void collectContracts(solidity::CompilerStack const& _cs); void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames);
std::atomic<bool> m_compiling; std::atomic<bool> m_compiling;
mutable dev::Mutex x_contractMap; mutable dev::Mutex x_contractMap;
ContractMap m_contractMap; ContractMap m_contractMap;
SourceMaps m_sourceMaps;
std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings; std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings;
QThread m_backgroundThread; QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker; BackgroundWorker m_backgroundWorker;

2
mix/qml/StateDialog.qml

@ -158,7 +158,7 @@ Dialog {
id: importJsonFileDialog id: importJsonFileDialog
visible: false visible: false
title: qsTr("Select State File") title: qsTr("Select State File")
nameFilters: [qsTr("JSON files (*.json)", "All files (*)")] nameFilters: Qt.platform.os === "osx" ? [] : [qsTr("JSON files (*.json)", "All files (*)")] //qt 5.4 segfaults with filter string on OSX
onAccepted: { onAccepted: {
var path = importJsonFileDialog.fileUrl.toString() var path = importJsonFileDialog.fileUrl.toString()
var jsonData = fileIo.readFile(path) var jsonData = fileIo.readFile(path)

5
mix/qml/html/codeeditor.js

@ -126,7 +126,10 @@ highlightExecution = function(start, end) {
executionMark.clear(); executionMark.clear();
if (debugWarning) if (debugWarning)
debugWarning.clear(); debugWarning.clear();
executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" }); if (start > 0 && end > start) {
executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" });
editor.scrollIntoView(editor.posFromIndex(start));
}
} }
var changeId; var changeId;

3
mix/qml/js/Debugger.js

@ -183,7 +183,8 @@ function selectState(stateIndex)
function highlightSelection(index) function highlightSelection(index)
{ {
statesList.positionViewAtRow(index, ListView.Center); if (statesList.visible)
statesList.positionViewAtRow(index, ListView.Visible);
statesList.selection.clear(); statesList.selection.clear();
statesList.selection.select(index); statesList.selection.select(index);
} }

2
mix/test/qml/js/TestTutorial.js

@ -52,7 +52,7 @@ function test_tutorial()
transactionDialog.selectFunction("setRating"); transactionDialog.selectFunction("setRating");
clickElement(transactionDialog, 200, 310); clickElement(transactionDialog, 200, 310);
ts.typeString("Titanic", transactionDialog); ts.typeString("Titanic", transactionDialog);
clickElement(transactionDialog, 200, 350); clickElement(transactionDialog, 200, 330);
ts.typeString("2", transactionDialog); ts.typeString("2", transactionDialog);
transactionDialog.acceptAndClose(); transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose(); mainApplication.projectModel.stateDialog.acceptAndClose();

Loading…
Cancel
Save