You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

288 lines
9.2 KiB

/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CodeModel.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <memory>
#include <atomic>
#include <map>
#include <QObject>
#include <QThread>
#include <QHash>
#include <QMetaEnum>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libevmasm/Assembly.h>
#include "SolidityType.h"
#include "QBigInt.h"
class QTextDocument;
namespace dev
{
namespace solidity
{
class CompilerStack;
class Type;
}
namespace mix
{
class CodeModel;
class CodeHighlighter;
class CodeHighlighterSettings;
class QContractDefinition;
//utility class to perform tasks in background thread
class BackgroundWorker: public QObject
{
Q_OBJECT
public:
BackgroundWorker(CodeModel* _model): QObject(), m_model(_model) {}
public slots:
void queueCodeChange(int _jobId);
private:
CodeModel* m_model;
};
using LocationPair = QPair<int, int>;
///Compilation result model. Contains all the compiled contract data required by UI
class CompiledContract: public QObject
{
Q_OBJECT
Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT)
Q_PROPERTY(QString codeHex READ codeHex CONSTANT)
Q_PROPERTY(QString documentId READ documentId CONSTANT)
public:
/// Successful compilation result constructor
CompiledContract(solidity::CompilerStack const& _compiler, QString const& _contractName, QString const& _source);
/// @returns contract definition for QML property
QContractDefinition* contract() const { return m_contract.get(); }
/// @returns contract definition
std::shared_ptr<QContractDefinition> sharedContract() const { return m_contract; }
/// @returns contract bytecode
dev::bytes const& bytes() const { return m_bytes; }
/// @returns contract bytecode as hex string
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; }
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; }
QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; }
private:
uint m_sourceHash;
std::shared_ptr<QContractDefinition> m_contract;
QString m_compilerMessage; ///< @todo: use some structure here
dev::bytes m_bytes;
QString m_contractInterface;
QString m_documentId;
eth::AssemblyItems m_assemblyItems;
eth::AssemblyItems m_constructorAssemblyItems;
QHash<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclarations> m_storage;
friend class CodeModel;
};
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
using GasCostsMaps = QMap<QString, QVariantList>; //gas cost by contract name
class GasMap: public QObject
{
Q_OBJECT
Q_ENUMS(type)
Q_PROPERTY(int start MEMBER m_start CONSTANT)
Q_PROPERTY(int end MEMBER m_end CONSTANT)
Q_PROPERTY(QString gas MEMBER m_gas CONSTANT)
Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT)
Q_PROPERTY(QString codeBlockType READ codeBlockType CONSTANT)
public:
enum type
{
Statement,
Function,
Constructor
};
GasMap(int _start, int _end, QString _gas, bool _isInfinite, type _type, QObject* _parent): QObject(_parent), m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite), m_type(_type) {}
int m_start;
int m_end;
QString m_gas;
bool m_isInfinite;
type m_type;
QString codeBlockType() const
{
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("type"));
if (m_type)
{
const char* key = units.valueToKey(m_type);
return QString(key).toLower();
}
return QString("");
}
};
class GasMapWrapper: public QObject
{
Q_OBJECT
Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT)
public:
GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){}
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type);
bool contains(QString _key);
void insert(QString _source, QVariantList _variantList);
QVariantList gasCostsByDocId(QString _source);
private:
GasCostsMaps m_gasMaps;
};
/// Code compilation model. Compiles contracts in background an provides compiled contract data
class CodeModel: public QObject
{
Q_OBJECT
public:
CodeModel();
~CodeModel();
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged)
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged)
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged)
Q_PROPERTY(bool optimizeCode MEMBER m_optimizeCode WRITE setOptimizeCode)
/// @returns latest compilation results for contracts
QVariantMap contracts() const;
/// @returns compilation status
bool isCompiling() const { return m_compiling; }
/// @returns true there is a contract which has at least one function
bool hasContract() const;
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
/// Get contract by name
/// Throws if not found
CompiledContract const& contract(QString const& _name) const;
/// Get contract by name
/// @returns nullptr if not found
Q_INVOKABLE CompiledContract const* tryGetContract(QString const& _name) const;
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString const& _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Delete a contract source
Q_INVOKABLE void unregisterContractSrc(QString const& _documentId);
/// Convert solidity type info to mix type
static SolidityType nodeType(dev::solidity::Type const* _type);
/// Retrieve subtype
static void retrieveSubType(SolidityType& _wrapperType, 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);
/// Gas estimation for compiled sources
void gasEstimation(solidity::CompilerStack const& _cs);
/// Gas cost by doc id
Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const;
Q_INVOKABLE void setOptimizeCode(bool _value);
signals:
/// Emited on compilation state change
void stateChanged();
/// Emitted on compilation complete
void compilationComplete();
/// Emitted on compilation error
void compilationError(QString _error, QVariantMap _firstErrorLoc, QVariantList _secondErrorLoc);
/// Internal signal used to transfer compilation job to background thread
void scheduleCompilationJob(int _jobId);
/// Emitted if there are any changes in the code model
void codeChanged();
/// Emitted if there are any changes in the contract interface
void contractInterfaceChanged(QString _documentId);
/// Emitted if there is a new contract compiled for the first time
void newContractCompiled(QString _documentId);
/// Emitted if a contract name has been changed
void contractRenamed(QString _documentId, QString _oldName, QString _newName);
public slots:
/// Update code model on source code change
void registerCodeChange(QString const& _documentId, QString const& _code);
/// Reset code model for a new project
void reset(QVariantMap const& _documents);
private:
void runCompilationJob(int _jobId);
void stop();
void releaseContracts();
void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames);
QVariantMap resolveCompilationErrorLocation(dev::solidity::CompilerStack const& _cs, dev::SourceLocation const& _location);
std::atomic<bool> m_compiling;
mutable dev::Mutex x_contractMap;
ContractMap m_contractMap;
SourceMaps m_sourceMaps;
GasMapWrapper* m_gasCostsMaps = 0;
std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings;
QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker;
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
std::map<QString, dev::bytes> m_compiledContracts; //by name
dev::Mutex x_pendingContracts;
std::map<QString, QString> m_pendingContracts; //name to source
bool m_optimizeCode = false;
friend class BackgroundWorker;
};
}
}