Browse Source

Merge remote-tracking branch 'upstream/develop' into evmjit

Conflicts:
	evmjit/libevmjit/Utils.h
cl-refactor
Paweł Bylica 10 years ago
parent
commit
bf9b0d9e85
  1. 17
      CMakeLists.txt
  2. 4
      CodingStandards.txt
  3. 3
      alethzero/CMakeLists.txt
  4. 6
      alethzero/MainWin.cpp
  5. 30
      alethzero/OurWebThreeStubServer.cpp
  6. 16
      alethzero/OurWebThreeStubServer.h
  7. 2
      cmake/EthDependencies.cmake
  8. 2
      evmjit/libevmjit/ExecutionEngine.cpp
  9. 3
      evmjit/libevmjit/Utils.h
  10. 9
      libdevcore/CommonData.h
  11. 12
      libdevcore/Exceptions.h
  12. 8
      libdevcore/RLP.h
  13. 6
      libdevcore/vector_ref.h
  14. 2
      libdevcrypto/ECDHE.h
  15. 2
      libethcore/BlockInfo.cpp
  16. 62
      libethcore/CommonEth.cpp
  17. 8
      libethcore/Exceptions.h
  18. 28
      libethcore/ProofOfWork.h
  19. 6
      libethereum/BlockDetails.h
  20. 135
      libethereum/Client.cpp
  21. 57
      libethereum/Client.h
  22. 5
      libethereum/Interface.h
  23. 2
      libethereum/LogFilter.cpp
  24. 8
      libethereum/LogFilter.h
  25. 12
      libethereum/Miner.cpp
  26. 28
      libethereum/Miner.h
  27. 18
      libethereum/State.cpp
  28. 4
      libethereum/State.h
  29. 2
      libevm/VM.cpp
  30. 2
      libevmcore/Assembly.cpp
  31. 4
      libevmcore/Assembly.h
  32. 31
      libjsqrc/ethereumjs/dist/ethereum.js
  33. 4
      libjsqrc/ethereumjs/dist/ethereum.js.map
  34. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  35. 31
      libjsqrc/ethereumjs/lib/providermanager.js
  36. 1
      libjsqrc/js.qrc
  37. 4
      liblll/Parser.cpp
  38. 35
      libnatspec/CMakeLists.txt
  39. 59
      libnatspec/NatspecExpressionEvaluator.cpp
  40. 49
      libnatspec/NatspecExpressionEvaluator.h
  41. 69
      libnatspec/natspec.js
  42. 5
      libnatspec/natspec.qrc
  43. 6
      libp2p/Common.h
  44. 68
      libsolidity/AST.cpp
  45. 43
      libsolidity/AST.h
  46. 2
      libsolidity/ASTJsonConverter.cpp
  47. 4
      libsolidity/Compiler.cpp
  48. 12
      libsolidity/CompilerStack.cpp
  49. 8
      libsolidity/CompilerStack.h
  50. 4
      libsolidity/DeclarationContainer.cpp
  51. 4
      libsolidity/DeclarationContainer.h
  52. 2
      libsolidity/Exceptions.h
  53. 309
      libsolidity/ExpressionCompiler.cpp
  54. 40
      libsolidity/ExpressionCompiler.h
  55. 26
      libsolidity/GlobalContext.cpp
  56. 72
      libsolidity/InterfaceHandler.cpp
  57. 18
      libsolidity/InterfaceHandler.h
  58. 266
      libsolidity/Parser.cpp
  59. 1
      libsolidity/Parser.h
  60. 129
      libsolidity/Scanner.cpp
  61. 3
      libsolidity/Token.cpp
  62. 479
      libsolidity/Token.h
  63. 217
      libsolidity/Types.cpp
  64. 78
      libsolidity/Types.h
  65. 0
      libsolidity/Utils.h
  66. 11
      libweb3jsonrpc/CMakeLists.txt
  67. 12
      libweb3jsonrpc/WebThreeStubServer.h
  68. 20
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  69. 3
      libweb3jsonrpc/WebThreeStubServerBase.h
  70. 12
      libweb3jsonrpc/abstractwebthreestubserver.h
  71. 4
      libweb3jsonrpc/spec.json
  72. 2
      mix/AppContext.cpp
  73. 45
      mix/AssemblyDebuggerControl.cpp
  74. 48
      mix/AssemblyDebuggerControl.h
  75. 2
      mix/CMakeLists.txt
  76. 44
      mix/ClientModel.cpp
  77. 42
      mix/ClientModel.h
  78. 8
      mix/CodeEditorExtensionManager.cpp
  79. 10
      mix/CodeHighlighter.cpp
  80. 2
      mix/CodeHighlighter.h
  81. 7
      mix/ContractCallDataEncoder.cpp
  82. 8
      mix/Exceptions.h
  83. 8
      mix/MachineStates.h
  84. 181
      mix/MixClient.cpp
  85. 17
      mix/MixClient.h
  86. 48
      mix/StateListView.cpp
  87. 45
      mix/StateListView.h
  88. 31
      mix/qml/CallStack.qml
  89. 38
      mix/qml/DebugInfoList.qml
  90. 304
      mix/qml/Debugger.qml
  91. 250
      mix/qml/FilesSection.qml
  92. 71
      mix/qml/MainContent.qml
  93. 256
      mix/qml/ProjectList.qml
  94. 1
      mix/qml/ProjectModel.qml
  95. 58
      mix/qml/StateList.qml
  96. 10
      mix/qml/StateListModel.qml
  97. 30
      mix/qml/Style.qml
  98. 58
      mix/qml/TransactionDialog.qml
  99. 111
      mix/qml/TransactionLog.qml
  100. 3
      mix/qml/WebPreview.qml

17
CMakeLists.txt

@ -43,6 +43,10 @@ function(configureProject)
if (EVMJIT)
add_definitions(-DETH_EVMJIT)
endif()
if (HEADLESS)
add_definitions(-DETH_HEADLESS)
endif()
endfunction()
@ -65,13 +69,13 @@ function(createBuildInfo)
endif ()
if (EVMJIT)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/JIT")
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/JIT")
else ()
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/int")
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/int")
endif ()
if (PARANOIA)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/PARA")
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/PARA")
endif ()
#cmake build type may be not specified when using msvc
@ -131,11 +135,15 @@ endif()
add_subdirectory(libdevcore)
add_subdirectory(libevmcore)
add_subdirectory(liblll)
add_subdirectory(libserpent)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(libserpent)
endif ()
add_subdirectory(libsolidity)
add_subdirectory(lllc)
add_subdirectory(solc)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(sc)
endif()
if (JSONRPC)
add_subdirectory(libweb3jsonrpc)
@ -164,6 +172,7 @@ endif ()
if (NOT HEADLESS)
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
add_subdirectory(libqwebthree)
add_subdirectory(alethzero)

4
CodingStandards.txt

@ -93,7 +93,7 @@ b. Only one per line.
c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)).
d. Favour declarations close to use; don't habitually declare at top of scope ala C.
e. Always pass non-trivial parameters with a const& suffix.
f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires.
f. If a func tion returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires.
g. Never use a macro where adequate non-preprocessor C++ can be written.
h. Prefer "using NewType = OldType" to "typedef OldType NewType".
@ -128,7 +128,7 @@ d. Use override, final and const judiciously.
e. No implementations with the class declaration, except:
- template or force-inline method (though prefer implementation at bottom of header file).
- one-line implementation (in which case include it in same line as declaration).
f. For a method 'foo'
f. For a property 'foo'
- Member: m_foo;
- Getter: foo() [ also: for booleans, isFoo() ];
- Setter: setFoo();

3
alethzero/CMakeLists.txt

@ -40,13 +40,16 @@ target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} secp256k1)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)
endif()
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} natspec)
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE})

6
alethzero/MainWin.cpp

@ -30,8 +30,10 @@
#include <QtCore/QtCore>
#include <boost/algorithm/string.hpp>
#include <test/JsonSpiritHeaders.h>
#ifndef _MSC_VER
#include <libserpent/funcs.h>
#include <libserpent/util.h>
#endif
#include <libdevcrypto/FileSystem.h>
#include <libethcore/CommonJS.h>
#include <liblll/Compiler.h>
@ -1719,6 +1721,7 @@ void Main::on_data_textChanged()
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>";
}
}
#ifndef _MSC_VER
else if (sourceIsSerpent(src))
{
try
@ -1732,6 +1735,7 @@ void Main::on_data_textChanged()
errors.push_back("Serpent " + err);
}
}
#endif
else
{
m_data = compileLLL(src, m_enableOptimizer, &errors);
@ -1936,7 +1940,7 @@ void Main::on_send_clicked()
{
h256 contractHash = compiler.getContractCodeHash(s);
m_natspecDB.add(contractHash,
compiler.getMetadata(s, dev::solidity::DocumentationType::NATSPEC_USER));
compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser));
}
}
catch (...)

30
alethzero/OurWebThreeStubServer.cpp

@ -24,6 +24,7 @@
#include <QMessageBox>
#include <QAbstractButton>
#include <libwebthree/WebThree.h>
#include <libnatspec/NatspecExpressionEvaluator.h>
#include "MainWin.h"
@ -84,36 +85,9 @@ bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t)
return showAuthenticationPopup("Unverified Pending Transaction",
"An undocumented transaction is about to be executed.");
QNatspecExpressionEvaluator evaluator(this, m_main);
NatspecExpressionEvaluator evaluator;
userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
// otherwise it's a transaction to a contract for which we have the natspec
return showAuthenticationPopup("Pending Transaction", userNotice);
}
QNatspecExpressionEvaluator::QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main)
: m_server(_server), m_main(_main)
{}
QNatspecExpressionEvaluator::~QNatspecExpressionEvaluator()
{}
QString QNatspecExpressionEvaluator::evalExpression(QString const& _expression) const
{
// load natspec.js only when we need it for natspec evaluation
m_main->evalRaw(contentsOfQResource(":/js/natspec.js"));
QVariant result = m_main->evalRaw("evaluateExpression('" + _expression + "')");
if (result.type() == QVariant::Invalid)
{
cerr << "Could not evaluate natspec expression: \"" << _expression.toStdString() << "\"" << endl;
// return the expression unevaluated
return _expression;
}
return result.toString();
}

16
alethzero/OurWebThreeStubServer.h

@ -47,19 +47,3 @@ private:
dev::WebThreeDirect* m_web3;
Main* m_main;
};
class QNatspecExpressionEvaluator: public QObject
{
Q_OBJECT
public:
QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main);
virtual ~QNatspecExpressionEvaluator();
QString evalExpression(QString const& _expression) const;
private:
OurWebThreeStubServer* m_server;
Main* m_main;
};

2
cmake/EthDependencies.cmake

@ -122,6 +122,8 @@ if (NOT HEADLESS)
string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM})
message(" - npm location : ${ETH_NPM}")
find_program(ETH_JSON_RPC_STUB jsonrpcstub)
endif() #HEADLESS
# use multithreaded boost libraries, with -mt suffix

2
evmjit/libevmjit/ExecutionEngine.cpp

@ -28,7 +28,7 @@ namespace jit
namespace
{
typedef ReturnCode(*EntryFuncPtr)(Runtime*);
using EntryFuncPtr = ReturnCode(*)(Runtime*);
std::string codeHash(i256 const& _hash)
{

3
evmjit/libevmjit/Utils.h

@ -1,4 +1,7 @@
#pragma once
#include <iostream>
#include "Common.h"
namespace dev

9
libdevcore/CommonData.h

@ -246,4 +246,13 @@ inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b)
/// Make normal string from fixed-length string.
std::string toString(string32 const& _s);
template<class T, class U>
std::vector<T> keysOf(std::map<T, U> const& _m)
{
std::vector<T> ret;
for (auto const& i: _m)
ret.push_back(i.first);
return ret;
}
}

12
libdevcore/Exceptions.h

@ -44,10 +44,10 @@ struct FileError: virtual Exception {};
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; };
// error information to be added to exceptions
typedef boost::error_info<struct tag_invalidSymbol, char> errinfo_invalidSymbol;
typedef boost::error_info<struct tag_address, std::string> errinfo_wrongAddress;
typedef boost::error_info<struct tag_comment, std::string> errinfo_comment;
typedef boost::error_info<struct tag_required, bigint> errinfo_required;
typedef boost::error_info<struct tag_got, bigint> errinfo_got;
typedef boost::tuple<errinfo_required, errinfo_got> RequirementError;
using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;
using errinfo_wrongAddress = boost::error_info<struct tag_address, std::string>;
using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
using errinfo_required = boost::error_info<struct tag_required, bigint>;
using errinfo_got = boost::error_info<struct tag_got, bigint>;
using RequirementError = boost::tuple<errinfo_required, errinfo_got>;
}

8
libdevcore/RLP.h

@ -37,7 +37,7 @@ namespace dev
{
class RLP;
typedef std::vector<RLP> RLPs;
using RLPs = std::vector<RLP>;
template <class _T> struct intTraits { static const unsigned maxSize = sizeof(_T); };
template <> struct intTraits<u160> { static const unsigned maxSize = 20; };
@ -125,7 +125,7 @@ public:
/// @note if used to access items in ascending order, this is efficient.
RLP operator[](unsigned _i) const;
typedef RLP element_type;
using element_type = RLP;
/// @brief Iterator class for iterating through items of RLP list.
class iterator
@ -133,8 +133,8 @@ public:
friend class RLP;
public:
typedef RLP value_type;
typedef RLP element_type;
using value_type = RLP;
using element_type = RLP;
iterator& operator++();
iterator operator++(int) { auto ret = *this; operator++(); return ret; }

6
libdevcore/vector_ref.h

@ -12,9 +12,9 @@ template <class _T>
class vector_ref
{
public:
typedef _T value_type;
typedef _T element_type;
typedef typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type mutable_value_type;
using value_type = _T;
using element_type = _T;
using mutable_value_type = typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type;
vector_ref(): m_data(nullptr), m_count(0) {}
vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {}

2
libdevcrypto/ECDHE.h

@ -31,7 +31,7 @@ namespace crypto
{
/// Public key of remote and corresponding shared secret.
typedef std::pair<Public,h256> AliasSession;
using AliasSession = std::pair<Public,h256>;
/**
* @brief An addressable EC key pair.

2
libethcore/BlockInfo.cpp

@ -208,7 +208,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const
if (parentHash != _parent.hash)
BOOST_THROW_EXCEPTION(InvalidParentHash());
if (timestamp < _parent.timestamp)
if (timestamp <= _parent.timestamp)
BOOST_THROW_EXCEPTION(InvalidTimestamp());
if (number != _parent.number + 1)

62
libethcore/CommonEth.cpp

@ -32,35 +32,45 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 52;
const unsigned c_protocolVersion = 53;
const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units =
template <size_t n> constexpr u256 exp10()
{
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Uether"},
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Vether"},
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Dether"},
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Nether"},
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Yether"},
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Zether"},
{((u256(1000000000) * 1000000000) * 1000000000) * 1000000000, "Eether"},
{((u256(1000000000) * 1000000000) * 1000000000) * 1000000, "Pether"},
{((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"},
{(u256(1000000000) * 1000000000) * 1000000000, "Gether"},
{(u256(1000000000) * 1000000000) * 1000000, "Mether"},
{(u256(1000000000) * 1000000000) * 1000, "grand"},
{u256(1000000000) * 1000000000, "ether"},
{u256(1000000000) * 1000000, "finney"},
{u256(1000000000) * 1000, "szabo"},
{u256(1000000000), "Gwei"},
{u256(1000000), "Mwei"},
{u256(1000), "Kwei"},
{u256(1), "wei"}
};
return exp10<n - 1>() * u256(10);
}
template <> constexpr u256 exp10<0>()
{
return u256(1);
}
vector<pair<u256, string>> const& units()
{
return g_units;
static const vector<pair<u256, string>> s_units =
{
{exp10<54>(), "Uether"},
{exp10<51>(), "Vether"},
{exp10<48>(), "Dether"},
{exp10<45>(), "Nether"},
{exp10<42>(), "Yether"},
{exp10<39>(), "Zether"},
{exp10<36>(), "Eether"},
{exp10<33>(), "Pether"},
{exp10<30>(), "Tether"},
{exp10<27>(), "Gether"},
{exp10<24>(), "Mether"},
{exp10<21>(), "grand"},
{exp10<18>(), "ether"},
{exp10<15>(), "finney"},
{exp10<12>(), "szabo"},
{exp10<9>(), "Gwei"},
{exp10<6>(), "Mwei"},
{exp10<3>(), "Kwei"},
{exp10<0>(), "wei"}
};
return s_units;
}
std::string formatBalance(bigint const& _b)
@ -75,13 +85,13 @@ std::string formatBalance(bigint const& _b)
else
b = (u256)_b;
if (b > g_units[0].first * 10000)
if (b > units()[0].first * 10000)
{
ret << (b / g_units[0].first) << " " << g_units[0].second;
ret << (b / units()[0].first) << " " << units()[0].second;
return ret.str();
}
ret << setprecision(5);
for (auto const& i: g_units)
for (auto const& i: units())
if (i.first != 1 && b >= i.first * 100)
{
ret << (double(b / (i.first / 1000)) / 1000.0) << " " << i.second;

8
libethcore/Exceptions.h

@ -29,10 +29,10 @@ namespace eth
{
// information to add to exceptions
typedef boost::error_info<struct tag_field, std::string> errinfo_name;
typedef boost::error_info<struct tag_field, int> errinfo_field;
typedef boost::error_info<struct tag_data, std::string> errinfo_data;
typedef boost::tuple<errinfo_field, errinfo_data> BadFieldError;
using errinfo_name = boost::error_info<struct tag_field, std::string>;
using errinfo_field = boost::error_info<struct tag_field, int>;
using errinfo_data = boost::error_info<struct tag_data, std::string>;
using BadFieldError = boost::tuple<errinfo_field, errinfo_data>;
struct DatabaseAlreadyOpen: virtual dev::Exception {};
struct NotEnoughCash: virtual dev::Exception {};

28
libethcore/ProofOfWork.h

@ -51,7 +51,7 @@ class ProofOfWorkEngine: public Evaluator
public:
static bool verify(h256 const& _root, h256 const& _nonce, u256 const& _difficulty) { return (bigint)(u256)Evaluator::eval(_root, _nonce) <= (bigint(1) << 256) / _difficulty; }
inline MineInfo mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false);
inline std::pair<MineInfo, h256> mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false);
protected:
h256 m_last;
@ -79,14 +79,14 @@ using SHA3ProofOfWork = ProofOfWorkEngine<SHA3Evaluator>;
using ProofOfWork = SHA3ProofOfWork;
template <class Evaluator>
MineInfo ProofOfWorkEngine<Evaluator>::mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo)
std::pair<MineInfo, h256> ProofOfWorkEngine<Evaluator>::mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo)
{
MineInfo ret;
std::pair<MineInfo, h256> ret;
static std::mt19937_64 s_eng((time(0) + (unsigned)m_last));
u256 s = (m_last = h256::random(s_eng));
bigint d = (bigint(1) << 256) / _difficulty;
ret.requirement = log2((double)d);
ret.first.requirement = log2((double)d);
// 2^ 0 32 64 128 256
// [--------*-------------------------]
@ -95,20 +95,26 @@ MineInfo ProofOfWorkEngine<Evaluator>::mine(h256& o_solution, h256 const& _root,
auto startTime = std::chrono::steady_clock::now();
if (!_turbo)
std::this_thread::sleep_for(std::chrono::milliseconds(_msTimeout * 90 / 100));
for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, ret.hashes++)
double best = 1e99; // high enough to be effectively infinity :)
h256 solution;
unsigned h = 0;
for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++)
{
o_solution = (h256)s;
auto e = (bigint)(u256)Evaluator::eval(_root, o_solution);
ret.best = std::min<double>(ret.best, log2((double)e));
solution = (h256)s;
auto e = (bigint)(u256)Evaluator::eval(_root, solution);
best = std::min<double>(best, log2((double)e));
if (e <= d)
{
ret.completed = true;
ret.first.completed = true;
break;
}
}
ret.first.hashes = h;
ret.first.best = best;
ret.second = solution;
if (ret.completed)
assert(verify(_root, o_solution, _difficulty));
if (ret.first.completed)
assert(verify(_root, solution, _difficulty));
return ret;
}

6
libethereum/BlockDetails.h

@ -70,9 +70,9 @@ struct BlockReceipts
TransactionReceipts receipts;
};
typedef std::map<h256, BlockDetails> BlockDetailsHash;
typedef std::map<h256, BlockLogBlooms> BlockLogBloomsHash;
typedef std::map<h256, BlockReceipts> BlockReceiptsHash;
using BlockDetailsHash = std::map<h256, BlockDetails>;
using BlockLogBloomsHash = std::map<h256, BlockLogBlooms>;
using BlockReceiptsHash = std::map<h256, BlockReceipts>;
static const BlockDetails NullBlockDetails;
static const BlockLogBlooms NullBlockLogBlooms;

135
libethereum/Client.cpp

@ -126,7 +126,7 @@ void Client::killChain()
m_tq.clear();
m_bq.clear();
m_miners.clear();
m_localMiners.clear();
m_preMine = State();
m_postMine = State();
@ -167,8 +167,8 @@ void Client::clearPending()
}
{
ReadGuard l(x_miners);
for (auto& m: m_miners)
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
m.noteStateChange();
}
@ -223,13 +223,17 @@ void Client::uninstallWatch(unsigned _i)
auto fit = m_filters.find(id);
if (fit != m_filters.end())
if (!--fit->second.refCount)
{
cwatch << "*X*" << fit->first << ":" << fit->second.filter;
m_filters.erase(fit);
}
}
void Client::noteChanged(h256Set const& _filters)
{
Guard l(m_filterLock);
cnote << "noteChanged(" << _filters << ")";
if (_filters.size())
cnote << "noteChanged(" << _filters << ")";
// accrue all changes left in each filter into the watches.
for (auto& i: m_watches)
if (_filters.count(i.second.id))
@ -250,8 +254,11 @@ LocalisedLogEntries Client::peekWatch(unsigned _watchId) const
Guard l(m_filterLock);
try {
return m_watches.at(_watchId).changes;
auto& w = m_watches.at(_watchId);
w.lastPoll = chrono::system_clock::now();
return w.changes;
} catch (...) {}
return LocalisedLogEntries();
}
@ -261,7 +268,9 @@ LocalisedLogEntries Client::checkWatch(unsigned _watchId)
LocalisedLogEntries ret;
try {
std::swap(ret, m_watches.at(_watchId).changes);
auto& w = m_watches.at(_watchId);
std::swap(ret, w.changes);
w.lastPoll = chrono::system_clock::now();
} catch (...) {}
return ret;
@ -311,8 +320,8 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
void Client::setForceMining(bool _enable)
{
m_forceMining = _enable;
ReadGuard l(x_miners);
for (auto& m: m_miners)
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
m.noteStateChange();
}
@ -321,19 +330,19 @@ void Client::setMiningThreads(unsigned _threads)
stopMining();
auto t = _threads ? _threads : thread::hardware_concurrency();
WriteGuard l(x_miners);
m_miners.clear();
m_miners.resize(t);
WriteGuard l(x_localMiners);
m_localMiners.clear();
m_localMiners.resize(t);
unsigned i = 0;
for (auto& m: m_miners)
for (auto& m: m_localMiners)
m.setup(this, i++);
}
MineProgress Client::miningProgress() const
{
MineProgress ret;
ReadGuard l(x_miners);
for (auto& m: m_miners)
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
ret.combine(m.miningProgress());
return ret;
}
@ -342,13 +351,13 @@ std::list<MineInfo> Client::miningHistory()
{
std::list<MineInfo> ret;
ReadGuard l(x_miners);
if (m_miners.empty())
ReadGuard l(x_localMiners);
if (m_localMiners.empty())
return ret;
ret = m_miners[0].miningHistory();
for (unsigned i = 1; i < m_miners.size(); ++i)
ret = m_localMiners[0].miningHistory();
for (unsigned i = 1; i < m_localMiners.size(); ++i)
{
auto l = m_miners[i].miningHistory();
auto l = m_localMiners[i].miningHistory();
auto ri = ret.begin();
auto li = l.begin();
for (; ri != ret.end() && li != l.end(); ++ri, ++li)
@ -465,6 +474,22 @@ void Client::inject(bytesConstRef _rlp)
m_tq.attemptImport(_rlp);
}
pair<h256, u256> Client::getWork()
{
Guard l(x_remoteMiner);
{
ReadGuard l(x_stateDB);
m_remoteMiner.update(m_postMine, m_bc);
}
return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty());
}
bool Client::submitNonce(h256 const&_nonce)
{
Guard l(x_remoteMiner);
return m_remoteMiner.submitWork(_nonce);
}
void Client::doWork()
{
// TODO: Use condition variable rather than polling.
@ -472,27 +497,34 @@ void Client::doWork()
cworkin << "WORK";
h256Set changeds;
auto maintainMiner = [&](Miner& m)
{
ReadGuard l(x_miners);
for (auto& m: m_miners)
if (m.isComplete())
if (m.isComplete())
{
cwork << "CHAIN <== postSTATE";
h256s hs;
{
cwork << "CHAIN <== postSTATE";
h256s hs;
{
WriteGuard l(x_stateDB);
hs = m_bc.attemptImport(m.blockData(), m_stateDB);
}
if (hs.size())
{
for (auto h: hs)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
//changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions.
}
for (auto& m: m_miners)
m.noteStateChange();
WriteGuard l(x_stateDB);
hs = m_bc.attemptImport(m.blockData(), m_stateDB);
}
if (hs.size())
{
for (auto const& h: hs)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
}
for (auto& m: m_localMiners)
m.noteStateChange();
}
};
{
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
maintainMiner(m);
}
{
Guard l(x_remoteMiner);
maintainMiner(m_remoteMiner);
}
// Synchronise state to block chain.
@ -502,7 +534,7 @@ void Client::doWork()
// if there are no checkpoints before our fork) reverting to the genesis block and replaying
// all blocks.
// Resynchronise state with block chain & trans
bool rsm = false;
bool resyncStateNeeded = false;
{
WriteGuard l(x_stateDB);
cwork << "BQ ==> CHAIN ==> STATE";
@ -525,7 +557,7 @@ void Client::doWork()
if (isMining())
cnote << "New block on chain: Restarting mining operation.";
m_postMine = m_preMine;
rsm = true;
resyncStateNeeded = true;
changeds.insert(PendingChangedFilter);
// TODO: Move transactions pending from m_postMine back to transaction queue.
}
@ -541,13 +573,13 @@ void Client::doWork()
if (isMining())
cnote << "Additional transaction ready: Restarting mining operation.";
rsm = true;
resyncStateNeeded = true;
}
}
if (rsm)
if (resyncStateNeeded)
{
ReadGuard l(x_miners);
for (auto& m: m_miners)
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
m.noteStateChange();
}
@ -556,6 +588,23 @@ void Client::doWork()
cworkout << "WORK";
this_thread::sleep_for(chrono::milliseconds(100));
if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
{
// garbage collect on watches
vector<unsigned> toUninstall;
{
Guard l(m_filterLock);
for (auto key: keysOf(m_watches))
if (chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
{
toUninstall.push_back(key);
cnote << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
}
for (auto i: toUninstall)
uninstallWatch(i);
m_lastGarbageCollection = chrono::system_clock::now();
}
}
unsigned Client::numberOf(int _n) const

57
libethereum/Client.h

@ -89,11 +89,12 @@ static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0);
struct ClientWatch
{
ClientWatch() {}
explicit ClientWatch(h256 _id): id(_id) {}
ClientWatch(): lastPoll(std::chrono::system_clock::now()) {}
explicit ClientWatch(h256 _id): id(_id), lastPoll(std::chrono::system_clock::now()) {}
h256 id;
LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange };
mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now();
};
struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; };
@ -134,6 +135,28 @@ template <class T> T abiOut(bytes const& _data)
return ABIDeserialiser<T>::deserialise(o);
}
class RemoteMiner: public Miner
{
public:
RemoteMiner() {}
void update(State const& _provisional, BlockChain const& _bc) { m_state = _provisional; m_state.commitToMine(_bc); }
h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); }
u256 const& difficulty() const { return m_state.info().difficulty; }
bool submitWork(h256 const& _nonce) { return (m_isComplete = m_state.completeMine(_nonce)); }
virtual bool isComplete() const override { return m_isComplete; }
virtual bytes const& blockData() const { return m_state.blockData(); }
virtual void noteStateChange() override {}
private:
bool m_isComplete = false;
State m_state;
};
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@ -252,20 +275,26 @@ public:
/// Stops mining and sets the number of mining threads (0 for automatic).
virtual void setMiningThreads(unsigned _threads = 0);
/// Get the effective number of mining threads.
virtual unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); }
virtual unsigned miningThreads() const { ReadGuard l(x_localMiners); return m_localMiners.size(); }
/// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread
virtual void startMining() { startWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); }
virtual void startMining() { startWorking(); ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.start(); }
/// Stop mining.
/// NOT thread-safe
virtual void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); }
virtual void stopMining() { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.stop(); }
/// Are we mining now?
virtual bool isMining() { ReadGuard l(x_miners); return m_miners.size() && m_miners[0].isRunning(); }
virtual bool isMining() { ReadGuard l(x_localMiners); return m_localMiners.size() && m_localMiners[0].isRunning(); }
/// Check the progress of the mining.
virtual MineProgress miningProgress() const;
/// Get and clear the mining history.
std::list<MineInfo> miningHistory();
/// Update to the latest transactions and get hash of the current block to be mined minus the
/// nonce (the 'work hash') and the difficulty to be met.
virtual std::pair<h256, u256> getWork() override;
/// Submit the nonce for the proof-of-work.
virtual bool submitNonce(h256 const&_nonce) override;
// Debug stuff:
DownloadMan const* downloadMan() const;
@ -294,6 +323,7 @@ private:
/// Do some work. Handles blockchain maintenance and mining.
virtual void doWork();
/// Called when Worker is exiting.
virtual void doneWorking();
/// Overrides for being a mining host.
@ -308,26 +338,31 @@ private:
State asOf(unsigned _h) const;
VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database.
CanonBlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
State m_preMine; ///< The present state of the client.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
std::vector<Miner> m_miners;
mutable SharedMutex x_miners;
mutable Mutex x_remoteMiner; ///< The remote miner lock.
RemoteMiner m_remoteMiner; ///< The remote miner.
std::vector<LocalMiner> m_localMiners; ///< The in-process miners.
mutable SharedMutex x_localMiners; ///< The in-process miners lock.
bool m_paranoia = false; ///< Should we be paranoid about our state?
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
mutable std::mutex m_filterLock;
mutable Mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, ClientWatch> m_watches;
mutable std::chrono::system_clock::time_point m_lastGarbageCollection;
};
}

5
libethereum/Interface.h

@ -149,6 +149,11 @@ public:
/// Are we mining now?
virtual bool isMining() = 0;
/// Get hash of the current block to be mined minus the nonce (the 'work hash').
virtual std::pair<h256, u256> getWork() = 0;
/// Submit the nonce for the proof-of-work.
virtual bool submitNonce(h256 const&) = 0;
/// Check the progress of the mining.
virtual MineProgress miningProgress() const = 0;

2
libethereum/LogFilter.cpp

@ -27,7 +27,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
std::ostream& dev::operator<<(std::ostream& _out, LogFilter const& _s)
std::ostream& dev::eth::operator<<(std::ostream& _out, LogFilter const& _s)
{
// TODO
_out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< +" << _s.m_skip << "^" << _s.m_max << ")";

8
libethereum/LogFilter.h

@ -34,12 +34,12 @@ namespace eth
class LogFilter;
}
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
namespace eth
{
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
class State;
class LogFilter
@ -65,7 +65,7 @@ public:
LogFilter withEarliest(int _e) { m_earliest = _e; return *this; }
LogFilter withLatest(int _e) { m_latest = _e; return *this; }
friend std::ostream& dev::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
private:
AddressSet m_addresses;

12
libethereum/Miner.cpp

@ -15,8 +15,8 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Miner.cpp
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @author Giacomo Tazzari
* @date 2014
*/
@ -28,19 +28,21 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
Miner::Miner(MinerHost* _host, unsigned _id):
Miner::~Miner() {}
LocalMiner::LocalMiner(MinerHost* _host, unsigned _id):
Worker("miner-" + toString(_id)),
m_host(_host)
{
}
void Miner::setup(MinerHost* _host, unsigned _id)
void LocalMiner::setup(MinerHost* _host, unsigned _id)
{
m_host = _host;
setName("miner-" + toString(_id));
}
void Miner::doWork()
void LocalMiner::doWork()
{
// Do some mining.
if (m_miningStatus != Waiting && m_miningStatus != Mined)
@ -63,7 +65,7 @@ void Miner::doWork()
if (m_miningStatus == Mining)
{
// Mine for a while.
// Mine for a while.
MineInfo mineInfo = m_mineState.mine(100, m_host->turbo());
{

28
libethereum/Miner.h

@ -63,6 +63,16 @@ public:
virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions.
};
class Miner
{
public:
virtual ~Miner();
virtual void noteStateChange() = 0;
virtual bool isComplete() const = 0;
virtual bytes const& blockData() const = 0;
};
/**
* @brief Implements Miner.
* To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the
@ -75,23 +85,23 @@ public:
* @threadsafe
* @todo Signal Miner to restart once with condition variables.
*/
class Miner: Worker
class LocalMiner: public Miner, Worker
{
public:
/// Null constructor.
Miner(): m_host(nullptr) {}
LocalMiner(): m_host(nullptr) {}
/// Constructor.
Miner(MinerHost* _host, unsigned _id = 0);
LocalMiner(MinerHost* _host, unsigned _id = 0);
/// Move-constructor.
Miner(Miner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); }
LocalMiner(LocalMiner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); }
/// Move-assignment.
Miner& operator=(Miner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); return *this; }
LocalMiner& operator=(LocalMiner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); return *this; }
/// Destructor. Stops miner.
~Miner() { stop(); }
~LocalMiner() { stop(); }
/// Setup its basics.
void setup(MinerHost* _host, unsigned _id = 0);
@ -103,16 +113,16 @@ public:
void stop() { stopWorking(); }
/// Call to notify Miner of a state change.
void noteStateChange() { m_miningStatus = Preparing; }
virtual void noteStateChange() override { m_miningStatus = Preparing; }
/// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force().
bool isRunning() { return isWorking(); }
/// @returns true if mining is complete.
bool isComplete() const { return m_miningStatus == Mined; }
virtual bool isComplete() const override { return m_miningStatus == Mined; }
/// @returns the internal State object.
bytes const& blockData() { return m_mineState.blockData(); }
virtual bytes const& blockData() const override { return m_mineState.blockData(); }
/// Check the progress of the mining.
MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; }

18
libethereum/State.cpp

@ -774,19 +774,31 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo)
// Update difficulty according to timestamp.
m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock);
MineInfo ret;
// TODO: Miner class that keeps dagger between mine calls (or just non-polling mining).
auto ret = m_pow.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHash(WithoutNonce), m_currentBlock.difficulty, _msTimeout, true, _turbo);
tie(ret, m_currentBlock.nonce) = m_pow.mine(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.difficulty, _msTimeout, true, _turbo);
if (!ret.completed)
m_currentBytes.clear();
else
{
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.nonce, m_currentBlock.difficulty);
}
return ret;
}
bool State::completeMine(h256 const& _nonce)
{
if (!m_pow.verify(m_currentBlock.headerHash(WithoutNonce), _nonce, m_currentBlock.difficulty))
return false;
m_currentBlock.nonce = _nonce;
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.nonce, m_currentBlock.difficulty);
completeMine();
return true;
}
void State::completeMine()
{
cdebug << "Completing mine!";

4
libethereum/State.h

@ -113,6 +113,10 @@ public:
/// This may be called multiple times and without issue.
void commitToMine(BlockChain const& _bc);
/// Pass in a solution to the proof-of-work.
/// @returns true iff the given nonce is a proof-of-work for this State's block.
bool completeMine(h256 const& _nonce);
/// Attempt to find valid nonce for block that this state represents.
/// This function is thread-safe. You can safely have other interactions with this object while it is happening.
/// @param _msTimeout Timeout before return in milliseconds.

2
libevm/VM.cpp

@ -354,7 +354,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
auto base = m_stack.back();
auto expon = m_stack[m_stack.size() - 2];
m_stack.pop_back();
m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256);
m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(1) << 256);
break;
}
case Instruction::NOT:

2
libevmcore/Assembly.cpp

@ -291,7 +291,7 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} },
{ Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} },
{ Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} },
{ Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(2) << 256);} },
{ Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} },
{ Instruction::SIGNEXTEND, signextend },
{ Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} },
{ Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} },

4
libevmcore/Assembly.h

@ -63,8 +63,8 @@ private:
u256 m_data;
};
typedef std::vector<AssemblyItem> AssemblyItems;
typedef vector_ref<AssemblyItem const> AssemblyItemsConstRef;
using AssemblyItems = std::vector<AssemblyItem>;
using AssemblyItemsConstRef = vector_ref<AssemblyItem const>;
std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i);
inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); }

31
libjsqrc/ethereumjs/dist/ethereum.js

@ -1043,33 +1043,16 @@ var ProviderManager = function() {
var self = this;
var poll = function () {
if (self.provider) {
var pollsBatch = self.polls.map(function (data) {
return data.data;
});
var payload = jsonrpc.toBatchPayload(pollsBatch);
var results = self.provider.send(payload);
self.polls.forEach(function (data, index) {
var result = results[index];
if (!jsonrpc.isValidResponse(result)) {
console.log(result);
return;
}
result = result.result;
// dont call the callback if result is not an array, or empty one
if (!(result instanceof Array) || result.length === 0) {
return;
}
self.polls.forEach(function (data) {
var result = self.send(data.data);
data.callback(result);
if (!(result instanceof Array) || result.length === 0) {
return;
}
});
data.callback(result);
});
}
setTimeout(poll, 1000);
};
poll();

4
libjsqrc/ethereumjs/dist/ethereum.js.map

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/dist/ethereum.min.js

File diff suppressed because one or more lines are too long

31
libjsqrc/ethereumjs/lib/providermanager.js

@ -42,33 +42,16 @@ var ProviderManager = function() {
var self = this;
var poll = function () {
if (self.provider) {
var pollsBatch = self.polls.map(function (data) {
return data.data;
});
self.polls.forEach(function (data) {
var result = self.send(data.data);
var payload = jsonrpc.toBatchPayload(pollsBatch);
var results = self.provider.send(payload);
if (!(result instanceof Array) || result.length === 0) {
return;
}
self.polls.forEach(function (data, index) {
var result = results[index];
if (!jsonrpc.isValidResponse(result)) {
console.log(result);
return;
}
data.callback(result);
});
result = result.result;
// dont call the callback if result is not an array, or empty one
if (!(result instanceof Array) || result.length === 0) {
return;
}
data.callback(result);
});
}
setTimeout(poll, 1000);
};
poll();

1
libjsqrc/js.qrc

@ -3,6 +3,5 @@
<file>bignumber.min.js</file>
<file>setup.js</file>
<file alias="webthree.js">ethereumjs/dist/ethereum.js</file>
<file>natspec.js</file>
</qresource>
</RCC>

4
liblll/Parser.cpp

@ -89,8 +89,8 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out)
using qi::standard::space;
using qi::standard::space_type;
using dev::eth::parseTreeLLL_::tagNode;
typedef sp::basic_string<std::string, sp::utree_type::symbol_type> symbol_type;
typedef string::const_iterator it;
using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>;
using it = string::const_iterator;
static const u256 ether = u256(1000000000) * 1000000000;
static const u256 finney = u256(1000000000) * 1000000;

35
libnatspec/CMakeLists.txt

@ -0,0 +1,35 @@
cmake_policy(SET CMP0015 NEW)
# let cmake autolink dependencies on windows
cmake_policy(SET CMP0020 NEW)
# this policy was introduced in cmake 3.0
# remove if, once 3.0 will be used on unix
if (${CMAKE_MAJOR_VERSION} GREATER 2)
# old policy do not use MACOSX_RPATH
cmake_policy(SET CMP0042 OLD)
cmake_policy(SET CMP0043 OLD)
endif()
set(CMAKE_AUTOMOC OFF)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(..)
set(EXECUTABLE natspec)
file(GLOB HEADERS "*.h")
qt5_add_resources(NATSPECQRC natspec.qrc)
if (ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${RESOURCE_ADDED} ${SRC_LIST} ${HEADERS} ${NATSPECQRC})
else()
add_library(${EXECUTABLE} SHARED ${RESOURCE_ADDED} ${SRC_LIST} ${HEADERS} ${NATSPECQRC})
endif()
target_link_libraries(${EXECUTABLE} Qt5::Core)
target_link_libraries(${EXECUTABLE} Qt5::Qml)
target_link_libraries(${EXECUTABLE} devcore)
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

59
libnatspec/NatspecExpressionEvaluator.cpp

@ -0,0 +1,59 @@
/*
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 NatspecExpressionEvaluator.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h>
#include "NatspecExpressionEvaluator.h"
using namespace std;
using namespace dev;
static QString contentsOfQResource(string const& _res)
{
QFile file(QString::fromStdString(_res));
if (!file.open(QFile::ReadOnly))
BOOST_THROW_EXCEPTION(FileError());
QTextStream in(&file);
return in.readAll();
}
NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _method, QString const& _params)
{
Q_INIT_RESOURCE(natspec);
QJSValue result = m_engine.evaluate(contentsOfQResource(":/natspec/natspec.js"));
if (result.isError())
BOOST_THROW_EXCEPTION(FileError());
m_engine.evaluate("globals.abi = " + _abi);
m_engine.evaluate("globals.method = " + _method);
m_engine.evaluate("globals.params = " + _params);
}
QString NatspecExpressionEvaluator::evalExpression(QString const& _expression)
{
QJSValue result = m_engine.evaluate("evaluateExpression(\"" + _expression + "\")");
if (result.isError())
{
cerr << "Could not evaluate expression: \"" << _expression.toStdString() << "\"" << endl;
return _expression;
}
return result.toString();
}

49
libnatspec/NatspecExpressionEvaluator.h

@ -0,0 +1,49 @@
/*
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 NatspecExpressionEvaluator.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#include <QtCore/QObject>
#include <QtCore/QtCore>
#include <QtQml/QJSEngine>
/**
* Should be used to evaluate natspec expression.
* @see test/natspec.cpp for natspec expression examples
*/
class NatspecExpressionEvaluator
{
public:
/// Construct natspec expression evaluator
/// @params abi - contract's abi in json format, passed as string
/// @params method - name of the contract's method for which we evaluate the natspec.
/// If we want to use raw string, it should be passed with quotation marks eg. "\"helloWorld\""
/// If we pass string "helloWorld", the value of the object with name "helloWorld" will be used
/// @params params - array of method input params, passed as string, objects in array should be
/// javascript valid objects
NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _method = "", QString const& _params = "[]");
/// Should be called to evaluate natspec expression
/// @params expression - natspec expression
/// @returns evaluated natspec expression if it was valid, otherwise original expression
QString evalExpression(QString const& _expression);
private:
QJSEngine m_engine;
};

69
libjsqrc/natspec.js → libnatspec/natspec.js

@ -2,13 +2,19 @@
/**
* This plugin exposes 'evaluateExpression' method which should be used
* to evaluate natspec description
* It should be reloaded each time we want to evaluate set of expressions
* Just because of security reasons
* TODO: make use of sync api (once it's finished) and remove unnecessary
* code from 'getContractMethods'
* TODO: unify method signature creation with abi.js (and make a separate method from it)
*/
/// Object which should be used by NatspecExpressionEvaluator
/// abi - abi of the contract that will be used
/// method - name of the method that is called
/// params - input params of the method that will be called
var globals = {
abi: [],
method: "",
params: []
};
/// Helper method
/// Should be called to copy values from object to global context
var copyToContext = function (obj, context) {
var keys = Object.keys(obj);
@ -17,32 +23,38 @@ var copyToContext = function (obj, context) {
});
}
/// Helper method
/// Should be called to get method with given name from the abi
/// @param contract's abi
/// @param name of the method that we are looking for
var getMethodWithName = function(abi, name) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].name === name) {
return abi[i];
}
}
//console.warn('could not find method with name: ' + name);
return undefined;
};
/// Function called to get all contract's storage values
/// @returns hashmap with contract properties which are used
/// TODO: check if this function will be used
var getContractProperties = function (address, abi) {
return {};
};
/// Function called to get all contract's methods
/// @returns hashmap with used contract's methods
/// TODO: check if this function will be used
var getContractMethods = function (address, abi) {
return web3.eth.contract(address, abi);
};
var getMethodWithName = function(abi, name) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].name === name) {
return abi[i];
}
}
console.warn('could not find method with name: ' + name);
return undefined;
//return web3.eth.contract(address, abi); // commented out web3 usage
return {};
};
/// Function called to get all contract method input variables
/// @returns hashmap with all contract's method input variables
var getContractInputParams = function (abi, methodName, params) {
var method = getMethodWithName(abi, methodName);
var getMethodInputParams = function (method, params) {
return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index];
return acc;
@ -55,18 +67,15 @@ var getContractInputParams = function (abi, methodName, params) {
var evaluateExpression = function (expression) {
var self = this;
var abi = web3._currentContractAbi;
var address = web3._currentContractAddress;
var methodName = web3._currentContractMethodName;
var params = web3._currentContractMethodParams;
var storage = getContractProperties(address, abi);
var methods = getContractMethods(address, abi);
var inputParams = getContractInputParams(abi, methodName, params);
copyToContext(storage, self);
copyToContext(methods, self);
copyToContext(inputParams, self);
//var storage = getContractProperties(address, abi);
//var methods = getContractMethods(address, abi);
var method = getMethodWithName(globals.abi, globals.method);
if (method) {
var input = getMethodInputParams(method, globals.params);
copyToContext(input, self);
}
// TODO: test if it is safe
var evaluatedExpression = "";

5
libnatspec/natspec.qrc

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/natspec">
<file>natspec.js</file>
</qresource>
</RCC>

6
libp2p/Common.h

@ -112,9 +112,9 @@ inline bool isPermanentProblem(DisconnectReason _r)
/// @returns the string form of the given disconnection reason.
std::string reasonOf(DisconnectReason _r);
typedef std::pair<std::string, u256> CapDesc;
typedef std::set<CapDesc> CapDescSet;
typedef std::vector<CapDesc> CapDescs;
using CapDesc = std::pair<std::string, u256>;
using CapDescSet = std::set<CapDesc>;
using CapDescs = std::vector<CapDesc>;
struct PeerInfo
{

68
libsolidity/AST.cpp

@ -58,11 +58,19 @@ void ContractDefinition::checkTypeRequirements()
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
FunctionDefinition const* fallbackFunction = getFallbackFunction();
if (fallbackFunction && fallbackFunction->getScope() == this && !fallbackFunction->getParameters().empty())
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError(
"Fallback function cannot take parameters."));
FunctionDefinition const* fallbackFunction = nullptr;
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
if (function->getName().empty())
{
if (fallbackFunction)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed."));
else
{
fallbackFunction = function.get();
if (!fallbackFunction->getParameters().empty())
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
}
}
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements();
@ -239,7 +247,7 @@ void StructDefinition::checkRecursion() const
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
if (member->getType()->getCategory() == Type::Category::Struct)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
@ -271,9 +279,9 @@ string FunctionDefinition::getCanonicalSignature() const
Declaration::LValueType VariableDeclaration::getLValueType() const
{
if (dynamic_cast<FunctionDefinition const*>(getScope()) || dynamic_cast<ModifierDefinition const*>(getScope()))
return Declaration::LValueType::LOCAL;
return Declaration::LValueType::Local;
else
return Declaration::LValueType::STORAGE;
return Declaration::LValueType::Storage;
}
TypePointer ModifierDefinition::getType(ContractDefinition const*) const
@ -376,13 +384,15 @@ void VariableDefinition::checkTypeRequirements()
// no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements();
TypePointer type = m_value->getType();
if (type->getCategory() == Type::Category::INTEGER_CONSTANT)
if (type->getCategory() == Type::Category::IntegerConstant)
{
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
if (!intType)
BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString()));
type = intType;
}
else if (type->getCategory() == Type::Category::Void)
BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type"));
m_variable->setType(type);
}
}
@ -396,7 +406,7 @@ void Assignment::checkTypeRequirements()
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_type = m_leftHandSide->getType();
if (m_assigmentOperator == Token::ASSIGN)
if (m_assigmentOperator == Token::Assign)
m_rightHandSide->expectType(*m_type);
else
{
@ -415,7 +425,7 @@ void Assignment::checkTypeRequirements()
void ExpressionStatement::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
if (m_expression->getType()->getCategory() == Type::Category::INTEGER_CONSTANT)
if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant)
if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->getType())->getIntegerType())
BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant."));
}
@ -439,9 +449,9 @@ void Expression::requireLValue()
void UnaryOperation::checkTypeRequirements()
{
// INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE
// Inc, Dec, Add, Sub, Not, BitNot, Delete
m_subExpression->checkTypeRequirements();
if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE)
if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete)
m_subExpression->requireLValue();
m_type = m_subExpression->getType()->unaryOperatorResult(m_operator);
if (!m_type)
@ -476,7 +486,7 @@ void FunctionCall::checkTypeRequirements()
if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
if (!m_names.empty())
BOOST_THROW_EXCEPTION(createTypeError("Type conversion can't allow named arguments."));
BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType();
@ -487,28 +497,30 @@ void FunctionCall::checkTypeRequirements()
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
if (m_names.empty())
{
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
if (!functionType->takesArbitraryParameters() &&
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call."));
}
else
{
if (functionType->takesArbitraryParameters())
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
"that take arbitrary parameters."));
auto const& parameterNames = functionType->getParameterNames();
if (parameterNames.size() != m_names.size())
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
// check duplicate names
for (size_t i = 0; i < m_names.size(); i++) {
for (size_t j = i + 1; j < m_names.size(); j++) {
for (size_t i = 0; i < m_names.size(); i++)
for (size_t j = i + 1; j < m_names.size(); j++)
if (*m_names[i] == *m_names[j])
BOOST_THROW_EXCEPTION(createTypeError("Duplicate named argument."));
}
}
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
for (size_t i = 0; i < m_names.size(); i++) {
bool found = false;
@ -523,7 +535,7 @@ void FunctionCall::checkTypeRequirements()
}
}
if (!found)
BOOST_THROW_EXCEPTION(createTypeError("Named argument doesn't match function declaration."));
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
}
}
@ -540,7 +552,7 @@ void FunctionCall::checkTypeRequirements()
bool FunctionCall::isTypeConversion() const
{
return m_expression->getType()->getCategory() == Type::Category::TYPE;
return m_expression->getType()->getCategory() == Type::Category::TypeType;
}
void NewExpression::checkTypeRequirements()
@ -552,7 +564,7 @@ void NewExpression::checkTypeRequirements()
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
FunctionType::Location::CREATION);
FunctionType::Location::Creation);
}
void MemberAccess::checkTypeRequirements()
@ -564,19 +576,19 @@ void MemberAccess::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
"visible in " + type.toString()));
//@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? Declaration::LValueType::STORAGE : Declaration::LValueType::NONE;
m_lvalue = type.getCategory() == Type::Category::Struct ? Declaration::LValueType::Storage : Declaration::LValueType::None;
}
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
if (m_base->getType()->getCategory() != Type::Category::Mapping)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_lvalue = Declaration::LValueType::STORAGE;
m_lvalue = Declaration::LValueType::Storage;
}
void Identifier::checkTypeRequirements()

43
libsolidity/AST.h

@ -132,17 +132,17 @@ private:
class Declaration: public ASTNode
{
public:
enum class LValueType { NONE, LOCAL, STORAGE };
enum class Visibility { DEFAULT, PUBLIC, PROTECTED, PRIVATE };
enum class LValueType { None, Local, Storage };
enum class Visibility { Default, Public, Protected, Private };
Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
Visibility _visibility = Visibility::DEFAULT):
Visibility _visibility = Visibility::Default):
ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
/// @returns the declared name.
ASTString const& getName() const { return *m_name; }
Visibility getVisibility() const { return m_visibility == Visibility::DEFAULT ? getDefaultVisibility() : m_visibility; }
bool isPublic() const { return getVisibility() == Visibility::PUBLIC; }
Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; }
bool isPublic() const { return getVisibility() == Visibility::Public; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
@ -154,10 +154,10 @@ public:
/// contract types.
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
/// @returns the lvalue type of expressions referencing this declaration
virtual LValueType getLValueType() const { return LValueType::NONE; }
virtual LValueType getLValueType() const { return LValueType::None; }
protected:
virtual Visibility getDefaultVisibility() const { return Visibility::PUBLIC; }
virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
private:
ASTPointer<ASTString> m_name;
@ -250,7 +250,7 @@ public:
/// Returns the constructor or nullptr if no constructor was specified.
FunctionDefinition const* getConstructor() const;
/// Returns the fallback function or nullptr if no constructor was specified.
/// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* getFallbackFunction() const;
private:
@ -414,7 +414,7 @@ public:
bool isIndexed() const { return m_isIndexed; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::PROTECTED; }
Visibility getDefaultVisibility() const override { return Visibility::Protected; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@ -847,8 +847,8 @@ public:
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLValue() const { return m_lvalue != Declaration::LValueType::NONE; }
bool isLocalLValue() const { return m_lvalue == Declaration::LValueType::LOCAL; }
bool isLValue() const { return m_lvalue != Declaration::LValueType::None; }
bool isLocalLValue() const { return m_lvalue == Declaration::LValueType::Local; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -865,7 +865,7 @@ protected:
std::shared_ptr<Type const> m_type;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
Declaration::LValueType m_lvalue = Declaration::LValueType::NONE;
Declaration::LValueType m_lvalue = Declaration::LValueType::None;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested = false;
};
@ -1112,13 +1112,23 @@ private:
};
/**
* A literal string or number. @see Type::literalToBigEndian is used to actually parse its value.
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
*/
class Literal: public PrimaryExpression
{
public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
PrimaryExpression(_location), m_token(_token), m_value(_value) {}
enum class SubDenomination
{
None = Token::Illegal,
Wei = Token::SubWei,
Szabo = Token::SubSzabo,
Finney = Token::SubFinney,
Ether = Token::SubEther
};
Literal(Location const& _location, Token::Value _token,
ASTPointer<ASTString> const& _value,
SubDenomination _sub = SubDenomination::None):
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
@ -1127,9 +1137,12 @@ public:
/// @returns the non-parsed value of the literal
ASTString const& getValue() const { return *m_value; }
SubDenomination getSubDenomination() const { return m_subDenomination; }
private:
Token::Value m_token;
ASTPointer<ASTString> m_value;
SubDenomination m_subDenomination;
};
/// @}

2
libsolidity/ASTJsonConverter.cpp

@ -118,7 +118,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
bool isLocalVariable = (_node.getLValueType() == VariableDeclaration::LValueType::LOCAL);
bool isLocalVariable = (_node.getLValueType() == VariableDeclaration::LValueType::Local);
addJsonNode("VariableDeclaration",
{ make_pair("name", _node.getName()),
make_pair("local", boost::lexical_cast<std::string>(isLocalVariable))},

4
libsolidity/Compiler.cpp

@ -189,7 +189,7 @@ unsigned Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, b
if (c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
bool const c_leftAligned = type->getCategory() == Type::Category::STRING;
bool const c_leftAligned = type->getCategory() == Type::Category::String;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, c_numBytes, c_leftAligned,
!_fromMemory, c_padToWords);
@ -213,7 +213,7 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_leftAligned = type->getCategory() == Type::Category::STRING;
bool const c_leftAligned = type->getCategory() == Type::Category::String;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords);
stackDepth -= type->getSizeOnStack();

12
libsolidity/CompilerStack.cpp

@ -227,12 +227,12 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN
string const& CompilerStack::getInterface(string const& _contractName) const
{
return getMetadata(_contractName, DocumentationType::ABI_INTERFACE);
return getMetadata(_contractName, DocumentationType::ABIInterface);
}
string const& CompilerStack::getSolidityInterface(string const& _contractName) const
{
return getMetadata(_contractName, DocumentationType::ABI_SOLIDITY_INTERFACE);
return getMetadata(_contractName, DocumentationType::ABISolidityInterface);
}
string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const
@ -245,16 +245,16 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat
std::unique_ptr<string const>* doc;
switch (_type)
{
case DocumentationType::NATSPEC_USER:
case DocumentationType::NatspecUser:
doc = &contract.userDocumentation;
break;
case DocumentationType::NATSPEC_DEV:
case DocumentationType::NatspecDev:
doc = &contract.devDocumentation;
break;
case DocumentationType::ABI_INTERFACE:
case DocumentationType::ABIInterface:
doc = &contract.interface;
break;
case DocumentationType::ABI_SOLIDITY_INTERFACE:
case DocumentationType::ABISolidityInterface:
doc = &contract.solidityInterface;
break;
default:

8
libsolidity/CompilerStack.h

@ -43,10 +43,10 @@ class InterfaceHandler;
enum class DocumentationType: uint8_t
{
NATSPEC_USER = 1,
NATSPEC_DEV,
ABI_INTERFACE,
ABI_SOLIDITY_INTERFACE
NatspecUser = 1,
NatspecDev,
ABIInterface,
ABISolidityInterface
};
extern const std::map<std::string, std::string> StandardSources;

4
libsolidity/DeclarationContainer.cpp

@ -30,6 +30,9 @@ namespace solidity
bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _update)
{
if (_declaration.getName().empty())
return true;
if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end())
return false;
m_declarations[_declaration.getName()] = &_declaration;
@ -38,6 +41,7 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration,
Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
auto result = m_declarations.find(_name);
if (result != m_declarations.end())
return result->second;

4
libsolidity/DeclarationContainer.h

@ -42,8 +42,8 @@ public:
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared.
/// @returns true iff it was not yet declared.
/// Registers the declaration in the scope unless its name is already declared or the name is empty.
/// @returns false if the name was already declared.
bool registerDeclaration(Declaration const& _declaration, bool _update = false);
Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }

2
libsolidity/Exceptions.h

@ -38,7 +38,7 @@ struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: virtual Exception {};
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, Location>;
}
}

309
libsolidity/ExpressionCompiler.cpp

@ -64,7 +64,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN) // compound assignment
if (op != Token::Assign) // compound assignment
{
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
@ -85,7 +85,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// 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
// represents the operator
if (_unaryOperation.getType()->getCategory() == Type::Category::INTEGER_CONSTANT)
if (_unaryOperation.getType()->getCategory() == Type::Category::IntegerConstant)
{
m_context << _unaryOperation.getType()->literalValue(nullptr);
return false;
@ -95,19 +95,19 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
switch (_unaryOperation.getOperator())
{
case Token::NOT: // !
case Token::Not: // !
m_context << eth::Instruction::ISZERO;
break;
case Token::BIT_NOT: // ~
case Token::BitNot: // ~
m_context << eth::Instruction::NOT;
break;
case Token::DELETE: // delete
case Token::Delete: // delete
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation);
m_currentLValue.reset();
break;
case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix)
case Token::Inc: // ++ (pre- or postfix)
case Token::Dec: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation())
@ -118,7 +118,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << eth::Instruction::DUP1;
}
m_context << u256(1);
if (_unaryOperation.getOperator() == Token::INC)
if (_unaryOperation.getOperator() == Token::Inc)
m_context << eth::Instruction::ADD;
else
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
@ -129,10 +129,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_currentLValue.storeValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
m_currentLValue.reset();
break;
case Token::ADD: // +
case Token::Add: // +
// unary add, so basically no-op
break;
case Token::SUB: // -
case Token::Sub: // -
m_context << u256(0) << eth::Instruction::SUB;
break;
default:
@ -149,19 +149,19 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
Type const& commonType = _binaryOperation.getCommonType();
Token::Value const c_op = _binaryOperation.getOperator();
if (c_op == Token::AND || c_op == Token::OR) // special case: short-circuiting
if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
else if (commonType.getCategory() == Type::Category::INTEGER_CONSTANT)
else if (commonType.getCategory() == Type::Category::IntegerConstant)
m_context << commonType.literalValue(nullptr);
else
{
bool cleanupNeeded = commonType.getCategory() == Type::Category::INTEGER &&
(Token::isCompareOp(c_op) || c_op == Token::DIV || c_op == Token::MOD);
bool cleanupNeeded = commonType.getCategory() == Type::Category::Integer &&
(Token::isCompareOp(c_op) || c_op == Token::Div || c_op == Token::Mod);
// for commutative operators, push the literal as late as possible to allow improved optimization
auto isLiteral = [](Expression const& _e)
{
return dynamic_cast<Literal const*>(&_e) || _e.getType()->getCategory() == Type::Category::INTEGER_CONSTANT;
return dynamic_cast<Literal const*>(&_e) || _e.getType()->getCategory() == Type::Category::IntegerConstant;
};
bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
if (swap)
@ -206,7 +206,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypePointers const& parameterTypes = function.getParameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
solAssert(callArguments.size() == parameterTypes.size(), "");
if (!function.takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
@ -226,7 +227,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
switch (function.getLocation())
{
case Location::INTERNAL:
case Location::Internal:
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
@ -252,12 +253,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
break;
}
case Location::EXTERNAL:
case Location::BARE:
case Location::External:
case Location::Bare:
_functionCall.getExpression().accept(*this);
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::Bare);
break;
case Location::CREATION:
case Location::Creation:
{
_functionCall.getExpression().accept(*this);
solAssert(!function.gasSet(), "Gas limit set for contract creation.");
@ -274,7 +275,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned length = bytecode.size();
length += appendArgumentCopyToMemory(function.getParameterTypes(), arguments, length);
length += appendArgumentsCopyToMemory(arguments, function.getParameterTypes(), length);
// size, offset, endowment
m_context << u256(length) << u256(0);
if (function.valueSet())
@ -286,7 +287,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << eth::swapInstruction(1) << eth::Instruction::POP;
break;
}
case Location::SET_GAS:
case Location::SetGas:
{
// stack layout: contract_address function_id [gas] [value]
_functionCall.getExpression().accept(*this);
@ -301,7 +302,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << eth::Instruction::POP;
break;
}
case Location::SET_VALUE:
case Location::SetValue:
// stack layout: contract_address function_id [gas] [value]
_functionCall.getExpression().accept(*this);
// Note that function is not the original function, but the ".value" function.
@ -310,31 +311,33 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << eth::Instruction::POP;
arguments.front()->accept(*this);
break;
case Location::SEND:
case Location::Send:
_functionCall.getExpression().accept(*this);
m_context << u256(0); // 0 gas, we do not want to execute code
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true);
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
Location::EXTERNAL, true, true), {}, true);
Location::External, false, true, true), {}, true);
break;
case Location::SUICIDE:
case Location::Suicide:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
{
unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments());
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
break;
case Location::LOG0:
case Location::LOG1:
case Location::LOG2:
case Location::LOG3:
case Location::LOG4:
}
case Location::Log0:
case Location::Log1:
case Location::Log2:
case Location::Log3:
case Location::Log4:
{
unsigned logNumber = int(function.getLocation()) - int(Location::LOG0);
unsigned logNumber = int(function.getLocation()) - int(Location::Log0);
for (unsigned arg = logNumber; arg > 0; --arg)
{
arguments[arg]->accept(*this);
@ -346,7 +349,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
break;
}
case Location::EVENT:
case Location::Event:
{
_functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
@ -372,18 +375,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed);
break;
}
case Location::BLOCKHASH:
case Location::BlockHash:
{
arguments[0]->accept(*this);
appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
m_context << eth::Instruction::BLOCKHASH;
break;
}
case Location::ECRECOVER:
case Location::ECRecover:
case Location::SHA256:
case Location::RIPEMD160:
{
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
m_context << contractAddresses.find(function.getLocation())->second;
@ -408,7 +411,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory())
{
case Type::Category::CONTRACT:
case Type::Category::Contract:
{
bool alsoSearchInteger = false;
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
@ -420,7 +423,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
u256 identifier = type.getFunctionIdentifier(member);
if (identifier != Invalid256)
{
appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true);
appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::Address), true);
m_context << identifier;
}
else
@ -430,24 +433,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
if (!alsoSearchInteger)
break;
}
case Type::Category::INTEGER:
case Type::Category::Integer:
if (member == "balance")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
IntegerType(0, IntegerType::Modifier::Address), true);
m_context << eth::Instruction::BALANCE;
}
else if (member == "send" || member.substr(0, min<size_t>(member.size(), 4)) == "call")
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
IntegerType(0, IntegerType::Modifier::Address), true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::FUNCTION:
case Type::Category::Function:
solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
"Invalid member access to function.");
break;
case Type::Category::MAGIC:
case Type::Category::Magic:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
m_context << eth::Instruction::COINBASE;
@ -472,15 +475,15 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
break;
case Type::Category::STRUCT:
case Type::Category::Struct:
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
case Type::Category::TYPE:
case Type::Category::TypeType:
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(member))
@ -512,7 +515,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
length += CompilerUtils(m_context).storeInMemory(length);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false;
@ -523,7 +526,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT)
if (magicVar->getType()->getCategory() == Type::Category::Contract)
// "this" or "super"
if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
m_context << eth::Instruction::ADDRESS;
@ -553,9 +556,9 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{
switch (_literal.getType()->getCategory())
{
case Type::Category::INTEGER_CONSTANT:
case Type::Category::BOOL:
case Type::Category::STRING:
case Type::Category::IntegerConstant:
case Type::Category::Bool:
case Type::Category::String:
m_context << _literal.getType()->literalValue(&_literal);
break;
default:
@ -566,11 +569,11 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{
Token::Value const c_op = _binaryOperation.getOperator();
solAssert(c_op == Token::OR || c_op == Token::AND, "");
solAssert(c_op == Token::Or || c_op == Token::And, "");
_binaryOperation.getLeftExpression().accept(*this);
m_context << eth::Instruction::DUP1;
if (c_op == Token::AND)
if (c_op == Token::And)
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem endLabel = m_context.appendConditionalJump();
m_context << eth::Instruction::POP;
@ -580,10 +583,10 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
{
if (_operator == Token::EQ || _operator == Token::NE)
if (_operator == Token::Equal || _operator == Token::NotEqual)
{
m_context << eth::Instruction::EQ;
if (_operator == Token::NE)
if (_operator == Token::NotEqual)
m_context << eth::Instruction::ISZERO;
}
else
@ -593,18 +596,18 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
switch (_operator)
{
case Token::GTE:
case Token::GreaterThanOrEqual:
m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
<< eth::Instruction::ISZERO;
break;
case Token::LTE:
case Token::LessThanOrEqual:
m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
<< eth::Instruction::ISZERO;
break;
case Token::GT:
case Token::GreaterThan:
m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
case Token::LT:
case Token::LessThan:
m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
default:
@ -632,19 +635,19 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
switch (_operator)
{
case Token::ADD:
case Token::Add:
m_context << eth::Instruction::ADD;
break;
case Token::SUB:
case Token::Sub:
m_context << eth::Instruction::SUB;
break;
case Token::MUL:
case Token::Mul:
m_context << eth::Instruction::MUL;
break;
case Token::DIV:
case Token::Div:
m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
case Token::MOD:
case Token::Mod:
m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
break;
default:
@ -656,13 +659,13 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
{
switch (_operator)
{
case Token::BIT_OR:
case Token::BitOr:
m_context << eth::Instruction::OR;
break;
case Token::BIT_AND:
case Token::BitAnd:
m_context << eth::Instruction::AND;
break;
case Token::BIT_XOR:
case Token::BitXor:
m_context << eth::Instruction::XOR;
break;
default:
@ -695,29 +698,38 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
Type::Category stackTypeCategory = _typeOnStack.getCategory();
Type::Category targetTypeCategory = _targetType.getCategory();
if (stackTypeCategory == Type::Category::STRING)
if (stackTypeCategory == Type::Category::String)
{
if (targetTypeCategory == Type::Category::INTEGER)
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
if (targetTypeCategory == Type::Category::Integer)
{
// conversion from string to hash. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed.");
solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same.");
m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
}
else
{
solAssert(targetTypeCategory == Type::Category::STRING, "Invalid type conversion requested.");
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
// clear lower-order bytes for conversion to shorter strings - we always clean
solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested.");
StaticStringType const& targetType = dynamic_cast<StaticStringType const&>(_targetType);
if (targetType.getNumBytes() < typeOnStack.getNumBytes())
{
if (targetType.getNumBytes() == 0)
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
else
m_context << (u256(1) << (256 - targetType.getNumBytes() * 8))
<< eth::Instruction::DUP1 << eth::Instruction::SWAP2
<< eth::Instruction::DIV << eth::Instruction::MUL;
}
}
}
else if (stackTypeCategory == Type::Category::INTEGER || stackTypeCategory == Type::Category::CONTRACT ||
stackTypeCategory == Type::Category::INTEGER_CONSTANT)
else if (stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Contract ||
stackTypeCategory == Type::Category::IntegerConstant)
{
if (targetTypeCategory == Type::Category::STRING && stackTypeCategory == Type::Category::INTEGER)
if (targetTypeCategory == Type::Category::String && stackTypeCategory == Type::Category::Integer)
{
// conversion from hash to string. no need to clean the high bit
// only to shift left because of opposite alignment
@ -729,11 +741,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
}
else
{
solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, "");
IntegerType addressType(0, IntegerType::Modifier::ADDRESS);
IntegerType const& targetType = targetTypeCategory == Type::Category::INTEGER
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
IntegerType addressType(0, IntegerType::Modifier::Address);
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::INTEGER_CONSTANT)
if (stackTypeCategory == Type::Category::IntegerConstant)
{
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
// We know that the stack is clean, we only have to clean for a narrowing conversion
@ -743,7 +755,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
}
else
{
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::INTEGER
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
// Widening: clean up according to source type width
// Non-widening and force: clean up according to target type bits
@ -773,7 +785,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
vector<ASTPointer<Expression const>> const& _arguments,
bool bare)
{
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
solAssert(_functionType.takesArbitraryParameters() ||
_arguments.size() == _functionType.getParameterTypes().size(), "");
// Assumed stack content here:
// <stack top>
@ -797,7 +810,10 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset);
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes.
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset,
_functionType.padArguments(), bare);
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
@ -828,33 +844,44 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
if (retSize > 0)
{
bool const c_leftAligned = firstType->getCategory() == Type::Category::STRING;
bool const c_leftAligned = firstType->getCategory() == Type::Category::String;
CompilerUtils(m_context).loadFromMemory(0, retSize, c_leftAligned, false, true);
}
}
unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types,
vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset)
unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
unsigned _memoryOffset,
bool _padToWordBoundaries,
bool _padExceptionIfFourBytes)
{
solAssert(_types.empty() || _types.size() == _arguments.size(), "");
unsigned length = 0;
for (unsigned i = 0; i < _arguments.size(); ++i)
length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + length);
for (size_t i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i];
appendTypeConversion(*_arguments[i]->getType(), *expectedType, true);
bool pad = _padToWordBoundaries;
// Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
pad = false;
length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(),
_memoryOffset + length, pad);
}
return length;
}
unsigned ExpressionCompiler::appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type,
Location const& _location, unsigned _memoryOffset)
unsigned ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, bool _padToWordBoundaries)
{
appendTypeConversion(_type, _expectedType, true);
unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize());
unsigned const c_encodedSize = _type.getCalldataEncodedSize();
unsigned const c_numBytes = _padToWordBoundaries ? CompilerUtils::getPaddedSize(c_encodedSize) : c_encodedSize;
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_location)
<< errinfo_comment("Type " + _expectedType.toString() + " not yet supported."));
bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords);
<< errinfo_comment("Type " + _type.toString() + " not yet supported."));
bool const c_leftAligned = _type.getCategory() == Type::Category::String;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, _padToWordBoundaries);
}
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
@ -862,39 +889,65 @@ unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedT
unsigned _memoryOffset)
{
_expression.accept(*this);
return appendTypeConversionAndMoveToMemory(_expectedType, *_expression.getType(), _expression.getLocation(), _memoryOffset);
appendTypeConversion(*_expression.getType(), _expectedType, true);
return appendTypeMoveToMemory(_expectedType, _expression.getLocation(), _memoryOffset);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{
FunctionType thisType(_varDecl);
solAssert(thisType.getReturnParameterTypes().size() == 1, "");
TypePointer const& resultType = thisType.getReturnParameterTypes().front();
unsigned sizeOnStack;
FunctionType accessorType(_varDecl);
unsigned length = 0;
TypePointers const& params = thisType.getParameterTypes();
TypePointers const& paramTypes = accessorType.getParameterTypes();
// move arguments to memory
for (TypePointer const& param: boost::adaptors::reverse(params))
length += appendTypeConversionAndMoveToMemory(*param, *param, Location(), length);
for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
length += appendTypeMoveToMemory(*paramType, Location(), length);
// retrieve the position of the mapping
// retrieve the position of the variable
m_context << m_context.getStorageLocationOfVariable(_varDecl);
TypePointer returnType = _varDecl.getType();
for (TypePointer const& param: params)
for (TypePointer const& paramType: paramTypes)
{
// move offset to memory
CompilerUtils(m_context).storeInMemory(length);
unsigned argLen = CompilerUtils::getPaddedSize(param->getCalldataEncodedSize());
unsigned argLen = CompilerUtils::getPaddedSize(paramType->getCalldataEncodedSize());
length -= argLen;
m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3;
returnType = dynamic_cast<MappingType const&>(*returnType).getValueType();
}
m_currentLValue = LValue(m_context, LValue::STORAGE, *resultType);
m_currentLValue.retrieveValue(resultType, Location(), true);
sizeOnStack = resultType->getSizeOnStack();
solAssert(sizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP;
unsigned retSizeOnStack = 0;
solAssert(accessorType.getReturnParameterTypes().size() >= 1, "");
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
{
auto const& names = accessorType.getReturnParameterNames();
auto const& types = accessorType.getReturnParameterTypes();
// struct
for (size_t i = 0; i < names.size(); ++i)
{
m_context << eth::Instruction::DUP1
<< structType->getStorageOffsetOfMember(names[i])
<< eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *types[i]);
m_currentLValue.retrieveValue(types[i], Location(), true);
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
m_context << eth::Instruction::SWAP1;
retSizeOnStack += types[i]->getSizeOnStack();
}
m_context << eth::Instruction::POP;
}
else
{
// simple value
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *returnType);
m_currentLValue.retrieveValue(returnType, Location(), true);
retSizeOnStack = returnType->getSizeOnStack();
}
solAssert(retSizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP;
}
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
@ -903,7 +956,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType
{
//@todo change the type cast for arrays
solAssert(_dataType.getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " +_dataType.toString() + " should fit in unsigned");
if (m_type == STORAGE)
if (m_type == LValueType::Storage)
m_size = unsigned(_dataType.getStorageSize());
else
m_size = unsigned(_dataType.getSizeOnStack());
@ -913,7 +966,7 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio
{
switch (m_type)
{
case STACK:
case LValueType::Stack:
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
@ -923,10 +976,10 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio
*m_context << eth::dupInstruction(stackPos + 1);
break;
}
case STORAGE:
case LValueType::Storage:
retrieveValueFromStorage(_type, _remove);
break;
case MEMORY:
case LValueType::Memory:
if (!_type->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
@ -962,7 +1015,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{
switch (m_type)
{
case STACK:
case LValueType::Stack:
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1;
if (stackDiff > 16)
@ -975,7 +1028,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
retrieveValue(_expression.getType(), _expression.getLocation());
break;
}
case LValue::STORAGE:
case LValueType::Storage:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
// stack layout: value value ... value ref
@ -1000,7 +1053,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
break;
case LValue::MEMORY:
case LValueType::Memory:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
@ -1017,7 +1070,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
{
switch (m_type)
{
case STACK:
case LValueType::Stack:
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackDiff > 16)
@ -1029,7 +1082,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
<< eth::Instruction::POP;
break;
}
case LValue::STORAGE:
case LValueType::Storage:
if (m_size == 0)
*m_context << eth::Instruction::POP;
for (unsigned i = 0; i < m_size; ++i)
@ -1041,7 +1094,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
<< u256(1) << eth::Instruction::ADD;
}
break;
case LValue::MEMORY:
case LValueType::Memory:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
@ -1066,7 +1119,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type)
{
m_type = STORAGE;
m_type = LValueType::Storage;
solAssert(_type->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _type->toString() + " should fit in an unsigned");
*m_context << m_context->getStorageLocationOfVariable(_varDecl);
m_size = unsigned(_type->getStorageSize());
@ -1076,7 +1129,7 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
{
if (m_context->isLocalVariable(&_declaration))
{
m_type = STACK;
m_type = LValueType::Stack;
m_size = _identifier.getType()->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
}

40
libsolidity/ExpressionCompiler.h

@ -92,17 +92,19 @@ private:
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
bool bare = false);
/// Appends code that copies the given arguments to memory (with optional offset).
/// @returns the number of bytes copied to memory
unsigned appendArgumentCopyToMemory(TypePointers const& _types,
std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset = 0);
/// Appends code that copies a type to memory.
/// @returns the number of bytes copied to memory
unsigned appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type,
Location const& _location, unsigned _memoryOffset = 0);
/// Appends code that evaluates a single expression and copies it to memory (with optional offset).
/// @returns the number of bytes copied to memory
/// Appends code that evaluates the given arguments and moves the result to memory (with optional offset).
/// @returns the number of bytes moved to memory
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
unsigned _memoryOffset = 0,
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory
/// @returns the number of bytes moved to memory
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset,
bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory (with optional offset).
/// @returns the number of bytes moved to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
@ -117,7 +119,7 @@ private:
class LValue
{
public:
enum LValueType { NONE, STACK, MEMORY, STORAGE };
enum class LValueType { None, Stack, Memory, Storage };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
@ -127,15 +129,15 @@ private:
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
/// Convenience function to set type for a state variable and retrieve the reference
void fromStateVariable(Declaration const& _varDecl, TypePointer const& _type);
void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; }
void reset() { m_type = LValueType::None; m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != NONE; }
bool isInOnStack() const { return m_type == STACK; }
bool isInMemory() const { return m_type == MEMORY; }
bool isInStorage() const { return m_type == STORAGE; }
bool isValid() const { return m_type != LValueType::None; }
bool isInOnStack() const { return m_type == LValueType::Stack; }
bool isInMemory() const { return m_type == LValueType::Memory; }
bool isInStorage() const { return m_type == LValueType::Storage; }
/// @returns true if this lvalue reference type occupies a slot on the stack.
bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; }
bool storesReferenceOnStack() const { return m_type == LValueType::Storage || m_type == LValueType::Memory; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
@ -159,7 +161,7 @@ private:
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
CompilerContext* m_context;
LValueType m_type = NONE;
LValueType m_type = LValueType::None;
/// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset = 0;

26
libsolidity/GlobalContext.cpp

@ -34,29 +34,29 @@ namespace solidity
{
GlobalContext::GlobalContext():
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::BLOCK)),
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::MSG)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)),
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::SUICIDE)),
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
make_shared<MagicVariableDeclaration>("sha3",
make_shared<FunctionType>(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA3)),
make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)),
make_shared<MagicVariableDeclaration>("log0",
make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::LOG0)),
make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
make_shared<MagicVariableDeclaration>("log1",
make_shared<FunctionType>(strings{"hash", "hash"},strings{}, FunctionType::Location::LOG1)),
make_shared<FunctionType>(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)),
make_shared<MagicVariableDeclaration>("log2",
make_shared<FunctionType>(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::LOG2)),
make_shared<FunctionType>(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)),
make_shared<MagicVariableDeclaration>("log3",
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG3)),
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)),
make_shared<MagicVariableDeclaration>("log4",
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG4)),
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
make_shared<MagicVariableDeclaration>("sha256",
make_shared<FunctionType>(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA256)),
make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)),
make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRECOVER)),
make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(strings{"hash"}, strings{"hash160"}, FunctionType::Location::RIPEMD160))})
make_shared<FunctionType>(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))})
{
}

72
libsolidity/InterfaceHandler.cpp

@ -13,7 +13,7 @@ namespace solidity
InterfaceHandler::InterfaceHandler()
{
m_lastTag = DocTagType::NONE;
m_lastTag = DocTagType::None;
}
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
@ -21,13 +21,13 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
{
switch(_type)
{
case DocumentationType::NATSPEC_USER:
case DocumentationType::NatspecUser:
return getUserDocumentation(_contractDef);
case DocumentationType::NATSPEC_DEV:
case DocumentationType::NatspecDev:
return getDevDocumentation(_contractDef);
case DocumentationType::ABI_INTERFACE:
case DocumentationType::ABIInterface:
return getABIInterface(_contractDef);
case DocumentationType::ABI_SOLIDITY_INTERFACE:
case DocumentationType::ABISolidityInterface:
return getABISolidityInterface(_contractDef);
}
@ -133,7 +133,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (strPtr)
{
resetUser();
parseDocString(*strPtr, CommentOwner::FUNCTION);
parseDocString(*strPtr, CommentOwner::Function);
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
@ -158,7 +158,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
{
m_contractAuthor.clear();
m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT);
parseDocString(*contractDoc, CommentOwner::Contract);
if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor;
@ -174,7 +174,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
if (strPtr)
{
resetDev();
parseDocString(*strPtr, CommentOwner::FUNCTION);
parseDocString(*strPtr, CommentOwner::Function);
if (!m_dev.empty())
method["details"] = Json::Value(m_dev);
@ -251,7 +251,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons
auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc));
m_lastTag = DocTagType::PARAM;
m_lastTag = DocTagType::Param;
return skipLineOrEOS(nlPos, _end);
}
@ -280,28 +280,28 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases. Also somehow automate list of acceptable tags for each
// language construct since current way does not scale well.
if (m_lastTag == DocTagType::NONE || _tag != "")
if (m_lastTag == DocTagType::None || _tag != "")
{
if (_tag == "dev")
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, false);
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false);
else if (_tag == "notice")
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, false);
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false);
else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, false);
return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false);
else if (_tag == "author")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, false);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, false);
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false);
else if (_owner == CommentOwner::Function)
return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false);
else
// LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
}
else if (_tag == "title")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, false);
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
@ -322,27 +322,27 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
{
switch (m_lastTag)
{
case DocTagType::DEV:
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, true);
case DocTagType::NOTICE:
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, true);
case DocTagType::RETURN:
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, true);
case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, true);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, true);
case DocTagType::Dev:
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true);
case DocTagType::Notice:
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true);
case DocTagType::Return:
return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true);
case DocTagType::Author:
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true);
else if (_owner == CommentOwner::Function)
return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, true);
case DocTagType::Title:
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM:
case DocTagType::Param:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
@ -378,14 +378,14 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
}
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
else if (m_lastTag != DocTagType::None) // continuation of the previous tag
currPos = appendDocTag(currPos, end, _owner);
else if (currPos != end)
{
// if it begins without a tag then consider it as @notice
if (currPos == _string.begin())
{
currPos = parseDocTag(currPos, end, "notice", CommentOwner::FUNCTION);
currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function);
continue;
}
else if (nlPos == end) //end of text

18
libsolidity/InterfaceHandler.h

@ -41,19 +41,19 @@ enum class DocumentationType: uint8_t;
enum class DocTagType: uint8_t
{
NONE = 0,
DEV,
NOTICE,
PARAM,
RETURN,
AUTHOR,
TITLE
None = 0,
Dev,
Notice,
Param,
Return,
Author,
Title
};
enum class CommentOwner
{
CONTRACT,
FUNCTION
Contract,
Function
};
class InterfaceHandler

266
libsolidity/Parser.cpp

@ -69,10 +69,10 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
switch (m_scanner->getCurrentToken())
{
case Token::IMPORT:
case Token::Import:
nodes.push_back(parseImportDirective());
break;
case Token::CONTRACT:
case Token::Contract:
nodes.push_back(parseContractDefinition());
break;
default:
@ -100,12 +100,12 @@ int Parser::getEndPosition() const
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::IMPORT);
if (m_scanner->getCurrentToken() != Token::STRING_LITERAL)
expectToken(Token::Import);
if (m_scanner->getCurrentToken() != Token::StringLiteral)
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
ASTPointer<ASTString> url = getLiteralAndAdvance();
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
return nodeFactory.createNode<ImportDirective>(url);
}
@ -115,7 +115,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
ASTPointer<ASTString> docString;
if (m_scanner->getCurrentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT);
expectToken(Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs;
@ -123,40 +123,40 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
vector<ASTPointer<FunctionDefinition>> functions;
vector<ASTPointer<ModifierDefinition>> modifiers;
vector<ASTPointer<EventDefinition>> events;
if (m_scanner->getCurrentToken() == Token::IS)
if (m_scanner->getCurrentToken() == Token::Is)
do
{
m_scanner->next();
baseContracts.push_back(parseInheritanceSpecifier());
}
while (m_scanner->getCurrentToken() == Token::COMMA);
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() == Token::Comma);
expectToken(Token::LBrace);
while (true)
{
Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE)
if (currentToken == Token::RBrace)
break;
else if (currentToken == Token::FUNCTION)
else if (currentToken == Token::Function)
functions.push_back(parseFunctionDefinition(name.get()));
else if (currentToken == Token::STRUCT)
else if (currentToken == Token::Struct)
structs.push_back(parseStructDefinition());
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
else if (currentToken == Token::Identifier || currentToken == Token::Mapping ||
Token::isElementaryTypeName(currentToken))
{
VarDeclParserOptions options;
options.isStateVariable = true;
stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
}
else if (currentToken == Token::MODIFIER)
else if (currentToken == Token::Modifier)
modifiers.push_back(parseModifierDefinition());
else if (currentToken == Token::EVENT)
else if (currentToken == Token::Event)
events.push_back(parseEventDefinition());
else
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs,
stateVariables, functions, modifiers, events);
}
@ -166,12 +166,12 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name(parseIdentifier());
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() == Token::LPAREN)
if (m_scanner->getCurrentToken() == Token::LParen)
{
m_scanner->next();
arguments = parseFunctionCallListArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
}
else
nodeFactory.setEndPositionFromNode(name);
@ -180,13 +180,13 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
{
Declaration::Visibility visibility;
if (_token == Token::PUBLIC)
visibility = Declaration::Visibility::PUBLIC;
else if (_token == Token::PROTECTED)
visibility = Declaration::Visibility::PROTECTED;
else if (_token == Token::PRIVATE)
visibility = Declaration::Visibility::PRIVATE;
Declaration::Visibility visibility(Declaration::Visibility::Default);
if (_token == Token::Public)
visibility = Declaration::Visibility::Public;
else if (_token == Token::Protected)
visibility = Declaration::Visibility::Protected;
else if (_token == Token::Private)
visibility = Declaration::Visibility::Private;
else
solAssert(false, "Invalid visibility specifier.");
m_scanner->next();
@ -200,29 +200,29 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION);
expectToken(Token::Function);
ASTPointer<ASTString> name;
if (m_scanner->getCurrentToken() == Token::LPAREN)
if (m_scanner->getCurrentToken() == Token::LParen)
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::DEFAULT);
Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
while (true)
{
Token::Value token = m_scanner->getCurrentToken();
if (token == Token::CONST)
if (token == Token::Const)
{
isDeclaredConst = true;
m_scanner->next();
}
else if (token == Token::IDENTIFIER)
else if (token == Token::Identifier)
modifiers.push_back(parseModifierInvocation());
else if (Token::isVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::DEFAULT)
if (visibility != Declaration::Visibility::Default)
BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers."));
visibility = parseVisibilitySpecifier(token);
}
@ -230,7 +230,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
break;
}
ASTPointer<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS)
if (m_scanner->getCurrentToken() == Token::Returns)
{
bool const permitEmptyParameterList = false;
m_scanner->next();
@ -249,17 +249,17 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
ASTPointer<StructDefinition> Parser::parseStructDefinition()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT);
expectToken(Token::Struct);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
expectToken(Token::LBrace);
while (m_scanner->getCurrentToken() != Token::RBrace)
{
members.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
expectToken(Token::RBrace);
return nodeFactory.createNode<StructDefinition>(name, members);
}
@ -267,20 +267,31 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOp
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
bool isIndexed = false;
ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken();
if (_options.allowIndexed && token == Token::INDEXED)
Declaration::Visibility visibility(Declaration::Visibility::Default);
if (_options.isStateVariable && Token::isVisibilitySpecifier(token))
visibility = parseVisibilitySpecifier(token);
if (_options.allowIndexed && token == Token::Indexed)
{
isIndexed = true;
m_scanner->next();
}
Declaration::Visibility visibility(Declaration::Visibility::DEFAULT);
if (_options.isStateVariable && Token::isVisibilitySpecifier(token))
visibility = parseVisibilitySpecifier(token);
nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(),
visibility, _options.isStateVariable,
isIndexed);
if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
{
identifier = make_shared<ASTString>("");
solAssert(type != nullptr, "");
nodeFactory.setEndPositionFromNode(type);
}
else
identifier = expectIdentifierToken();
return nodeFactory.createNode<VariableDeclaration>(type, identifier,
visibility, _options.isStateVariable,
isIndexed);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -293,10 +304,10 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::MODIFIER);
expectToken(Token::Modifier);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LPAREN)
if (m_scanner->getCurrentToken() == Token::LParen)
parameters = parseParameterList();
else
parameters = createEmptyParameterList();
@ -312,15 +323,15 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::EVENT);
expectToken(Token::Event);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LPAREN)
if (m_scanner->getCurrentToken() == Token::LParen)
parameters = parseParameterList(true, true);
else
parameters = createEmptyParameterList();
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
}
@ -329,12 +340,12 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name(parseIdentifier());
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() == Token::LPAREN)
if (m_scanner->getCurrentToken() == Token::LParen)
{
m_scanner->next();
arguments = parseFunctionCallListArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
}
else
nodeFactory.setEndPositionFromNode(name);
@ -357,17 +368,17 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next();
}
else if (token == Token::VAR)
else if (token == Token::Var)
{
if (!_allowVar)
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
m_scanner->next();
}
else if (token == Token::MAPPING)
else if (token == Token::Mapping)
{
type = parseMapping();
}
else if (token == Token::IDENTIFIER)
else if (token == Token::Identifier)
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
@ -381,18 +392,18 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
ASTPointer<Mapping> Parser::parseMapping()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::MAPPING);
expectToken(Token::LPAREN);
expectToken(Token::Mapping);
expectToken(Token::LParen);
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken()))
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type"));
ASTPointer<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next();
expectToken(Token::ARROW);
expectToken(Token::Arrow);
bool const allowVar = false;
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
@ -402,13 +413,14 @@ ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _all
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options;
options.allowIndexed = _allowIndexed;
expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
options.allowEmptyName = true;
expectToken(Token::LParen);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
{
parameters.push_back(parseVariableDeclaration(options));
while (m_scanner->getCurrentToken() != Token::RPAREN)
while (m_scanner->getCurrentToken() != Token::RParen)
{
expectToken(Token::COMMA);
expectToken(Token::Comma);
parameters.push_back(parseVariableDeclaration(options));
}
}
@ -420,12 +432,12 @@ ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _all
ASTPointer<Block> Parser::parseBlock()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE);
expectToken(Token::LBrace);
vector<ASTPointer<Statement>> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE)
while (m_scanner->getCurrentToken() != Token::RBrace)
statements.push_back(parseStatement());
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
expectToken(Token::RBrace);
return nodeFactory.createNode<Block>(statements);
}
@ -434,28 +446,28 @@ ASTPointer<Statement> Parser::parseStatement()
ASTPointer<Statement> statement;
switch (m_scanner->getCurrentToken())
{
case Token::IF:
case Token::If:
return parseIfStatement();
case Token::WHILE:
case Token::While:
return parseWhileStatement();
case Token::FOR:
case Token::For:
return parseForStatement();
case Token::LBRACE:
case Token::LBrace:
return parseBlock();
// starting from here, all statements must be terminated by a semicolon
case Token::CONTINUE:
case Token::Continue:
statement = ASTNodeFactory(*this).createNode<Continue>();
m_scanner->next();
break;
case Token::BREAK:
case Token::Break:
statement = ASTNodeFactory(*this).createNode<Break>();
m_scanner->next();
break;
case Token::RETURN:
case Token::Return:
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression;
if (m_scanner->next() != Token::SEMICOLON)
if (m_scanner->next() != Token::Semicolon)
{
expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression);
@ -463,7 +475,7 @@ ASTPointer<Statement> Parser::parseStatement()
statement = nodeFactory.createNode<Return>(expression);
break;
}
case Token::IDENTIFIER:
case Token::Identifier:
if (m_insideModifier && m_scanner->getCurrentLiteral() == "_")
{
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>();
@ -474,20 +486,20 @@ ASTPointer<Statement> Parser::parseStatement()
default:
statement = parseVarDefOrExprStmt();
}
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
return statement;
}
ASTPointer<IfStatement> Parser::parseIfStatement()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::IF);
expectToken(Token::LPAREN);
expectToken(Token::If);
expectToken(Token::LParen);
ASTPointer<Expression> condition = parseExpression();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
ASTPointer<Statement> trueBody = parseStatement();
ASTPointer<Statement> falseBody;
if (m_scanner->getCurrentToken() == Token::ELSE)
if (m_scanner->getCurrentToken() == Token::Else)
{
m_scanner->next();
falseBody = parseStatement();
@ -501,10 +513,10 @@ ASTPointer<IfStatement> Parser::parseIfStatement()
ASTPointer<WhileStatement> Parser::parseWhileStatement()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::WHILE);
expectToken(Token::LPAREN);
expectToken(Token::While);
expectToken(Token::LParen);
ASTPointer<Expression> condition = parseExpression();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body);
return nodeFactory.createNode<WhileStatement>(condition, body);
@ -516,21 +528,21 @@ ASTPointer<ForStatement> Parser::parseForStatement()
ASTPointer<Statement> initExpression;
ASTPointer<Expression> conditionExpression;
ASTPointer<ExpressionStatement> loopExpression;
expectToken(Token::FOR);
expectToken(Token::LPAREN);
expectToken(Token::For);
expectToken(Token::LParen);
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN?
if (m_scanner->getCurrentToken() != Token::SEMICOLON)
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDefOrExprStmt();
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::SEMICOLON)
if (m_scanner->getCurrentToken() != Token::Semicolon)
conditionExpression = parseExpression();
expectToken(Token::SEMICOLON);
expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::RPAREN)
if (m_scanner->getCurrentToken() != Token::RParen)
loopExpression = parseExpressionStatement();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body);
@ -555,7 +567,7 @@ ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
options.allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN)
if (m_scanner->getCurrentToken() == Token::Assign)
{
m_scanner->next();
value = parseExpression();
@ -632,9 +644,9 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression;
if (m_scanner->getCurrentToken() == Token::NEW)
if (m_scanner->getCurrentToken() == Token::New)
{
expectToken(Token::NEW);
expectToken(Token::New);
ASTPointer<Identifier> contractName(parseIdentifier());
nodeFactory.setEndPositionFromNode(contractName);
expression = nodeFactory.createNode<NewExpression>(contractName);
@ -646,30 +658,30 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{
switch (m_scanner->getCurrentToken())
{
case Token::LBRACK:
case Token::LBrack:
{
m_scanner->next();
ASTPointer<Expression> index = parseExpression();
nodeFactory.markEndPosition();
expectToken(Token::RBRACK);
expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index);
}
break;
case Token::PERIOD:
case Token::Period:
{
m_scanner->next();
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
}
break;
case Token::LPAREN:
case Token::LParen:
{
m_scanner->next();
vector<ASTPointer<Expression>> arguments;
vector<ASTPointer<ASTString>> names;
std::tie(arguments, names) = parseFunctionCallArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
}
break;
@ -686,24 +698,34 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
ASTPointer<Expression> expression;
switch (token)
{
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
case Token::TrueLiteral:
case Token::FalseLiteral:
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::NUMBER:
case Token::STRING_LITERAL:
case Token::Number:
if (Token::isEtherSubdenomination(m_scanner->peekNextToken()))
{
ASTPointer<ASTString> literal = getLiteralAndAdvance();
nodeFactory.markEndPosition();
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->getCurrentToken());
m_scanner->next();
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
break;
}
// fall-through
case Token::StringLiteral:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::IDENTIFIER:
case Token::Identifier:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break;
case Token::LPAREN:
case Token::LParen:
{
m_scanner->next();
ASTPointer<Expression> expression = parseExpression();
expectToken(Token::RPAREN);
expectToken(Token::RParen);
return expression;
}
default:
@ -726,12 +748,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
{
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN)
if (m_scanner->getCurrentToken() != Token::RParen)
{
arguments.push_back(parseExpression());
while (m_scanner->getCurrentToken() != Token::RPAREN)
while (m_scanner->getCurrentToken() != Token::RParen)
{
expectToken(Token::COMMA);
expectToken(Token::Comma);
arguments.push_back(parseExpression());
}
}
@ -742,22 +764,22 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
{
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
Token::Value token = m_scanner->getCurrentToken();
if (token == Token::LBRACE)
if (token == Token::LBrace)
{
// call({arg1 : 1, arg2 : 2 })
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
expectToken(Token::LBrace);
while (m_scanner->getCurrentToken() != Token::RBrace)
{
ret.second.push_back(expectIdentifierToken());
expectToken(Token::COLON);
expectToken(Token::Colon);
ret.first.push_back(parseExpression());
if (m_scanner->getCurrentToken() == Token::COMMA)
expectToken(Token::COMMA);
if (m_scanner->getCurrentToken() == Token::Comma)
expectToken(Token::Comma);
else
break;
}
expectToken(Token::RBRACE);
expectToken(Token::RBrace);
}
else
ret.first = parseFunctionCallListArguments();
@ -771,11 +793,11 @@ bool Parser::peekVariableDefinition()
// (which include assignments to other expressions and pre-declared variables)
// We have a variable definition if we get a keyword that specifies a type name, or
// in the case of a user-defined type, we have two identifiers following each other.
return (m_scanner->getCurrentToken() == Token::MAPPING ||
m_scanner->getCurrentToken() == Token::VAR ||
return (m_scanner->getCurrentToken() == Token::Mapping ||
m_scanner->getCurrentToken() == Token::Var ||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
m_scanner->getCurrentToken() == Token::IDENTIFIER) &&
m_scanner->peekNextToken() == Token::IDENTIFIER));
m_scanner->getCurrentToken() == Token::Identifier) &&
m_scanner->peekNextToken() == Token::Identifier));
}
void Parser::expectToken(Token::Value _value)
@ -796,7 +818,7 @@ Token::Value Parser::expectAssignmentOperator()
ASTPointer<ASTString> Parser::expectIdentifierToken()
{
if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
if (m_scanner->getCurrentToken() != Token::Identifier)
BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
return getLiteralAndAdvance();
}

1
libsolidity/Parser.h

@ -50,6 +50,7 @@ private:
bool allowVar = false;
bool isStateVariable = false;
bool allowIndexed = false;
bool allowEmptyName = false;
};
///@{

129
libsolidity/Scanner.cpp

@ -225,7 +225,7 @@ Token::Value Scanner::skipSingleLineComment()
// separately by the lexical grammar and becomes part of the
// stream of input elements for the syntactic grammar
while (advance() && !isLineTerminator(m_char)) { };
return Token::WHITESPACE;
return Token::Whitespace;
}
Token::Value Scanner::scanSingleLineDocComment()
@ -255,7 +255,7 @@ Token::Value Scanner::scanSingleLineDocComment()
advance();
}
literal.complete();
return Token::COMMENT_LITERAL;
return Token::CommentLiteral;
}
Token::Value Scanner::skipMultiLineComment()
@ -272,11 +272,11 @@ Token::Value Scanner::skipMultiLineComment()
if (ch == '*' && m_char == '/')
{
m_char = ' ';
return Token::WHITESPACE;
return Token::Whitespace;
}
}
// Unterminated multi-line comment.
return Token::ILLEGAL;
return Token::Illegal;
}
Token::Value Scanner::scanMultiLineDocComment()
@ -319,9 +319,9 @@ Token::Value Scanner::scanMultiLineDocComment()
}
literal.complete();
if (!endFound)
return Token::ILLEGAL;
return Token::Illegal;
else
return Token::COMMENT_LITERAL;
return Token::CommentLiteral;
}
Token::Value Scanner::scanSlash()
@ -331,7 +331,7 @@ Token::Value Scanner::scanSlash()
if (m_char == '/')
{
if (!advance()) /* double slash comment directly before EOS */
return Token::WHITESPACE;
return Token::Whitespace;
else if (m_char == '/')
{
// doxygen style /// comment
@ -340,7 +340,7 @@ Token::Value Scanner::scanSlash()
comment = scanSingleLineDocComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
return Token::WHITESPACE;
return Token::Whitespace;
}
else
return skipSingleLineComment();
@ -349,7 +349,7 @@ Token::Value Scanner::scanSlash()
{
// doxygen style /** natspec comment
if (!advance()) /* slash star comment before EOS */
return Token::WHITESPACE;
return Token::Whitespace;
else if (m_char == '*')
{
advance(); //consume the last '*' at /**
@ -366,15 +366,15 @@ Token::Value Scanner::scanSlash()
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
}
return Token::WHITESPACE;
return Token::Whitespace;
}
else
return skipMultiLineComment();
}
else if (m_char == '=')
return selectToken(Token::ASSIGN_DIV);
return selectToken(Token::AssignDiv);
else
return Token::DIV;
return Token::Div;
}
void Scanner::scanToken()
@ -391,7 +391,7 @@ void Scanner::scanToken()
case '\n': // fall-through
case ' ':
case '\t':
token = selectToken(Token::WHITESPACE);
token = selectToken(Token::Whitespace);
break;
case '"':
case '\'':
@ -401,79 +401,76 @@ void Scanner::scanToken()
// < <= << <<=
advance();
if (m_char == '=')
token = selectToken(Token::LTE);
token = selectToken(Token::LessThanOrEqual);
else if (m_char == '<')
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
token = selectToken('=', Token::AssignShl, Token::SHL);
else
token = Token::LT;
token = Token::LessThan;
break;
case '>':
// > >= >> >>= >>> >>>=
advance();
if (m_char == '=')
token = selectToken(Token::GTE);
token = selectToken(Token::GreaterThanOrEqual);
else if (m_char == '>')
{
// >> >>= >>> >>>=
advance();
if (m_char == '=')
token = selectToken(Token::ASSIGN_SAR);
token = selectToken(Token::AssignSar);
else if (m_char == '>')
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
token = selectToken('=', Token::AssignShr, Token::SHR);
else
token = Token::SAR;
}
else
token = Token::GT;
token = Token::GreaterThan;
break;
case '=':
// = == =>
advance();
if (m_char == '=')
token = selectToken(Token::EQ);
token = selectToken(Token::Equal);
else if (m_char == '>')
token = selectToken(Token::ARROW);
token = selectToken(Token::Arrow);
else
token = Token::ASSIGN;
token = Token::Assign;
break;
case '!':
// ! !=
advance();
if (m_char == '=')
token = selectToken(Token::NE);
token = selectToken(Token::NotEqual);
else
token = Token::NOT;
token = Token::Not;
break;
case '+':
// + ++ +=
advance();
if (m_char == '+')
token = selectToken(Token::INC);
token = selectToken(Token::Inc);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_ADD);
token = selectToken(Token::AssignAdd);
else
token = Token::ADD;
token = Token::Add;
break;
case '-':
// - -- -=
advance();
if (m_char == '-')
{
advance();
token = Token::DEC;
}
token = selectToken(Token::Dec);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_SUB);
token = selectToken(Token::AssignSub);
else
token = Token::SUB;
token = Token::Sub;
break;
case '*':
// * *=
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
token = selectToken('=', Token::AssignMul, Token::Mul);
break;
case '%':
// % %=
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
token = selectToken('=', Token::AssignMod, Token::Mod);
break;
case '/':
// / // /* /=
@ -483,25 +480,25 @@ void Scanner::scanToken()
// & && &=
advance();
if (m_char == '&')
token = selectToken(Token::AND);
token = selectToken(Token::And);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_AND);
token = selectToken(Token::AssignBitAnd);
else
token = Token::BIT_AND;
token = Token::BitAnd;
break;
case '|':
// | || |=
advance();
if (m_char == '|')
token = selectToken(Token::OR);
token = selectToken(Token::Or);
else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_OR);
token = selectToken(Token::AssignBitOr);
else
token = Token::BIT_OR;
token = Token::BitOr;
break;
case '^':
// ^ ^=
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
token = selectToken('=', Token::AssignBitXor, Token::BitXor);
break;
case '.':
// . Number
@ -509,40 +506,40 @@ void Scanner::scanToken()
if (isDecimalDigit(m_char))
token = scanNumber('.');
else
token = Token::PERIOD;
token = Token::Period;
break;
case ':':
token = selectToken(Token::COLON);
token = selectToken(Token::Colon);
break;
case ';':
token = selectToken(Token::SEMICOLON);
token = selectToken(Token::Semicolon);
break;
case ',':
token = selectToken(Token::COMMA);
token = selectToken(Token::Comma);
break;
case '(':
token = selectToken(Token::LPAREN);
token = selectToken(Token::LParen);
break;
case ')':
token = selectToken(Token::RPAREN);
token = selectToken(Token::RParen);
break;
case '[':
token = selectToken(Token::LBRACK);
token = selectToken(Token::LBrack);
break;
case ']':
token = selectToken(Token::RBRACK);
token = selectToken(Token::RBrack);
break;
case '{':
token = selectToken(Token::LBRACE);
token = selectToken(Token::LBrace);
break;
case '}':
token = selectToken(Token::RBRACE);
token = selectToken(Token::RBrace);
break;
case '?':
token = selectToken(Token::CONDITIONAL);
token = selectToken(Token::Conditional);
break;
case '~':
token = selectToken(Token::BIT_NOT);
token = selectToken(Token::BitNot);
break;
default:
if (isIdentifierStart(m_char))
@ -550,17 +547,17 @@ void Scanner::scanToken()
else if (isDecimalDigit(m_char))
token = scanNumber();
else if (skipWhitespace())
token = Token::WHITESPACE;
token = Token::Whitespace;
else if (isSourcePastEndOfInput())
token = Token::EOS;
else
token = selectToken(Token::ILLEGAL);
token = selectToken(Token::Illegal);
break;
}
// Continue scanning for tokens as long as we're just skipping
// whitespace.
}
while (token == Token::WHITESPACE);
while (token == Token::Whitespace);
m_nextToken.location.end = getSourcePos();
m_nextToken.token = token;
}
@ -618,16 +615,16 @@ Token::Value Scanner::scanString()
if (c == '\\')
{
if (isSourcePastEndOfInput() || !scanEscape())
return Token::ILLEGAL;
return Token::Illegal;
}
else
addLiteralChar(c);
}
if (m_char != quote)
return Token::ILLEGAL;
return Token::Illegal;
literal.complete();
advance(); // consume quote
return Token::STRING_LITERAL;
return Token::StringLiteral;
}
void Scanner::scanDecimalDigits()
@ -660,7 +657,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
kind = HEX;
addLiteralCharAndAdvance();
if (!isHexDigit(m_char))
return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
while (isHexDigit(m_char))
addLiteralCharAndAdvance();
}
@ -681,13 +678,13 @@ Token::Value Scanner::scanNumber(char _charSeen)
{
solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
if (kind != DECIMAL)
return Token::ILLEGAL;
return Token::Illegal;
// scan exponent
addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance();
if (!isDecimalDigit(m_char))
return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E'
return Token::Illegal; // we must have at least one decimal digit after 'e'/'E'
scanDecimalDigits();
}
// The source character immediately following a numeric literal must
@ -695,9 +692,9 @@ Token::Value Scanner::scanNumber(char _charSeen)
// section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0).
if (isDecimalDigit(m_char) || isIdentifierStart(m_char))
return Token::ILLEGAL;
return Token::Illegal;
literal.complete();
return Token::NUMBER;
return Token::Number;
}
Token::Value Scanner::scanIdentifierOrKeyword()

3
libsolidity/Token.cpp

@ -80,7 +80,6 @@ char const Token::m_tokenType[] =
{
TOKEN_LIST(KT, KK)
};
Token::Value Token::fromIdentifierOrKeyword(const std::string& _name)
{
// The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
@ -91,7 +90,7 @@ Token::Value Token::fromIdentifierOrKeyword(const std::string& _name)
#undef KEYWORD
#undef TOKEN
auto it = keywords.find(_name);
return it == keywords.end() ? Token::IDENTIFIER : it->second;
return it == keywords.end() ? Token::Identifier : it->second;
}
#undef KT

479
libsolidity/Token.h

@ -47,11 +47,6 @@
#include <libsolidity/Utils.h>
#include <libsolidity/Exceptions.h>
#if defined(DELETE)
#undef DELETE
//#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes.
#endif
namespace dev
{
namespace solidity
@ -77,262 +72,267 @@ namespace solidity
T(EOS, "EOS", 0) \
\
/* Punctuators (ECMA-262, section 7.7, page 15). */ \
T(LPAREN, "(", 0) \
T(RPAREN, ")", 0) \
T(LBRACK, "[", 0) \
T(RBRACK, "]", 0) \
T(LBRACE, "{", 0) \
T(RBRACE, "}", 0) \
T(COLON, ":", 0) \
T(SEMICOLON, ";", 0) \
T(PERIOD, ".", 0) \
T(CONDITIONAL, "?", 3) \
T(ARROW, "=>", 0) \
T(LParen, "(", 0) \
T(RParen, ")", 0) \
T(LBrack, "[", 0) \
T(RBrack, "]", 0) \
T(LBrace, "{", 0) \
T(RBrace, "}", 0) \
T(Colon, ":", 0) \
T(Semicolon, ";", 0) \
T(Period, ".", 0) \
T(Conditional, "?", 3) \
T(Arrow, "=>", 0) \
\
/* Assignment operators. */ \
/* IsAssignmentOp() relies on this block of enum values being */ \
/* contiguous and sorted in the same order!*/ \
T(ASSIGN, "=", 2) \
T(Assign, "=", 2) \
/* The following have to be in exactly the same order as the simple binary operators*/ \
T(ASSIGN_BIT_OR, "|=", 2) \
T(ASSIGN_BIT_XOR, "^=", 2) \
T(ASSIGN_BIT_AND, "&=", 2) \
T(ASSIGN_SHL, "<<=", 2) \
T(ASSIGN_SAR, ">>=", 2) \
T(ASSIGN_SHR, ">>>=", 2) \
T(ASSIGN_ADD, "+=", 2) \
T(ASSIGN_SUB, "-=", 2) \
T(ASSIGN_MUL, "*=", 2) \
T(ASSIGN_DIV, "/=", 2) \
T(ASSIGN_MOD, "%=", 2) \
T(AssignBitOr, "|=", 2) \
T(AssignBitXor, "^=", 2) \
T(AssignBitAnd, "&=", 2) \
T(AssignShl, "<<=", 2) \
T(AssignSar, ">>=", 2) \
T(AssignShr, ">>>=", 2) \
T(AssignAdd, "+=", 2) \
T(AssignSub, "-=", 2) \
T(AssignMul, "*=", 2) \
T(AssignDiv, "/=", 2) \
T(AssignMod, "%=", 2) \
\
/* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(COMMA, ",", 1) \
T(OR, "||", 4) \
T(AND, "&&", 5) \
T(BIT_OR, "|", 8) \
T(BIT_XOR, "^", 9) \
T(BIT_AND, "&", 10) \
T(Comma, ",", 1) \
T(Or, "||", 4) \
T(And, "&&", 5) \
T(BitOr, "|", 8) \
T(BitXor, "^", 9) \
T(BitAnd, "&", 10) \
T(SHL, "<<", 11) \
T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \
T(ADD, "+", 12) \
T(SUB, "-", 12) \
T(MUL, "*", 13) \
T(DIV, "/", 13) \
T(MOD, "%", 13) \
T(Add, "+", 12) \
T(Sub, "-", 12) \
T(Mul, "*", 13) \
T(Div, "/", 13) \
T(Mod, "%", 13) \
\
/* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(EQ, "==", 6) \
T(NE, "!=", 6) \
T(LT, "<", 7) \
T(GT, ">", 7) \
T(LTE, "<=", 7) \
T(GTE, ">=", 7) \
K(IN, "in", 7) \
T(Equal, "==", 6) \
T(NotEqual, "!=", 6) \
T(LessThan, "<", 7) \
T(GreaterThan, ">", 7) \
T(LessThanOrEqual, "<=", 7) \
T(GreaterThanOrEqual, ">=", 7) \
K(In, "in", 7) \
\
/* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(NOT, "!", 0) \
T(BIT_NOT, "~", 0) \
T(INC, "++", 0) \
T(DEC, "--", 0) \
K(DELETE, "delete", 0) \
T(Not, "!", 0) \
T(BitNot, "~", 0) \
T(Inc, "++", 0) \
T(Dec, "--", 0) \
K(Delete, "delete", 0) \
\
/* Keywords */ \
K(BREAK, "break", 0) \
K(CASE, "case", 0) \
K(CONST, "constant", 0) \
K(CONTINUE, "continue", 0) \
K(CONTRACT, "contract", 0) \
K(DEFAULT, "default", 0) \
K(DO, "do", 0) \
K(ELSE, "else", 0) \
K(EVENT, "event", 0) \
K(IS, "is", 0) \
K(INDEXED, "indexed", 0) \
K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \
K(IF, "if", 0) \
K(IMPORT, "import", 0) \
K(MAPPING, "mapping", 0) \
K(MODIFIER, "modifier", 0) \
K(NEW, "new", 0) \
K(PUBLIC, "public", 0) \
K(PRIVATE, "private", 0) \
K(PROTECTED, "protected", 0) \
K(RETURN, "return", 0) \
K(RETURNS, "returns", 0) \
K(STRUCT, "struct", 0) \
K(SWITCH, "switch", 0) \
K(VAR, "var", 0) \
K(WHILE, "while", 0) \
K(Break, "break", 0) \
K(Case, "case", 0) \
K(Const, "constant", 0) \
K(Continue, "continue", 0) \
K(Contract, "contract", 0) \
K(Default, "default", 0) \
K(Do, "do", 0) \
K(Else, "else", 0) \
K(Event, "event", 0) \
K(Is, "is", 0) \
K(Indexed, "indexed", 0) \
K(For, "for", 0) \
K(Function, "function", 0) \
K(If, "if", 0) \
K(Import, "import", 0) \
K(Mapping, "mapping", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
K(Public, "public", 0) \
K(Private, "private", 0) \
K(Protected, "protected", 0) \
K(Return, "return", 0) \
K(Returns, "returns", 0) \
K(Struct, "struct", 0) \
K(Switch, "switch", 0) \
K(Var, "var", 0) \
K(While, "while", 0) \
\
\
/* Ether subdenominations */ \
K(SubWei, "wei", 0) \
K(SubSzabo, "szabo", 0) \
K(SubFinney, "finney", 0) \
K(SubEther, "ether", 0) \
/* type keywords, keep them in this order, keep int as first keyword
* the implementation in Types.cpp has to be synced to this here
* TODO more to be added */ \
K(INT, "int", 0) \
K(INT8, "int8", 0) \
K(INT16, "int16", 0) \
K(INT24, "int24", 0) \
K(INT32, "int32", 0) \
K(INT40, "int40", 0) \
K(INT48, "int48", 0) \
K(INT56, "int56", 0) \
K(INT64, "int64", 0) \
K(INT72, "int72", 0) \
K(INT80, "int80", 0) \
K(INT88, "int88", 0) \
K(INT96, "int96", 0) \
K(INT104, "int104", 0) \
K(INT112, "int112", 0) \
K(INT120, "int120", 0) \
K(INT128, "int128", 0) \
K(INT136, "int136", 0) \
K(INT144, "int144", 0) \
K(INT152, "int152", 0) \
K(INT160, "int160", 0) \
K(INT168, "int168", 0) \
K(INT176, "int178", 0) \
K(INT184, "int184", 0) \
K(INT192, "int192", 0) \
K(INT200, "int200", 0) \
K(INT208, "int208", 0) \
K(INT216, "int216", 0) \
K(INT224, "int224", 0) \
K(INT232, "int232", 0) \
K(INT240, "int240", 0) \
K(INT248, "int248", 0) \
K(INT256, "int256", 0) \
K(UINT, "uint", 0) \
K(UINT8, "uint8", 0) \
K(UINT16, "uint16", 0) \
K(UINT24, "uint24", 0) \
K(UINT32, "uint32", 0) \
K(UINT40, "uint40", 0) \
K(UINT48, "uint48", 0) \
K(UINT56, "uint56", 0) \
K(UINT64, "uint64", 0) \
K(UINT72, "uint72", 0) \
K(UINT80, "uint80", 0) \
K(UINT88, "uint88", 0) \
K(UINT96, "uint96", 0) \
K(UINT104, "uint104", 0) \
K(UINT112, "uint112", 0) \
K(UINT120, "uint120", 0) \
K(UINT128, "uint128", 0) \
K(UINT136, "uint136", 0) \
K(UINT144, "uint144", 0) \
K(UINT152, "uint152", 0) \
K(UINT160, "uint160", 0) \
K(UINT168, "uint168", 0) \
K(UINT176, "uint178", 0) \
K(UINT184, "uint184", 0) \
K(UINT192, "uint192", 0) \
K(UINT200, "uint200", 0) \
K(UINT208, "uint208", 0) \
K(UINT216, "uint216", 0) \
K(UINT224, "uint224", 0) \
K(UINT232, "uint232", 0) \
K(UINT240, "uint240", 0) \
K(UINT248, "uint248", 0) \
K(UINT256, "uint256", 0) \
K(HASH, "hash", 0) \
K(HASH8, "hash8", 0) \
K(HASH16, "hash16", 0) \
K(HASH24, "hash24", 0) \
K(HASH32, "hash32", 0) \
K(HASH40, "hash40", 0) \
K(HASH48, "hash48", 0) \
K(HASH56, "hash56", 0) \
K(HASH64, "hash64", 0) \
K(HASH72, "hash72", 0) \
K(HASH80, "hash80", 0) \
K(HASH88, "hash88", 0) \
K(HASH96, "hash96", 0) \
K(HASH104, "hash104", 0) \
K(HASH112, "hash112", 0) \
K(HASH120, "hash120", 0) \
K(HASH128, "hash128", 0) \
K(HASH136, "hash136", 0) \
K(HASH144, "hash144", 0) \
K(HASH152, "hash152", 0) \
K(HASH160, "hash160", 0) \
K(HASH168, "hash168", 0) \
K(HASH176, "hash178", 0) \
K(HASH184, "hash184", 0) \
K(HASH192, "hash192", 0) \
K(HASH200, "hash200", 0) \
K(HASH208, "hash208", 0) \
K(HASH216, "hash216", 0) \
K(HASH224, "hash224", 0) \
K(HASH232, "hash232", 0) \
K(HASH240, "hash240", 0) \
K(HASH248, "hash248", 0) \
K(HASH256, "hash256", 0) \
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \
K(STRING0, "string0", 0) \
K(STRING1, "string1", 0) \
K(STRING2, "string2", 0) \
K(STRING3, "string3", 0) \
K(STRING4, "string4", 0) \
K(STRING5, "string5", 0) \
K(STRING6, "string6", 0) \
K(STRING7, "string7", 0) \
K(STRING8, "string8", 0) \
K(STRING9, "string9", 0) \
K(STRING10, "string10", 0) \
K(STRING11, "string11", 0) \
K(STRING12, "string12", 0) \
K(STRING13, "string13", 0) \
K(STRING14, "string14", 0) \
K(STRING15, "string15", 0) \
K(STRING16, "string16", 0) \
K(STRING17, "string17", 0) \
K(STRING18, "string18", 0) \
K(STRING19, "string19", 0) \
K(STRING20, "string20", 0) \
K(STRING21, "string21", 0) \
K(STRING22, "string22", 0) \
K(STRING23, "string23", 0) \
K(STRING24, "string24", 0) \
K(STRING25, "string25", 0) \
K(STRING26, "string26", 0) \
K(STRING27, "string27", 0) \
K(STRING28, "string28", 0) \
K(STRING29, "string29", 0) \
K(STRING30, "string30", 0) \
K(STRING31, "string31", 0) \
K(STRING32, "string32", 0) \
K(TEXT, "text", 0) \
K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \
T(TYPES_END, NULL, 0) /* used as type enum end marker */ \
K(Int, "int", 0) \
K(Int8, "int8", 0) \
K(Int16, "int16", 0) \
K(Int24, "int24", 0) \
K(Int32, "int32", 0) \
K(Int40, "int40", 0) \
K(Int48, "int48", 0) \
K(Int56, "int56", 0) \
K(Int64, "int64", 0) \
K(Int72, "int72", 0) \
K(Int80, "int80", 0) \
K(Int88, "int88", 0) \
K(Int96, "int96", 0) \
K(Int104, "int104", 0) \
K(Int112, "int112", 0) \
K(Int120, "int120", 0) \
K(Int128, "int128", 0) \
K(Int136, "int136", 0) \
K(Int144, "int144", 0) \
K(Int152, "int152", 0) \
K(Int160, "int160", 0) \
K(Int168, "int168", 0) \
K(Int176, "int178", 0) \
K(Int184, "int184", 0) \
K(Int192, "int192", 0) \
K(Int200, "int200", 0) \
K(Int208, "int208", 0) \
K(Int216, "int216", 0) \
K(Int224, "int224", 0) \
K(Int232, "int232", 0) \
K(Int240, "int240", 0) \
K(Int248, "int248", 0) \
K(Int256, "int256", 0) \
K(UInt, "uint", 0) \
K(UInt8, "uint8", 0) \
K(UInt16, "uint16", 0) \
K(UInt24, "uint24", 0) \
K(UInt32, "uint32", 0) \
K(UInt40, "uint40", 0) \
K(UInt48, "uint48", 0) \
K(UInt56, "uint56", 0) \
K(UInt64, "uint64", 0) \
K(UInt72, "uint72", 0) \
K(UInt80, "uint80", 0) \
K(UInt88, "uint88", 0) \
K(UInt96, "uint96", 0) \
K(UInt104, "uint104", 0) \
K(UInt112, "uint112", 0) \
K(UInt120, "uint120", 0) \
K(UInt128, "uint128", 0) \
K(UInt136, "uint136", 0) \
K(UInt144, "uint144", 0) \
K(UInt152, "uint152", 0) \
K(UInt160, "uint160", 0) \
K(UInt168, "uint168", 0) \
K(UInt176, "uint178", 0) \
K(UInt184, "uint184", 0) \
K(UInt192, "uint192", 0) \
K(UInt200, "uint200", 0) \
K(UInt208, "uint208", 0) \
K(UInt216, "uint216", 0) \
K(UInt224, "uint224", 0) \
K(UInt232, "uint232", 0) \
K(UInt240, "uint240", 0) \
K(UInt248, "uint248", 0) \
K(UInt256, "uint256", 0) \
K(Hash, "hash", 0) \
K(Hash8, "hash8", 0) \
K(Hash16, "hash16", 0) \
K(Hash24, "hash24", 0) \
K(Hash32, "hash32", 0) \
K(Hash40, "hash40", 0) \
K(Hash48, "hash48", 0) \
K(Hash56, "hash56", 0) \
K(Hash64, "hash64", 0) \
K(Hash72, "hash72", 0) \
K(Hash80, "hash80", 0) \
K(Hash88, "hash88", 0) \
K(Hash96, "hash96", 0) \
K(Hash104, "hash104", 0) \
K(Hash112, "hash112", 0) \
K(Hash120, "hash120", 0) \
K(Hash128, "hash128", 0) \
K(Hash136, "hash136", 0) \
K(Hash144, "hash144", 0) \
K(Hash152, "hash152", 0) \
K(Hash160, "hash160", 0) \
K(Hash168, "hash168", 0) \
K(Hash176, "hash178", 0) \
K(Hash184, "hash184", 0) \
K(Hash192, "hash192", 0) \
K(Hash200, "hash200", 0) \
K(Hash208, "hash208", 0) \
K(Hash216, "hash216", 0) \
K(Hash224, "hash224", 0) \
K(Hash232, "hash232", 0) \
K(Hash240, "hash240", 0) \
K(Hash248, "hash248", 0) \
K(Hash256, "hash256", 0) \
K(Address, "address", 0) \
K(Bool, "bool", 0) \
K(StringType, "string", 0) \
K(String0, "string0", 0) \
K(String1, "string1", 0) \
K(String2, "string2", 0) \
K(String3, "string3", 0) \
K(String4, "string4", 0) \
K(String5, "string5", 0) \
K(String6, "string6", 0) \
K(String7, "string7", 0) \
K(String8, "string8", 0) \
K(String9, "string9", 0) \
K(String10, "string10", 0) \
K(String11, "string11", 0) \
K(String12, "string12", 0) \
K(String13, "string13", 0) \
K(String14, "string14", 0) \
K(String15, "string15", 0) \
K(String16, "string16", 0) \
K(String17, "string17", 0) \
K(String18, "string18", 0) \
K(String19, "string19", 0) \
K(String20, "string20", 0) \
K(String21, "string21", 0) \
K(String22, "string22", 0) \
K(String23, "string23", 0) \
K(String24, "string24", 0) \
K(String25, "string25", 0) \
K(String26, "string26", 0) \
K(String27, "string27", 0) \
K(String28, "string28", 0) \
K(String29, "string29", 0) \
K(String30, "string30", 0) \
K(String31, "string31", 0) \
K(String32, "string32", 0) \
K(Text, "text", 0) \
K(Real, "real", 0) \
K(UReal, "ureal", 0) \
T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
\
/* Literals */ \
K(NULL_LITERAL, "null", 0) \
K(TRUE_LITERAL, "true", 0) \
K(FALSE_LITERAL, "false", 0) \
T(NUMBER, NULL, 0) \
T(STRING_LITERAL, NULL, 0) \
T(COMMENT_LITERAL, NULL, 0) \
K(NullLiteral, "null", 0) \
K(TrueLiteral, "true", 0) \
K(FalseLiteral, "false", 0) \
T(Number, NULL, 0) \
T(StringLiteral, NULL, 0) \
T(CommentLiteral, NULL, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, NULL, 0) \
T(Identifier, NULL, 0) \
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
T(Illegal, "ILLEGAL", 0) \
\
/* Scanner-internal use only. */ \
T(WHITESPACE, NULL, 0)
T(Whitespace, NULL, 0)
class Token
@ -359,25 +359,26 @@ public:
}
// Predicates
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND ||
op == ADD || op == MUL || op == EQ || op == NE; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; }
static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; }
static bool isBinaryOp(Value op) { return Comma <= op && op <= Mod; }
static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd ||
op == Add || op == Mul || op == Equal || op == NotEqual; }
static bool isArithmeticOp(Value op) { return Add <= op && op <= Mod; }
static bool isCompareOp(Value op) { return Equal <= op && op <= In; }
static Value AssignmentToBinaryOp(Value op)
{
solAssert(isAssignmentOp(op) && op != ASSIGN, "");
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
solAssert(isAssignmentOp(op) && op != Assign, "");
return Token::Value(op + (BitOr - AssignBitOr));
}
static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; }
static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; }
static bool isCountOp(Value op) { return op == INC || op == DEC; }
static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; }
static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; }
static bool isCountOp(Value op) { return op == Inc || op == Dec; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return op == PUBLIC || op == PRIVATE || op == PROTECTED; }
static bool isVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Protected; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; }
// Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't

217
libsolidity/Types.cpp

@ -37,24 +37,24 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
{
solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
if (Token::INT <= _typeToken && _typeToken <= Token::HASH256)
if (Token::Int <= _typeToken && _typeToken <= Token::Hash256)
{
int offset = _typeToken - Token::INT;
int offset = _typeToken - Token::Int;
int bytes = offset % 33;
if (bytes == 0)
bytes = 32;
int modifier = offset / 33;
return make_shared<IntegerType>(bytes * 8,
modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH);
modifier == 0 ? IntegerType::Modifier::Signed :
modifier == 1 ? IntegerType::Modifier::Unsigned :
IntegerType::Modifier::Hash);
}
else if (_typeToken == Token::ADDRESS)
return make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
else if (_typeToken == Token::BOOL)
else if (_typeToken == Token::Address)
return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
else if (_typeToken == Token::Bool)
return make_shared<BoolType>();
else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::STRING0));
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
@ -87,12 +87,12 @@ shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
{
switch (_literal.getToken())
{
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
case Token::TrueLiteral:
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::NUMBER:
return IntegerConstantType::fromLiteral(_literal.getValue());
case Token::STRING_LITERAL:
case Token::Number:
return make_shared<IntegerConstantType>(_literal);
case Token::StringLiteral:
//@todo put larger strings into dynamic strings
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
default:
@ -140,31 +140,31 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() == Category::STRING)
if (_convertTo.getCategory() == Category::String)
{
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
return isHash() && (m_bits == convertTo.getNumBytes() * 8);
}
return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT;
return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract;
}
TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
{
// "delete" is ok for all integer types
if (_operator == Token::DELETE)
if (_operator == Token::Delete)
return make_shared<VoidType>();
// no further unary operators for addresses
else if (isAddress())
return TypePointer();
// "~" is ok for all other types
else if (_operator == Token::BIT_NOT)
else if (_operator == Token::BitNot)
return shared_from_this();
// nothing else for hashes
else if (isHash())
return TypePointer();
// for non-hash integers, we allow +, -, ++ and --
else if (_operator == Token::ADD || _operator == Token::SUB ||
_operator == Token::INC || _operator == Token::DEC)
else if (_operator == Token::Add || _operator == Token::Sub ||
_operator == Token::Inc || _operator == Token::Dec)
return shared_from_this();
else
return TypePointer();
@ -188,7 +188,7 @@ string IntegerType::toString() const
TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (_other->getCategory() != Category::INTEGER_CONSTANT && _other->getCategory() != getCategory())
if (_other->getCategory() != Category::IntegerConstant && _other->getCategory() != getCategory())
return TypePointer();
auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other));
@ -210,15 +210,28 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
const MemberList IntegerType::AddressMemberList =
MemberList({{"balance", make_shared<IntegerType >(256)},
{"callstring32", make_shared<FunctionType>(strings{"string32"}, strings{},
FunctionType::Location::BARE)},
{"callstring32string32", make_shared<FunctionType>(strings{"string32", "string32"},
strings{}, FunctionType::Location::BARE)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::SEND)}});
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}});
shared_ptr<IntegerConstantType const> IntegerConstantType::fromLiteral(string const& _literal)
IntegerConstantType::IntegerConstantType(Literal const& _literal)
{
return make_shared<IntegerConstantType>(bigint(_literal));
m_value = bigint(_literal.getValue());
switch (_literal.getSubDenomination())
{
case Literal::SubDenomination::Wei:
case Literal::SubDenomination::None:
break;
case Literal::SubDenomination::Szabo:
m_value *= bigint("1000000000000");
break;
case Literal::SubDenomination::Finney:
m_value *= bigint("1000000000000000");
break;
case Literal::SubDenomination::Ether:
m_value *= bigint("1000000000000000000");
break;
}
}
bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@ -238,13 +251,13 @@ TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) con
bigint value;
switch (_operator)
{
case Token::BIT_NOT:
case Token::BitNot:
value = ~m_value;
break;
case Token::ADD:
case Token::Add:
value = m_value;
break;
case Token::SUB:
case Token::Sub:
value = -m_value;
break;
default:
@ -255,7 +268,7 @@ TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) con
TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (_other->getCategory() == Category::INTEGER)
if (_other->getCategory() == Category::Integer)
{
shared_ptr<IntegerType const> integerType = getIntegerType();
if (!integerType)
@ -279,30 +292,30 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty
bigint value;
switch (_operator)
{
case Token::BIT_OR:
case Token::BitOr:
value = m_value | other.m_value;
break;
case Token::BIT_XOR:
case Token::BitXor:
value = m_value ^ other.m_value;
break;
case Token::BIT_AND:
case Token::BitAnd:
value = m_value & other.m_value;
break;
case Token::ADD:
case Token::Add:
value = m_value + other.m_value;
break;
case Token::SUB:
case Token::Sub:
value = m_value - other.m_value;
break;
case Token::MUL:
case Token::Mul:
value = m_value * other.m_value;
break;
case Token::DIV:
case Token::Div:
if (other.m_value == 0)
return TypePointer();
value = m_value / other.m_value;
break;
case Token::MOD:
case Token::Mod:
if (other.m_value == 0)
return TypePointer();
value = m_value % other.m_value;
@ -328,13 +341,24 @@ string IntegerConstantType::toString() const
u256 IntegerConstantType::literalValue(Literal const*) const
{
u256 value;
// we ignore the literal and hope that the type was correctly determined
solAssert(m_value <= u256(-1), "Integer constant too large.");
solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small.");
if (m_value >= 0)
return u256(m_value);
value = u256(m_value);
else
return s2u(s256(m_value));
value = s2u(s256(m_value));
return value;
}
TypePointer IntegerConstantType::getRealType() const
{
auto intType = getIntegerType();
solAssert(!!intType, "getRealType called with invalid integer constant " + toString());
return intType;
}
shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
@ -347,8 +371,8 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
return shared_ptr<IntegerType const>();
else
return make_shared<IntegerType>(max(bytesRequired(value), 1u) * 8,
negative ? IntegerType::Modifier::SIGNED
: IntegerType::Modifier::UNSIGNED);
negative ? IntegerType::Modifier::Signed
: IntegerType::Modifier::Unsigned);
}
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
@ -374,13 +398,16 @@ bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() == Category::INTEGER)
if (_convertTo.getCategory() == getCategory())
return true;
if (_convertTo.getCategory() == Category::Integer)
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits()))
return true;
}
return isImplicitlyConvertibleTo(_convertTo);
return false;
}
bool StaticStringType::operator==(Type const& _other) const
@ -416,9 +443,9 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
u256 BoolType::literalValue(Literal const* _literal) const
{
solAssert(_literal, "");
if (_literal->getToken() == Token::TRUE_LITERAL)
if (_literal->getToken() == Token::TrueLiteral)
return u256(1);
else if (_literal->getToken() == Token::FALSE_LITERAL)
else if (_literal->getToken() == Token::FalseLiteral)
return u256(0);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
@ -426,16 +453,16 @@ u256 BoolType::literalValue(Literal const* _literal) const
TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::DELETE)
if (_operator == Token::Delete)
return make_shared<VoidType>();
return (_operator == Token::NOT) ? shared_from_this() : TypePointer();
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
}
TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (getCategory() != _other->getCategory())
return TypePointer();
if (Token::isCompareOp(_operator) || _operator == Token::AND || _operator == Token::OR)
if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or)
return _other;
else
return TypePointer();
@ -445,9 +472,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (*this == _convertTo)
return true;
if (_convertTo.getCategory() == Category::INTEGER)
if (_convertTo.getCategory() == Category::Integer)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
if (_convertTo.getCategory() == Category::CONTRACT)
if (_convertTo.getCategory() == Category::Contract)
{
auto const& bases = getContractDefinition().getLinearizedBaseContracts();
if (m_super && bases.size() <= 1)
@ -460,13 +487,13 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER ||
_convertTo.getCategory() == Category::CONTRACT;
return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::Integer ||
_convertTo.getCategory() == Category::Contract;
}
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
}
bool ContractType::operator==(Type const& _other) const
@ -530,7 +557,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
}
bool StructType::operator==(Type const& _other) const
@ -589,7 +616,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL),
m_location(_isInternal ? Location::Internal : Location::External),
m_isConstant(_function.isDeclaredConst()),
m_declaration(&_function)
{
@ -619,26 +646,35 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
}
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl)
{
TypePointers params;
vector<string> paramNames;
TypePointers retParams;
vector<string> retParamNames;
TypePointer varDeclType = _varDecl.getType();
auto mappingType = dynamic_cast<MappingType const*>(varDeclType.get());
auto returnType = varDeclType;
auto returnType = _varDecl.getType();
while (mappingType != nullptr)
while (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{
params.push_back(mappingType->getKeyType());
paramNames.push_back("");
returnType = mappingType->getValueType();
mappingType = dynamic_cast<MappingType const*>(mappingType->getValueType().get());
}
retParams.push_back(returnType);
retParamNames.push_back("");
TypePointers retParams;
vector<string> retParamNames;
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
{
for (pair<string, TypePointer> const& member: structType->getMembers())
if (member.second->canLiveOutsideStorage())
{
retParamNames.push_back(member.first);
retParams.push_back(member.second);
}
}
else
{
retParams.push_back(returnType);
retParamNames.push_back("");
}
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
@ -647,7 +683,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
}
FunctionType::FunctionType(const EventDefinition& _event):
m_location(Location::EVENT), m_declaration(&_event)
m_location(Location::Event), m_declaration(&_event)
{
TypePointers params;
vector<string> paramNames;
@ -704,9 +740,9 @@ string FunctionType::toString() const
unsigned FunctionType::getSizeOnStack() const
{
unsigned size = 0;
if (m_location == Location::EXTERNAL)
if (m_location == Location::External)
size = 2;
else if (m_location == Location::INTERNAL || m_location == Location::BARE)
else if (m_location == Location::Internal || m_location == Location::Bare)
size = 1;
if (m_gasSet)
size++;
@ -719,22 +755,22 @@ MemberList const& FunctionType::getMembers() const
{
switch (m_location)
{
case Location::EXTERNAL:
case Location::CREATION:
case Location::ECRECOVER:
case Location::External:
case Location::Creation:
case Location::ECRecover:
case Location::SHA256:
case Location::RIPEMD160:
case Location::BARE:
case Location::Bare:
if (!m_members)
{
map<string, TypePointer> members{
{"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
TypePointers{copyAndSetGasOrValue(true, false)},
Location::SET_GAS, m_gasSet, m_valueSet)},
Location::SetGas, false, m_gasSet, m_valueSet)},
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
TypePointers{copyAndSetGasOrValue(false, true)},
Location::SET_VALUE, m_gasSet, m_valueSet)}};
if (m_location == Location::CREATION)
Location::SetValue, false, m_gasSet, m_valueSet)}};
if (m_location == Location::Creation)
members.erase("gas");
m_members.reset(new MemberList(members));
}
@ -772,6 +808,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
{
return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
m_arbitraryParameters,
m_gasSet || _setGas, m_valueSet || _setValue);
}
@ -829,7 +866,7 @@ MemberList const& TypeType::getMembers() const
if (!m_members)
{
map<string, TypePointer> members;
if (m_actualType->getCategory() == Category::CONTRACT && m_currentContract != nullptr)
if (m_actualType->getCategory() == Category::Contract && m_currentContract != nullptr)
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).getContractDefinition();
vector<ContractDefinition const*> currentBases = m_currentContract->getLinearizedBaseContracts();
@ -883,21 +920,21 @@ MagicType::MagicType(MagicType::Kind _kind):
{
switch (m_kind)
{
case Kind::BLOCK:
m_members = MemberList({{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)},
case Kind::Block:
m_members = MemberList({{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"timestamp", make_shared<IntegerType>(256)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"hash"}, FunctionType::Location::BLOCKHASH)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)},
{"difficulty", make_shared<IntegerType>(256)},
{"number", make_shared<IntegerType>(256)},
{"gaslimit", make_shared<IntegerType>(256)}});
break;
case Kind::MSG:
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)},
case Kind::Message:
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}});
break;
case Kind::TX:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)},
case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gasprice", make_shared<IntegerType>(256)}});
break;
default:
@ -917,11 +954,11 @@ string MagicType::toString() const
{
switch (m_kind)
{
case Kind::BLOCK:
case Kind::Block:
return "block";
case Kind::MSG:
case Kind::Message:
return "msg";
case Kind::TX:
case Kind::Transaction:
return "tx";
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));

78
libsolidity/Types.h

@ -76,7 +76,9 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public:
enum class Category
{
INTEGER, INTEGER_CONSTANT, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MODIFIER, MAGIC
Integer, IntegerConstant, Bool, Real,
String, Contract, Struct, Function,
Mapping, Void, TypeType, Modifier, Magic
};
///@{
@ -130,6 +132,8 @@ public:
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; }
/// @returns the real type of some types, like e.g: IntegerConstant
virtual TypePointer getRealType() const { return shared_from_this(); }
/// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
@ -140,7 +144,7 @@ public:
virtual u256 literalValue(Literal const*) const
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
"for type without literals."));
"for type without literals."));
}
protected:
@ -156,11 +160,11 @@ class IntegerType: public Type
public:
enum class Modifier
{
UNSIGNED, SIGNED, HASH, ADDRESS
Unsigned, Signed, Hash, Address
};
virtual Category getCategory() const override { return Category::INTEGER; }
virtual Category getCategory() const override { return Category::Integer; }
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
@ -177,9 +181,9 @@ public:
virtual std::string toString() const override;
int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; }
bool isAddress() const { return m_modifier == Modifier::ADDRESS; }
bool isSigned() const { return m_modifier == Modifier::SIGNED; }
bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; }
bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
static const MemberList AddressMemberList;
@ -195,10 +199,9 @@ private:
class IntegerConstantType: public Type
{
public:
virtual Category getCategory() const override { return Category::INTEGER_CONSTANT; }
static std::shared_ptr<IntegerConstantType const> fromLiteral(std::string const& _literal);
virtual Category getCategory() const override { return Category::IntegerConstant; }
explicit IntegerConstantType(Literal const& _literal);
explicit IntegerConstantType(bigint _value): m_value(_value) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
@ -214,6 +217,7 @@ public:
virtual std::string toString() const override;
virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer getRealType() const override;
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> getIntegerType() const;
@ -228,7 +232,7 @@ private:
class StaticStringType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRING; }
virtual Category getCategory() const override { return Category::String; }
/// @returns the smallest string type for the given literal or an empty pointer
/// if no type fits.
@ -259,7 +263,7 @@ class BoolType: public Type
{
public:
BoolType() {}
virtual Category getCategory() const { return Category::BOOL; }
virtual Category getCategory() const { return Category::Bool; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
@ -277,7 +281,7 @@ public:
class ContractType: public Type
{
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
virtual Category getCategory() const override { return Category::Contract; }
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
m_contract(_contract), m_super(_super) {}
/// Contracts can be implicitly converted to super classes and to addresses.
@ -319,7 +323,7 @@ private:
class StructType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRUCT; }
virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
@ -351,26 +355,27 @@ public:
/// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack
/// @todo This documentation is outdated, and Location should rather be named "Type"
enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
SHA3, SUICIDE,
ECRECOVER, SHA256, RIPEMD160,
LOG0, LOG1, LOG2, LOG3, LOG4, EVENT,
SET_GAS, SET_VALUE, BLOCKHASH,
BARE };
virtual Category getCategory() const override { return Category::FUNCTION; }
enum class Location { Internal, External, Creation, Send,
SHA3, Suicide,
ECRecover, SHA256, RIPEMD160,
Log0, Log1, Log2, Log3, Log4, Event,
SetGas, SetValue, BlockHash,
Bare };
virtual Category getCategory() const override { return Category::Function; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
explicit FunctionType(VariableDeclaration const& _varDecl);
explicit FunctionType(EventDefinition const& _event);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::INTERNAL):
Location _location = Location::Internal, bool _arbitraryParameters = false):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
_location) {}
_location, _arbitraryParameters) {}
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL,
bool _gasSet = false, bool _valueSet = false):
Location _location = Location::Internal,
bool _arbitraryParameters = false, bool _gasSet = false, bool _valueSet = false):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
m_location(_location),
m_arbitraryParameters(_arbitraryParameters), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
@ -403,6 +408,9 @@ public:
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> getDocumentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); }
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
@ -418,6 +426,8 @@ private:
std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames;
Location const m_location;
/// true iff the function takes an arbitrary number of arguments of arbitrary types
bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool m_isConstant;
@ -431,7 +441,7 @@ private:
class MappingType: public Type
{
public:
virtual Category getCategory() const override { return Category::MAPPING; }
virtual Category getCategory() const override { return Category::Mapping; }
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
m_keyType(_keyType), m_valueType(_valueType) {}
@ -454,7 +464,7 @@ private:
class VoidType: public Type
{
public:
virtual Category getCategory() const override { return Category::VOID; }
virtual Category getCategory() const override { return Category::Void; }
VoidType() {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
@ -472,7 +482,7 @@ public:
class TypeType: public Type
{
public:
virtual Category getCategory() const override { return Category::TYPE; }
virtual Category getCategory() const override { return Category::TypeType; }
explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; }
@ -501,7 +511,7 @@ private:
class ModifierType: public Type
{
public:
virtual Category getCategory() const override { return Category::MODIFIER; }
virtual Category getCategory() const override { return Category::Modifier; }
explicit ModifierType(ModifierDefinition const& _modifier);
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
@ -524,8 +534,8 @@ private:
class MagicType: public Type
{
public:
enum class Kind { BLOCK, MSG, TX };
virtual Category getCategory() const override { return Category::MAGIC; }
enum class Kind { Block, Message, Transaction };
virtual Category getCategory() const override { return Category::Magic; }
explicit MagicType(Kind _kind);

0
libsolidity/Utils.h

11
libweb3jsonrpc/CMakeLists.txt

@ -32,6 +32,17 @@ target_link_libraries(${EXECUTABLE} secp256k1)
target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} serpent)
if (ETH_JSON_RPC_STUB)
add_custom_target(jsonrpcstub)
add_custom_command(TARGET jsonrpcstub
POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${ETH_JSON_RPC_STUB} spec.json --cpp-server=AbstractWebThreeStubServer --cpp-client=WebThreeStubClient
COMMAND cmake -E rename webthreestubclient.h ../test/webthreestubclient.h
)
add_dependencies(${EXECUTABLE} jsonrpcstub)
endif()
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

12
libweb3jsonrpc/WebThreeStubServer.h

@ -44,13 +44,13 @@ public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
private:
dev::eth::Interface* client() override;
std::shared_ptr<dev::shh::Interface> face() override;
dev::WebThreeNetworkFace* network() override;
dev::WebThreeStubDatabaseFace* db() override;
virtual dev::eth::Interface* client() override;
virtual std::shared_ptr<dev::shh::Interface> face() override;
virtual dev::WebThreeNetworkFace* network() override;
virtual dev::WebThreeStubDatabaseFace* db() override;
std::string get(std::string const& _name, std::string const& _key) override;
void put(std::string const& _name, std::string const& _key, std::string const& _value) override;
virtual std::string get(std::string const& _name, std::string const& _key) override;
virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override;
private:
dev::WebThreeDirect& m_web3;

20
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -31,7 +31,9 @@
#include <libethcore/CommonJS.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
#ifndef _MSC_VER
#include <libserpent/funcs.h>
#endif
#include "WebThreeStubServerBase.h"
using namespace std;
@ -426,6 +428,20 @@ int WebThreeStubServerBase::eth_newFilterString(std::string const& _filter)
return ret;
}
Json::Value WebThreeStubServerBase::eth_getWork()
{
Json::Value ret(Json::arrayValue);
auto r = client()->getWork();
ret.append(toJS(r.first));
ret.append(toJS(r.second));
return ret;
}
bool WebThreeStubServerBase::eth_submitWork(std::string const& _nonce)
{
return client()->submitNonce(jsToFixed<32>(_nonce));
}
std::string WebThreeStubServerBase::shh_newGroup(std::string const& _id, std::string const& _who)
{
(void)_id;
@ -446,7 +462,9 @@ Json::Value WebThreeStubServerBase::eth_compilers()
Json::Value ret(Json::arrayValue);
ret.append("lll");
ret.append("solidity");
#ifndef _MSC_VER
ret.append("serpent");
#endif
return ret;
}
@ -462,6 +480,7 @@ std::string WebThreeStubServerBase::eth_lll(std::string const& _code)
std::string WebThreeStubServerBase::eth_serpent(std::string const& _code)
{
string res;
#ifndef _MSC_VER
try
{
res = toJS(dev::asBytes(::compile(_code)));
@ -474,6 +493,7 @@ std::string WebThreeStubServerBase::eth_serpent(std::string const& _code)
{
cwarn << "Uncought serpent compilation exception";
}
#endif
return res;
}

3
libweb3jsonrpc/WebThreeStubServerBase.h

@ -103,6 +103,9 @@ public:
virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i);
virtual bool eth_uninstallFilter(int const& _id);
virtual Json::Value eth_getWork();
virtual bool eth_submitWork(std::string const& _nonce);
virtual std::string db_get(std::string const& _name, std::string const& _key);
virtual std::string db_getString(std::string const& _name, std::string const& _key);
virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value);

12
libweb3jsonrpc/abstractwebthreestubserver.h

@ -49,6 +49,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(new jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_filterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_filterLogsI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_logs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_logsI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI);
this->bindAndAddMethod(new jsonrpc::Procedure("db_putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putStringI);
@ -211,6 +213,14 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{
response = this->eth_logs(request[0u]);
}
inline virtual void eth_getWorkI(const Json::Value &request, Json::Value &response)
{
response = this->eth_getWork();
}
inline virtual void eth_submitWorkI(const Json::Value &request, Json::Value &response)
{
response = this->eth_submitWork(request[0u].asString());
}
inline virtual void db_putI(const Json::Value &request, Json::Value &response)
{
response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString());
@ -296,6 +306,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual Json::Value eth_changed(const int& param1) = 0;
virtual Json::Value eth_filterLogs(const int& param1) = 0;
virtual Json::Value eth_logs(const Json::Value& param1) = 0;
virtual Json::Value eth_getWork() = 0;
virtual bool eth_submitWork(const std::string& param1) = 0;
virtual bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual std::string db_get(const std::string& param1, const std::string& param2) = 0;
virtual bool db_putString(const std::string& param1, const std::string& param2, const std::string& param3) = 0;

4
libweb3jsonrpc/spec.json

@ -36,7 +36,6 @@
{ "name": "eth_solidity", "params": [""], "order": [], "returns": ""},
{ "name": "eth_serpent", "params": [""], "order": [], "returns": ""},
{ "name": "eth_newFilter", "params": [{}], "order": [], "returns": 0},
{ "name": "eth_newFilterString", "params": [""], "order": [], "returns": 0},
{ "name": "eth_uninstallFilter", "params": [0], "order": [], "returns": true},
@ -44,6 +43,9 @@
{ "name": "eth_filterLogs", "params": [0], "order": [], "returns": []},
{ "name": "eth_logs", "params": [{}], "order": [], "returns": []},
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": [""], "order": [], "returns": true},
{ "name": "db_put", "params": ["", "", ""], "order": [], "returns": true},
{ "name": "db_get", "params": ["", ""], "order": [], "returns": ""},
{ "name": "db_putString", "params": ["", "", ""], "order": [], "returns": true},

2
mix/AppContext.cpp

@ -84,7 +84,7 @@ void AppContext::load()
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow *>(m_applicationEngine->rootObjects().at(0));
QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
appLoaded();
}

45
mix/AssemblyDebuggerControl.cpp

@ -1,45 +0,0 @@
/*
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 AssemblyDebuggerControl.cpp
* @author Yann yann@ethdev.com
* @date 2014
* display opcode debugging.
*/
#include <QDebug>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include "ClientModel.h"
#include "AssemblyDebuggerControl.h"
using namespace dev::mix;
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::RightView)
{
}
QString AssemblyDebuggerControl::contentUrl() const
{
return QStringLiteral("qrc:/qml/Debugger.qml");
}
QString AssemblyDebuggerControl::title() const
{
return QApplication::tr("Debugger");
}
void AssemblyDebuggerControl::start() const
{
}

48
mix/AssemblyDebuggerControl.h

@ -1,48 +0,0 @@
/*
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 AssemblyDebuggerControl.h
* @author Yann yann@ethdev.com
* @date 2014
* Extension which display debugging steps in assembly code.
*/
#pragma once
#include <atomic>
#include "Extension.h"
namespace dev
{
namespace mix
{
class AppContext;
/**
* @brief Extension which display transaction creation or transaction call debugging.
*/
class AssemblyDebuggerControl: public Extension
{
Q_OBJECT
public:
AssemblyDebuggerControl(AppContext* _context);
~AssemblyDebuggerControl() {}
void start() const override;
QString title() const override;
QString contentUrl() const override;
};
}
}

2
mix/CMakeLists.txt

@ -46,7 +46,9 @@ target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} secp256k1)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)
endif()
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore)

44
mix/ClientModel.cpp

@ -82,7 +82,7 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry");
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry");
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
@ -113,24 +113,27 @@ QString ClientModel::apiCall(QString const& _message)
void ClientModel::mine()
{
if (m_running)
if (m_running || m_mining)
BOOST_THROW_EXCEPTION(ExecutionStateException());
m_running = true;
emit runStarted();
emit runStateChanged();
m_mining = true;
emit miningStarted();
emit miningStateChanged();
QtConcurrent::run([=]()
{
try
{
m_client->mine();
newBlock();
m_mining = false;
emit miningComplete();
}
catch (...)
{
m_mining = false;
std::cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
m_running = false;
emit miningStateChanged();
});
}
@ -233,8 +236,8 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
}
if (!f)
BOOST_THROW_EXCEPTION(FunctionNotFoundException() << FunctionName(transaction.functionId.toStdString()));
encoder.encode(f);
if (!transaction.functionId.isEmpty())
encoder.encode(f);
for (int p = 0; p < transaction.parameterValues.size(); p++)
{
if (f->parametersList().at(p)->type() != transaction.parameterValues.at(p)->declaration()->type())
@ -244,6 +247,8 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
if (transaction.functionId.isEmpty())
{
bytes param = encoder.encodedData();
contractCode.insert(contractCode.end(), param.begin(), param.end());
Address newAddress = deployContract(contractCode, transaction);
if (newAddress != m_contractAddress)
{
@ -308,10 +313,10 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
}
void ClientModel::debugTransaction(unsigned _block, unsigned _index)
void ClientModel::debugRecord(unsigned _index)
{
auto const& t = m_client->execution(_block, _index);
showDebuggerForTransaction(t);
ExecutionResult const& e = m_client->executions().at(_index);
showDebuggerForTransaction(e);
}
void ClientModel::showDebugError(QString const& _error)
@ -341,9 +346,10 @@ void ClientModel::onStateReset()
void ClientModel::onNewTransaction()
{
unsigned block = m_client->number() + 1;
unsigned index = m_client->pendingExecutions().size() - 1;
ExecutionResult const& tr = m_client->lastExecution();
unsigned block = m_client->number() + 1;
unsigned recordIndex = m_client->executions().size() - 1;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value));
QString contract = address;
@ -354,7 +360,7 @@ void ClientModel::onNewTransaction()
//TODO: handle value transfer
FixedHash<4> functionHash;
bool call = false;
bool abi = false;
if (creation)
{
//contract creation
@ -369,12 +375,12 @@ void ClientModel::onNewTransaction()
}
else
{
//call
//transaction/call
if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4)
{
functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash));
call = true;
abi = true;
}
else
function = QObject::tr("<none>");
@ -388,7 +394,7 @@ void ClientModel::onNewTransaction()
auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract();
contract = def->name();
if (call)
if (abi)
{
QFunctionDefinition* funcDef = def->getFunction(functionHash);
if (funcDef)
@ -402,9 +408,9 @@ void ClientModel::onNewTransaction()
}
}
TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned);
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall());
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newTransaction(log);
emit newRecord(log);
}
}

42
mix/ClientModel.h

@ -26,6 +26,7 @@
#include <atomic>
#include <map>
#include <QString>
#include <QVariantMap>
#include "MachineStates.h"
namespace dev
@ -68,13 +69,13 @@ struct TransactionSettings
/// UI Transaction log record
class TransactionLogEntry: public QObject
class RecordLogEntry: public QObject
{
Q_OBJECT
/// Transaction block number
Q_PROPERTY(unsigned block MEMBER m_block CONSTANT)
/// Transaction index within the block
Q_PROPERTY(unsigned tindex MEMBER m_index CONSTANT)
/// Recording index
Q_PROPERTY(unsigned recordIndex MEMBER m_recordIndex CONSTANT)
/// Human readable transaction bloack and transaction index
Q_PROPERTY(QString transactionIndex MEMBER m_transactionIndex CONSTANT)
/// Contract name if any
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT)
/// Function name if any
@ -85,21 +86,25 @@ class TransactionLogEntry: public QObject
Q_PROPERTY(QString address MEMBER m_address CONSTANT)
/// Returned value or transaction address in case of creation
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT)
/// true if call, false if transaction
Q_PROPERTY(bool call MEMBER m_call CONSTANT)
public:
TransactionLogEntry():
m_block(0), m_index(0) {}
TransactionLogEntry(int _block, int _index, QString _contract, QString _function, QString _value, QString _address, QString _returned):
m_block(_block), m_index(_index), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned) {}
RecordLogEntry():
m_recordIndex(0), m_call(false) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call) {}
private:
unsigned m_block;
unsigned m_index;
unsigned m_recordIndex;
QString m_transactionIndex;
QString m_contract;
QString m_function;
QString m_value;
QString m_address;
QString m_returned;
bool m_call;
};
/**
@ -114,6 +119,8 @@ public:
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
/// @returns true if currently mining
Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
/// ethereum.js RPC request entry point
@ -130,8 +137,8 @@ public slots:
/// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void setupState(QVariantMap _state);
/// Show the debugger for a specified transaction
Q_INVOKABLE void debugTransaction(unsigned _block, unsigned _index);
/// Show the debugger for a specified record
Q_INVOKABLE void debugRecord(unsigned _index);
private slots:
/// Update UI with machine states result. Display a modal dialog.
@ -144,6 +151,12 @@ signals:
void runStarted();
/// Transaction execution completed successfully
void runComplete();
/// Mining has started
void miningStarted();
/// Mined a new block
void miningComplete();
/// Mining stopped or started
void miningStateChanged();
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
@ -159,7 +172,7 @@ signals:
/// @param _message RPC response in Json format
void apiResponse(QString const& _message);
/// New transaction log entry
void newTransaction(TransactionLogEntry* _tr);
void newRecord(RecordLogEntry* _r);
/// State (transaction log) cleared
void stateCleared();
@ -174,6 +187,7 @@ private:
AppContext* m_context;
std::atomic<bool> m_running;
std::atomic<bool> m_mining;
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;

8
mix/CodeEditorExtensionManager.cpp

@ -26,8 +26,6 @@
#include <QQmlComponent>
#include <QQuickTextDocument>
#include "StatusPane.h"
#include "AssemblyDebuggerControl.h"
#include "StateListView.h"
#include "AppContext.h"
#include "MixApplication.h"
#include "CodeModel.h"
@ -56,13 +54,9 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
void CodeEditorExtensionManager::initExtensions()
{
std::shared_ptr<StatusPane> output = std::make_shared<StatusPane>(m_appContext);
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output);
initExtension(debug);
initExtension(stateList);
}
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
@ -94,10 +88,10 @@ void CodeEditorExtensionManager::applyCodeHighlight()
void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
{
m_rightView = _rightView;
initExtensions(); //TODO: move this to a proper place
}
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView)
{
m_headerView = _headerView;
initExtensions(); //TODO: move this to a proper place
}

10
mix/CodeHighlighter.cpp

@ -76,14 +76,14 @@ void CodeHighlighter::processSource(std::string const& _source)
solidity::Token::Value token = scanner.getCurrentToken();
while (token != Token::EOS)
{
if ((token >= Token::BREAK && token < Token::TYPES_END) ||
token == Token::IN || token == Token::DELETE || token == Token::NULL_LITERAL || token == Token::TRUE_LITERAL || token == Token::FALSE_LITERAL)
if ((token >= Token::Break && token < Token::TypesEnd) ||
token == Token::In || token == Token::Delete || token == Token::NullLiteral || token == Token::TrueLiteral || token == Token::FalseLiteral)
m_formats.push_back(FormatRange(CodeHighlighterSettings::Keyword, scanner.getCurrentLocation()));
else if (token == Token::STRING_LITERAL)
else if (token == Token::StringLiteral)
m_formats.push_back(FormatRange(CodeHighlighterSettings::StringLiteral, scanner.getCurrentLocation()));
else if (token == Token::COMMENT_LITERAL)
else if (token == Token::CommentLiteral)
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, scanner.getCurrentLocation()));
else if (token == Token::NUMBER)
else if (token == Token::Number)
m_formats.push_back(FormatRange(CodeHighlighterSettings::NumLiteral, scanner.getCurrentLocation()));
token = scanner.next();

2
mix/CodeHighlighter.h

@ -81,7 +81,7 @@ public:
int start;
int length;
};
typedef std::vector<FormatRange> Formats; // Sorted by start position
using Formats = std::vector<FormatRange>; // Sorted by start position
public:
/// Collect highligting information by lexing the source

7
mix/ContractCallDataEncoder.cpp

@ -51,6 +51,8 @@ void ContractCallDataEncoder::push(bytes const& _b)
QList<QVariableDefinition*> ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value)
{
bytesConstRef value(&_value);
bytes rawParam(32);
QList<QVariableDefinition*> r;
for (int k = 0; k <_returnParameters.length(); k++)
{
@ -69,11 +71,10 @@ QList<QVariableDefinition*> ContractCallDataEncoder::decode(QList<QVariableDecla
else
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found"));
bytes rawParam(_value.begin(), _value.begin() + 32);
value.populate(&rawParam);
def->decodeValue(rawParam);
r.push_back(def);
if (_value.size() > 32)
_value = bytes(_value.begin() + 32, _value.end());
value = value.cropped(32);
qDebug() << "decoded return value : " << dec->type() << " " << def->value();
}
return r;

8
mix/Exceptions.h

@ -40,10 +40,10 @@ struct FunctionNotFoundException: virtual Exception {};
struct ExecutionStateException: virtual Exception {};
struct ParameterChangedException: virtual Exception {};
typedef boost::error_info<struct tagQmlError, QQmlError> QmlErrorInfo;
typedef boost::error_info<struct tagFileError, std::string> FileError;
typedef boost::error_info<struct tagBlockIndex, unsigned> BlockIndex;
typedef boost::error_info<struct tagFunctionName, std::string> FunctionName;
using QmlErrorInfo = boost::error_info<struct tagQmlError, QQmlError>;
using FileError = boost::error_info<struct tagFileError, std::string>;
using BlockIndex = boost::error_info<struct tagBlockIndex, unsigned>;
using FunctionName = boost::error_info<struct tagFunctionName, std::string>;
}
}

8
mix/MachineStates.h

@ -61,7 +61,7 @@ namespace mix
*/
struct ExecutionResult
{
ExecutionResult() : receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {}
ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {}
std::vector<MachineState> machineStates;
std::vector<bytes> transactionData;
@ -71,9 +71,11 @@ namespace mix
dev::Address sender;
dev::Address contractAddress;
dev::u256 value;
dev::eth::TransactionReceipt receipt;
unsigned transactionIndex;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
};
using ExecutionResults = std::vector<ExecutionResult>;
}
}
}

181
mix/MixClient.cpp

@ -15,7 +15,6 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file MixClient.cpp
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
@ -27,6 +26,7 @@
#include <libethereum/Transaction.h>
#include <libethereum/Executive.h>
#include <libethereum/ExtVM.h>
#include <libethereum/BlockChain.h>
#include <libevm/VM.h>
#include "Exceptions.h"
@ -34,15 +34,37 @@
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
namespace dev
{
namespace mix
{
const Secret c_userAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
const u256 c_mixGenesisDifficulty = (u256) 1 << 4;
class MixBlockChain: public dev::eth::BlockChain
{
public:
MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, true)
{
}
static bytes createGenesisBlock(h256 _stateRoot)
{
RLPStream block(3);
block.appendList(14)
<< h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_mixGenesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << std::string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
}
};
MixClient::MixClient(std::string const& _dbPath):
m_userAccount(c_userAccountSecret), m_bc(_dbPath, true), m_dbPath(_dbPath), m_minigThreads(0)
m_userAccount(c_userAccountSecret), m_dbPath(_dbPath), m_minigThreads(0)
{
//TODO: put this into genesis block somehow
//resetState(10000000 * ether);
resetState(10000000 * ether);
}
MixClient::~MixClient()
@ -51,32 +73,34 @@ MixClient::~MixClient()
void MixClient::resetState(u256 _balance)
{
(void) _balance;
{
WriteGuard l(x_state);
Guard fl(m_filterLock);
m_filters.clear();
m_watches.clear();
m_bc.reopen(m_dbPath, true);
m_state = eth::State();
m_stateDB = OverlayDB();
m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::CanonGenesis);
m_state.sync(m_bc);
m_startState = m_state;
m_pendingExecutions.clear();
}
mine();
WriteGuard l(x_state);
Guard fl(m_filterLock);
m_filters.clear();
m_watches.clear();
m_stateDB = OverlayDB();
TrieDB<Address, MemoryDB> accountState(&m_stateDB);
accountState.init();
std::map<Address, Account> genesisState = { std::make_pair(KeyPair(c_userAccountSecret).address(), Account(_balance, Account::NormalCreation)) };
dev::eth::commit(genesisState, static_cast<MemoryDB&>(m_stateDB), accountState);
h256 stateRoot = accountState.root();
m_bc.reset();
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot));
m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty);
m_state.sync(bc());
m_startState = m_state;
m_executions.clear();
}
void MixClient::executeTransaction(Transaction const& _t, State& _state)
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call)
{
bytes rlp = _t.rlp();
// do debugging run first
LastHashes lastHashes(256);
lastHashes[0] = m_bc.numberHash(m_bc.number());
lastHashes[0] = bc().numberHash(bc().number());
for (unsigned i = 1; i < 256; ++i)
lastHashes[i] = lastHashes[i - 1] ? m_bc.details(lastHashes[i - 1]).parent : h256();
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
State execState = _state;
Executive execution(execState, lastHashes, 0);
@ -145,64 +169,56 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
d.value = _t.value();
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage
m_pendingExecutions.emplace_back(std::move(d));
if (!_call)
d.transactionIndex = m_state.pending().size();
m_executions.emplace_back(std::move(d));
// execute on a state
_state.execute(lastHashes, rlp, nullptr, true);
// collect watches
h256Set changed;
Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > m_bc.number())
{
// acceptable number.
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size())
if (!_call)
{
_state.execute(lastHashes, rlp, nullptr, true);
// collect watches
h256Set changed;
Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > bc().number())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1));
changed.insert(i.first);
// acceptable number.
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1));
changed.insert(i.first);
}
}
}
changed.insert(dev::eth::PendingChangedFilter);
noteChanged(changed);
changed.insert(dev::eth::PendingChangedFilter);
noteChanged(changed);
}
}
void MixClient::mine()
{
WriteGuard l(x_state);
m_state.commitToMine(m_bc);
m_state.commitToMine(bc());
while (!m_state.mine(100, true).completed) {}
m_state.completeMine();
m_bc.import(m_state.blockData(), m_stateDB);
m_state.sync(m_bc);
//m_state.cleanup(true);
bc().import(m_state.blockData(), m_stateDB);
m_state.sync(bc());
m_startState = m_state;
m_executions.emplace_back(std::move(m_pendingExecutions));
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed);
}
ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const
{
if (_block == m_bc.number() + 1)
return m_pendingExecutions.at(_transaction);
return m_executions.at(_block).at(_transaction);
}
ExecutionResult const& MixClient::lastExecution() const
{
if (m_pendingExecutions.size() > 0)
return m_pendingExecutions.back();
return m_executions.back().back();
return m_executions.back();
}
ExecutionResults const& MixClient::pendingExecutions() const
ExecutionResults const& MixClient::executions() const
{
return m_pendingExecutions;
return m_executions;
}
State MixClient::asOf(int _block) const
@ -213,25 +229,23 @@ State MixClient::asOf(int _block) const
else if (_block == -1)
return m_startState;
else
return State(m_stateDB, m_bc, m_bc.numberHash(_block));
return State(m_stateDB, bc(), bc().numberHash(_block));
}
void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
_gasPrice = 0; //TODO: remove after fixing setBalance
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
executeTransaction(t, m_state);
executeTransaction(t, m_state, false);
}
Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
_gasPrice = 0; //TODO: remove after fixing setBalance
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state);
executeTransaction(t, m_state, false);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address;
}
@ -240,7 +254,7 @@ void MixClient::inject(bytesConstRef _rlp)
{
WriteGuard l(x_state);
eth::Transaction t(_rlp, CheckSignature::None);
executeTransaction(t, m_state);
executeTransaction(t, m_state, false);
}
void MixClient::flushTransactions()
@ -259,8 +273,8 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp);
return m_pendingExecutions.back().returnValue;
executeTransaction(t, temp, true);
return lastExecution().returnValue;
}
u256 MixClient::balanceAt(Address _a, int _block) const
@ -301,12 +315,12 @@ eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
{
LocalisedLogEntries ret;
unsigned lastBlock = m_bc.number();
unsigned lastBlock = bc().number();
unsigned block = std::min<unsigned>(lastBlock, (unsigned)_f.latest());
unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest()));
unsigned skip = _f.skip();
// Pending transactions
if (block > m_bc.number())
if (block > bc().number())
{
ReadGuard l(x_state);
for (unsigned i = 0; i < m_state.pending().size(); ++i)
@ -318,15 +332,15 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
skip -= std::min(skip, static_cast<unsigned>(logEntries.size()));
}
block = m_bc.number();
block = bc().number();
}
// The rest
auto h = m_bc.numberHash(block);
auto h = bc().numberHash(block);
for (; ret.size() != block && block != end; block--)
{
if (_f.matches(m_bc.info(h).logBloom))
for (TransactionReceipt receipt: m_bc.receipts(h).receipts)
if (_f.matches(bc().info(h).logBloom))
for (TransactionReceipt receipt: bc().receipts(h).receipts)
if (_f.matches(receipt.bloom()))
{
LogEntries logEntries = _f.matches(receipt);
@ -334,7 +348,7 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
skip -= std::min(skip, static_cast<unsigned>(logEntries.size()));
}
h = m_bc.details(h).parent;
h = bc().details(h).parent;
}
return ret;
}
@ -416,22 +430,22 @@ LocalisedLogEntries MixClient::checkWatch(unsigned _watchId)
h256 MixClient::hashFromNumber(unsigned _number) const
{
return m_bc.numberHash(_number);
return bc().numberHash(_number);
}
eth::BlockInfo MixClient::blockInfo(h256 _hash) const
{
return BlockInfo(m_bc.block(_hash));
return BlockInfo(bc().block(_hash));
}
eth::BlockDetails MixClient::blockDetails(h256 _hash) const
{
return m_bc.details(_hash);
return bc().details(_hash);
}
eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const
{
auto bl = m_bc.block(_blockHash);
auto bl = bc().block(_blockHash);
RLP b(bl);
if (_i < b[1].itemCount())
return Transaction(b[1][_i].data(), CheckSignature::Range);
@ -441,7 +455,7 @@ eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const
eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const
{
auto bl = m_bc.block(_blockHash);
auto bl = bc().block(_blockHash);
RLP b(bl);
if (_i < b[2].itemCount())
return BlockInfo::fromHeader(b[2][_i].data());
@ -451,7 +465,7 @@ eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const
unsigned MixClient::number() const
{
return m_bc.number();
return bc().number();
}
eth::Transactions MixClient::pending() const
@ -461,7 +475,7 @@ eth::Transactions MixClient::pending() const
eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const
{
State st(m_stateDB, m_bc, _block);
State st(m_stateDB, bc(), _block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
}
@ -526,3 +540,6 @@ eth::MineProgress MixClient::miningProgress() const
{
return eth::MineProgress();
}
}
}

17
mix/MixClient.h

@ -27,7 +27,6 @@
#include <string>
#include <libethereum/Interface.h>
#include <libethereum/Client.h>
#include <libethereum/CanonBlockChain.h>
#include "MachineStates.h"
namespace dev
@ -35,6 +34,8 @@ namespace dev
namespace mix
{
class MixBlockChain;
class MixClient: public dev::eth::Interface
{
public:
@ -44,9 +45,8 @@ public:
void resetState(u256 _balance);
KeyPair const& userAccount() const { return m_userAccount; }
void mine();
ExecutionResult const& execution(unsigned _block, unsigned _transaction) const;
ExecutionResult const& lastExecution() const;
ExecutionResults const& pendingExecutions() const;
ExecutionResults const& executions() const;
//dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -85,23 +85,26 @@ public:
void stopMining() override;
bool isMining() override;
eth::MineProgress miningProgress() const override;
std::pair<h256, u256> getWork() override { return std::pair<h256, u256>(); }
bool submitNonce(h256 const&) override { return false; }
private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state);
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call);
void noteChanged(h256Set const& _filters);
dev::eth::State asOf(int _block) const;
MixBlockChain& bc() { return *m_bc; }
MixBlockChain const& bc() const { return *m_bc; }
KeyPair m_userAccount;
eth::State m_state;
eth::State m_startState;
OverlayDB m_stateDB;
eth::CanonBlockChain m_bc;
std::auto_ptr<MixBlockChain> m_bc;
mutable boost::shared_mutex x_state;
mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches;
std::vector<ExecutionResults> m_executions;
ExecutionResults m_pendingExecutions;
ExecutionResults m_executions;
std::string m_dbPath;
unsigned m_minigThreads;
};

48
mix/StateListView.cpp

@ -1,48 +0,0 @@
/*
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 StateListView.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQuickItem>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include "StateListView.h"
using namespace dev::mix;
StateListView::StateListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightView)
{
}
QString StateListView::contentUrl() const
{
return QStringLiteral("qrc:/qml/StateList.qml");
}
QString StateListView::title() const
{
return QApplication::tr("States");
}
void StateListView::start() const
{
}

45
mix/StateListView.h

@ -1,45 +0,0 @@
/*
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 StateListView.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <memory>
#include <QTextDocument>
#include "Extension.h"
namespace dev
{
namespace mix
{
/// State list control
class StateListView: public Extension
{
Q_OBJECT
public:
StateListView(AppContext* _context);
void start() const override;
QString title() const override;
QString contentUrl() const override;
};
}
}

31
mix/qml/CallStack.qml

@ -1,31 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
Item {
property alias model: callTable.model
signal frameActivated(int index)
ColumnLayout {
anchors.fill: parent
Text {
text: qsTr("Call Stack")
Layout.fillWidth: true
}
TableView {
id: callTable
Layout.fillWidth: true
Layout.fillHeight: true
headerDelegate: null
TableViewColumn {
role: "modelData"
title: qsTr("Address")
width: parent.width
}
onActivated: {
frameActivated(row);
}
}
}
}

38
mix/qml/DebugInfoList.qml

@ -4,14 +4,19 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
ColumnLayout {
id: root
property string title
property variant listModel;
property bool collapsible;
property bool enableSelection;
property real storedHeight: 0;
property Component itemDelegate
signal rowActivated(int index)
spacing: 0
function collapse()
{
storedHeight = childrenRect.height;
storageContainer.state = "collapsed";
}
@ -32,7 +37,9 @@ ColumnLayout {
Image {
source: "qrc:/qml/img/opentriangleindicator.png"
width: 15
height: 15
sourceSize.width: 15
sourceSize.height: 15
id: storageImgArrow
}
@ -53,16 +60,20 @@ ColumnLayout {
if (storageContainer.state == "collapsed")
{
storageContainer.state = "";
storageContainer.parent.parent.height = storageContainer.parent.parent.Layout.maximumHeight;
storageContainer.parent.parent.height = storedHeight;
}
else
{
storedHeight = root.childrenRect.height;
storageContainer.state = "collapsed";
}
}
}
}
}
Rectangle
{
id: storageContainer
border.width: 3
border.color: "#deddd9"
Layout.fillWidth: true
@ -80,18 +91,35 @@ ColumnLayout {
}
}
]
id: storageContainer
ListView {
TableView {
clip: true;
alternatingRowColors: false
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 3
anchors.leftMargin: 3
width: parent.width - 3
height: parent.height - 6
id: storageList
model: listModel
delegate: itemDelegate
selectionMode: enableSelection ? SelectionMode.SingleSelection : SelectionMode.NoSelection
headerDelegate: null
itemDelegate: root.itemDelegate
onHeightChanged: {
if (height <= 0 && collapsible) {
if (storedHeight <= 0)
storedHeight = 200;
storageContainer.state = "collapsed";
}
else if (height > 0 && storageContainer.state == "collapsed") {
//TODO: fix increasing size
//storageContainer.state = "";
}
}
onActivated: rowActivated(row);
TableViewColumn {
role: "modelData"
width: parent.width
}
}
}
}

304
mix/qml/Debugger.qml

@ -9,8 +9,10 @@ import "js/ErrorLocationFormater.js" as ErrorLocationFormater
Rectangle {
id: debugPanel
property alias transactionLog : transactionLog
objectName: "debugPanel"
anchors.fill: parent;
color: "#ededed"
clip: true
@ -22,7 +24,7 @@ Rectangle {
function update(data, giveFocus)
{
if (statusPane.result.successful)
if (statusPane && statusPane.result.successful)
{
Debugger.init(data);
debugScrollArea.visible = true;
@ -55,6 +57,16 @@ Rectangle {
onCompilationComplete: update(null, false);
}
Settings {
id: splitSettings
property alias transactionLogHeight: transactionLog.height
property alias callStackHeight: callStackRect.height
property alias storageHeightSettings: storageRect.height
property alias memoryDumpHeightSettings: memoryRect.height
property alias callDataHeightSettings: callDataRect.height
property alias transactionLogVisible: transactionLog.visible
}
Rectangle
{
visible: false;
@ -104,49 +116,56 @@ Rectangle {
}
}
Flickable {
property int firstColumnWidth: 180
property int secondColumnWidth: 250
SplitView {
id: debugScrollArea
flickableDirection: Flickable.VerticalFlick
anchors.fill: parent
contentHeight: 4000
contentWidth: parent.width
Rectangle
{
orientation: Qt.Vertical
handleDelegate: Rectangle {
height: machineStates.sideMargin
color: "transparent"
anchors.fill: parent
ColumnLayout
{
property int sideMargin: 10
id: machineStates
}
TransactionLog {
id: transactionLog
Layout.fillWidth: true
Layout.minimumHeight: 60
height: 250
}
ScrollView
{
property int sideMargin: 10
id: machineStates
Layout.fillWidth: true
Layout.fillHeight: true
function updateHeight() {
statesLayout.height = buttonRow.childrenRect.height + assemblyCodeRow.childrenRect.height +
callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height + 120;
}
Component.onCompleted: updateHeight();
ColumnLayout {
id: statesLayout
anchors.top: parent.top
anchors.topMargin: 15
anchors.left: parent.left;
anchors.leftMargin: machineStates.sideMargin
anchors.right: parent.right;
anchors.rightMargin: machineStates.sideMargin
anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
TransactionLog {
Layout.fillWidth: true
height: 250
}
width: debugScrollArea.width - machineStates.sideMargin * 2 - 20;
spacing: machineStates.sideMargin
RowLayout {
Rectangle {
// step button + slider
id: buttonRow
spacing: machineStates.sideMargin
height: 27
Layout.fillWidth: true
color: "transparent"
Rectangle
{
height: parent.height
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
color: "transparent"
width: debugScrollArea.firstColumnWidth
width: stateListContainer.width
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
id: jumpButtons
@ -226,9 +245,11 @@ Rectangle {
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: debugInfoContainer.width
color: "transparent"
Layout.fillWidth: true
height: parent.height
Slider {
id: statesSlider
anchors.fill: parent
@ -255,83 +276,99 @@ Rectangle {
}
}
RowLayout {
Rectangle {
// Assembly code
id: assemblyCodeRow
Layout.fillWidth: true
height: 405
implicitHeight: 405
spacing: machineStates.sideMargin
color: "transparent"
Rectangle
{
id: stateListContainer
width: debugScrollArea.firstColumnWidth
anchors.top : parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.width * 0.4
height: parent.height
border.width: 3
border.color: "#deddd9"
color: "white"
anchors.top: parent.top
ListView {
TableView {
id: statesList
anchors.fill: parent
anchors.leftMargin: 3
anchors.rightMargin: 3
anchors.topMargin: 3
anchors.bottomMargin: 3
clip: true
id: statesList
delegate: renderDelegate
highlight: highlightBar
//highlightFollowsCurrentItem: false
headerDelegate: null
itemDelegate: renderDelegate
model: ListModel {}
TableViewColumn {
role: "line"
width: parent.width - 10
}
}
Component {
id: highlightBar
Rectangle {
radius: 4
height: statesList.currentItem.height
width: statesList.currentItem.width;
anchors.fill: parent
y: statesList.currentItem.y
color: "#4A90E2"
//Behavior on y {
// PropertyAnimation { properties: "y"; easing.type: Easing.InOutQuad; duration: 50}
//}
}
}
Component {
id: renderDelegate
RowLayout {
id: wrapperItem
height: 20
width: parent.width
spacing: 5
Text {
anchors.left: parent.left
anchors.leftMargin: 10
width: 15
color: "#b2b3ae"
text: line.split(' ')[0]
font.family: "monospace"
font.pointSize: 9
id: id
wrapMode: Text.NoWrap
Item {
Rectangle {
radius: 4
anchors.fill: parent
color: "#4A90E2"
visible: styleData.selected;
}
Text {
wrapMode: Text.NoWrap
color: parent.ListView.isCurrentItem ? "white" : "black"
font.family: "monospace"
text: line.replace(line.split(' ')[0], '')
anchors.left: id.right
font.pointSize: 9
RowLayout {
id: wrapperItem
anchors.fill: parent
spacing: 5
Text {
anchors.left: parent.left
anchors.leftMargin: 10
width: 15
color: "#b2b3ae"
text: styleData.value.split(' ')[0]
font.family: "monospace"
font.pointSize: 9
wrapMode: Text.NoWrap
id: id
}
Text {
anchors.left: id.right;
wrapMode: Text.NoWrap
color: styleData.selected ? "white" : "black"
font.family: "monospace"
text: styleData.value.replace(styleData.value.split(' ')[0], '')
font.pointSize: 9
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
id: debugInfoContainer
width: parent.width * 0.6 - machineStates.sideMargin
anchors.top : parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
height: parent.height //- 2 * stateListContainer.border.width
color: "transparent"
ColumnLayout
@ -372,7 +409,7 @@ Rectangle {
title : qsTr("Stack")
itemDelegate: Item {
id: renderedItem
height: 25
//height: 25
width: parent.width
RowLayout
{
@ -391,7 +428,7 @@ Rectangle {
anchors.leftMargin: 5
font.family: "monospace"
color: "#4a4a4a"
text: model.index;
text: styleData.row;
font.pointSize: 9
}
}
@ -402,7 +439,6 @@ Rectangle {
Layout.fillWidth: true
Layout.minimumWidth: 15
Layout.preferredWidth: 15
Layout.maximumWidth: 60
Layout.minimumHeight: parent.height
Text {
anchors.left: parent.left
@ -410,7 +446,7 @@ Rectangle {
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: modelData
text: styleData.value
font.pointSize: 9
}
}
@ -434,42 +470,95 @@ Rectangle {
id: splitInfoList
Layout.fillHeight: true
Layout.fillWidth: true
Settings {
id: splitSettings
property alias storageHeightSettings: storageRect.height
property alias memoryDumpHeightSettings: memoryRect.height
property alias callDataHeightSettings: callDataRect.height
}
orientation: Qt.Vertical
width: debugPanel.width - 2 * machineStates.sideMargin
Rectangle
{
id: callStackRect;
color: "transparent"
height: 120
width: parent.width
Layout.minimumHeight: 120
Layout.maximumHeight: 400
CallStack {
anchors.fill: parent
Layout.minimumHeight: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
DebugInfoList
{
id: callStack
onFrameActivated: Debugger.displayFrame(index);
collapsible: true
anchors.fill: parent
title : qsTr("Call Stack")
enableSelection: true
onRowActivated: Debugger.displayFrame(index);
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: 9
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.Wrap
anchors.left: parent.left
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: styleData.value;
elide: Text.ElideRight
font.pointSize: 9
}
}
}
Rectangle {
anchors.top: row.bottom
width: parent.width;
height: 1;
color: "#cccccc"
anchors.bottom: parent.bottom
}
}
}
}
Rectangle
{
id: storageRect
color: "transparent"
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 223
height: 25
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
DebugInfoList
{
id: storage
@ -478,29 +567,24 @@ Rectangle {
title : qsTr("Storage")
itemDelegate:
Item {
height: 27
width: parent.width;
anchors.fill: parent
RowLayout
{
id: row
width: parent.width
height: 26
anchors.fill: parent
Rectangle
{
color: "#f7f7f7"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.preferredWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Layout.minimumHeight: parent.height
Layout.maximumHeight: parent.height
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
font.family: "monospace"
anchors.leftMargin: 5
color: "#4a4a4a"
text: modelData.split('\t')[0];
text: styleData.value.split('\t')[0];
font.pointSize: 9
width: parent.width - 5
elide: Text.ElideRight
@ -511,10 +595,7 @@ Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.minimumWidth: parent.width / 2
Layout.preferredWidth: parent.width / 2
Layout.maximumWidth: parent.width / 2
Layout.minimumHeight: parent.height
Layout.maximumHeight: parent.height
Text {
anchors.leftMargin: 5
width: parent.width - 5
@ -523,7 +604,7 @@ Rectangle {
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: modelData.split('\t')[1];
text: styleData.value.split('\t')[1];
elide: Text.ElideRight
font.pointSize: 9
}
@ -545,10 +626,10 @@ Rectangle {
{
id: memoryRect;
color: "transparent"
height: 25
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 223
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
DebugInfoList {
id: memoryDump
anchors.fill: parent
@ -567,10 +648,10 @@ Rectangle {
{
id: callDataRect
color: "transparent"
height: 25
width: parent.width
Layout.minimumHeight: 25
Layout.maximumHeight: 223
Layout.maximumHeight: 800
onHeightChanged: machineStates.updateHeight();
DebugInfoList {
id: callDataDump
anchors.fill: parent
@ -586,8 +667,9 @@ Rectangle {
}
Rectangle
{
id: bottomRect;
width: parent.width
Layout.minimumHeight: 25
Layout.minimumHeight: 20
color: "transparent"
}
}

250
mix/qml/FilesSection.qml

@ -0,0 +1,250 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.3
import "."
ColumnLayout {
id: wrapperItem
signal documentSelected(string doc, string groupName)
property alias model: filesList.model
property string sectionName;
property variant selManager;
Layout.fillWidth: true
Layout.minimumHeight: hiddenHeightTopLevel()
height: hiddenHeightTopLevel()
Layout.maximumHeight: hiddenHeightTopLevel()
spacing: 0
function hiddenHeightTopLevel()
{
return section.state === "hidden" ? Style.documentsList.height : Style.documentsList.fileNameHeight * model.count + Style.documentsList.height;
}
function hiddenHeightRepeater()
{
return section.state === "hidden" ? 0 : Style.documentsList.fileNameHeight * wrapperItem.model.count;
}
function hiddenHeightElement()
{
return section.state === "hidden" ? 0 : Style.documentsList.fileNameHeight;
}
function getDocumentIndex(documentId)
{
for (var i = 0; i < model.count; i++)
if (model.get(i).documentId === documentId)
return i;
return -1;
}
function removeDocument(documentId)
{
var i = getDocumentIndex(documentId);
if (i !== -1)
model.remove(i);
}
FontLoader
{
id: fileNameFont
source: "qrc:/qml/fonts/SourceSansPro-Regular.ttf"
}
FontLoader
{
id: boldFont
source: "qrc:/qml/fonts/SourceSansPro-Bold.ttf"
}
RowLayout
{
anchors.top: parent.top
id: rowCol
width: parent.width
height: Style.documentsList.height
Image {
source: "qrc:/qml/img/opentriangleindicator_filesproject.png"
width: 15
sourceSize.width: 12
id: imgArrow
anchors.right: section.left
anchors.rightMargin: 8
anchors.top: parent.top
anchors.topMargin: 6
}
Text
{
id: section
text: sectionName
anchors.left: parent.left
anchors.leftMargin: Style.general.leftMargin
color: Style.documentsList.sectionColor
font.family: boldFont.name
font.pointSize: Style.documentsList.sectionFontSize
states: [
State {
name: "hidden"
PropertyChanges { target: filesList; visible: false; }
PropertyChanges { target: rowCol; Layout.minimumHeight: Style.documentsList.height; Layout.maximumHeight: Style.documentsList.height; height: Style.documentsList.height; }
PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" }
}
]
}
MouseArea {
id: titleMouseArea
anchors.fill: parent
hoverEnabled: true
z: 2
onClicked: {
if (section.state === "hidden")
section.state = "";
else
section.state = "hidden";
}
}
}
ColumnLayout {
height: wrapperItem.hiddenHeightRepeater()
Layout.minimumHeight: wrapperItem.hiddenHeightRepeater()
Layout.preferredHeight: wrapperItem.hiddenHeightRepeater()
Layout.maximumHeight: wrapperItem.hiddenHeightRepeater()
width: parent.width
visible: section.state !== "hidden"
spacing: 0
Repeater
{
id: filesList
visible: section.state !== "hidden"
Rectangle
{
visible: section.state !== "hidden"
id: rootItem
Layout.fillWidth: true
Layout.minimumHeight: wrapperItem.hiddenHeightElement()
Layout.preferredHeight: wrapperItem.hiddenHeightElement()
Layout.maximumHeight: wrapperItem.hiddenHeightElement()
height: wrapperItem.hiddenHeightElement()
color: isSelected ? Style.documentsList.highlightColor : Style.documentsList.background
property bool isSelected
property bool renameMode
Text {
id: nameText
height: parent.height
visible: !renameMode
color: rootItem.isSelected ? Style.documentsList.selectedColor : Style.documentsList.color
text: name;
font.family: fileNameFont.name
font.pointSize: Style.documentsList.fontSize
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
anchors.left: parent.left
anchors.leftMargin: Style.general.leftMargin + 2
width: parent.width
Connections
{
target: selManager
onSelected: {
if (groupName != sectionName)
rootItem.isSelected = false;
else if (doc === documentId)
rootItem.isSelected = true;
else
rootItem.isSelected = false;
if (rootItem.isSelected && section.state === "hidden")
section.state = "";
}
}
}
TextInput {
id: textInput
text: nameText.text
visible: renameMode
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Style.general.leftMargin
MouseArea {
id: textMouseArea
anchors.fill: parent
hoverEnabled: true
z: 2
onClicked: {
textInput.forceActiveFocus();
}
}
onVisibleChanged: {
if (visible) {
selectAll();
forceActiveFocus();
}
}
onAccepted: close(true);
onCursorVisibleChanged: {
if (!cursorVisible)
close(false);
}
onFocusChanged: {
if (!focus)
close(false);
}
function close(accept) {
rootItem.renameMode = false;
if (accept)
{
var i = getDocumentIndex(documentId);
projectModel.renameDocument(documentId, textInput.text);
wrapperItem.model.set(i, projectModel.getDocument(documentId));
}
}
}
MouseArea {
id: mouseArea
z: 1
hoverEnabled: false
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked:{
if (mouse.button === Qt.RightButton && !isContract)
contextMenu.popup();
else if (mouse.button === Qt.LeftButton)
{
rootItem.isSelected = true;
projectModel.openDocument(documentId);
documentSelected(documentId, groupName);
}
}
}
Menu {
id: contextMenu
MenuItem {
text: qsTr("Rename")
onTriggered: {
rootItem.renameMode = true;
}
}
MenuItem {
text: qsTr("Delete")
onTriggered: {
projectModel.removeDocument(documentId);
wrapperItem.removeDocument(documentId);
}
}
}
}
}
}
}

71
mix/qml/MainContent.qml

@ -7,9 +7,9 @@ import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
import "."
Rectangle {
objectName: "mainContent"
signal keyPressed(variant event)
focus: true
@ -21,11 +21,12 @@ Rectangle {
anchors.fill: parent
id: root
property alias rightViewVisible : rightView.visible
property alias webViewVisible : webPreview.visible
property alias projectViewVisible : projectList.visible
property alias runOnProjectLoad : mainSettings.runOnProjectLoad
property bool webViewHorizontal : codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property alias rightViewVisible: rightView.visible
property alias webViewVisible: webPreview.visible
property alias projectViewVisible: projectList.visible
property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: rightView
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property bool firstCompile: true
Connections {
@ -76,7 +77,6 @@ Rectangle {
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;
}
Settings {
@ -121,6 +121,12 @@ Rectangle {
}
}
Rectangle{
Layout.fillWidth: true
height: 1
color: "#8c8c8c"
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: root.height - headerView.height;
@ -136,16 +142,16 @@ Rectangle {
{
anchors.fill: parent
handleDelegate: Rectangle {
width: 4
height: 4
color: "#cccccc"
width: 1
height: 1
color: "#8c8c8c"
}
orientation: Qt.Horizontal
ProjectList {
id: projectList
width: 200
Layout.minimumWidth: 180
width: 350
Layout.minimumWidth: 250
Layout.fillHeight: true
}
Rectangle {
@ -154,9 +160,9 @@ Rectangle {
Layout.fillWidth: true
SplitView {
handleDelegate: Rectangle {
width: 4
height: 4
color: "#cccccc"
width: 1
height: 1
color: "#8c8c8c"
}
id: codeWebSplitter
anchors.fill: parent
@ -178,46 +184,13 @@ Rectangle {
}
}
Rectangle {
Debugger {
visible: false;
id: rightView;
Layout.fillHeight: true
Keys.onEscapePressed: visible = false
height: parent.height;
width: 515
Layout.minimumWidth: 515
anchors.right: parent.right
Rectangle {
anchors.fill: parent;
id: rightPaneView
TabView {
id: rightPaneTabs
tabsVisible: true
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tabBar:
Rectangle {
color: "#ededed"
id: background
}
tab: Rectangle {
color: "#ededed"
implicitWidth: 80
implicitHeight: 20
radius: 2
Text {
anchors.centerIn: parent
text: styleData.title
color: styleData.selected ? "#7da4cd" : "#202020"
}
}
frame: Rectangle {
}
}
}
}
}
}
}

256
mix/qml/ProjectList.qml

@ -2,138 +2,166 @@ import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.3
import Qt.labs.settings 1.0
import "."
Item {
property bool renameMode: false;
ColumnLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
color: "blue"
text: projectModel.projectTitle
horizontalAlignment: Text.AlignHCenter
visible: !projectModel.isEmpty;
id: filesCol
spacing: 0
FontLoader
{
id: srcSansProLight
source: "qrc:/qml/fonts/SourceSansPro-Light.ttf"
}
ListView {
id: projectList
Layout.fillWidth: true
Layout.fillHeight: true
model: projectModel.listModel
Rectangle
{
color: Style.title.background
height: Style.title.height
Layout.fillWidth: true
Image {
id: projectIcon
source: "qrc:/qml/img/projecticon.png"
sourceSize.height: 30
anchors.right: projectTitle.left
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 6
}
delegate: renderDelegate
highlight: Rectangle {
color: "lightsteelblue";
Text
{
id: projectTitle
color: Style.title.color
text: projectModel.projectTitle
anchors.verticalCenter: parent.verticalCenter
visible: !projectModel.isEmpty;
anchors.left: parent.left
anchors.leftMargin: Style.general.leftMargin
font.family: srcSansProLight.name
font.pointSize: Style.title.pointSize
font.weight: Font.Light
}
highlightFollowsCurrentItem: true
focus: true
clip: true
onCurrentIndexChanged: {
if (currentIndex >= 0 && currentIndex < projectModel.listModel.count)
projectModel.openDocument(projectModel.listModel.get(currentIndex).documentId);
Text
{
text: "-"
anchors.right: parent.right
anchors.rightMargin: 15
font.family: srcSansProLight.name
font.pointSize: Style.title.pointSize
anchors.verticalCenter: parent.verticalCenter
font.weight: Font.Light
}
}
Menu {
id: contextMenu
MenuItem {
text: qsTr("Rename")
onTriggered: {
renameMode = true;
}
}
MenuItem {
text: qsTr("Delete")
onTriggered: {
projectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId);
}
}
Rectangle
{
Layout.fillWidth: true
height: 10
color: Style.documentsList.background
}
}
Component {
id: renderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
RowLayout {
anchors.fill: parent
visible: !(index === projectList.currentIndex) || !renameMode
Text {
id: nameText
Layout.fillWidth: true
Layout.fillHeight: true
text: name
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
}
TextInput {
id: textInput
text: nameText.text
visible: (index === projectList.currentIndex) && renameMode
MouseArea {
id: textMouseArea
anchors.fill: parent
hoverEnabled: true
z:2
onClicked: {
textInput.forceActiveFocus();
}
}
onVisibleChanged: {
if (visible) {
selectAll();
forceActiveFocus();
}
}
onAccepted: close(true);
onCursorVisibleChanged: {
if (!cursorVisible)
close(false);
}
onFocusChanged: {
if (!focus)
close(false);
}
function close(accept) {
renameMode = false;
if (accept)
projectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text);
}
}
MouseArea {
id: mouseArea
z: 1
hoverEnabled: false
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked:{
projectList.currentIndex = index;
if (mouse.button === Qt.RightButton && !projectList.model.get(index).isContract)
contextMenu.popup();
}
}
}
}
Connections {
target: projectModel
onProjectLoaded: {
projectList.currentIndex = 0;
if (projectList.currentIndex >= 0 && projectList.currentIndex < projectModel.listModel.count)
projectModel.openDocument(projectModel.listModel.get(projectList.currentIndex).documentId);
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
color: Style.documentsList.background
}
onProjectClosed: {
projectList.currentIndex = -1;
}
onDocumentOpened: {
if (projectList.currentItem.documentId !== document.documentId)
projectList.currentIndex = projectModel.getDocumentIndex(document.documentId);
ColumnLayout
{
anchors.top: parent.top
width: parent.width
spacing: 0
Repeater {
model: [qsTr("Contracts"), qsTr("Javascript"), qsTr("Web Pages"), qsTr("Styles"), qsTr("Images"), qsTr("Misc")];
signal selected(string doc, string groupName)
id: sectionRepeater
FilesSection
{
sectionName: modelData
model: sectionModel
selManager: sectionRepeater
onDocumentSelected: {
selManager.selected(doc, groupName);
}
ListModel
{
id: sectionModel
}
Connections {
target: codeModel
onCompilationComplete: {
if (modelData === "Contracts")
{
var ctr = projectModel.listModel.get(0);
if (codeModel.code.contract.name !== ctr.name)
{
ctr.name = codeModel.code.contract.name;
projectModel.listModel.set(0, ctr);
sectionModel.set(0, ctr);
}
}
}
}
Connections {
id: projectModelConnection
target: projectModel
function addDocToSubModel()
{
for (var k = 0; k < projectModel.listModel.count; k++)
{
var item = projectModel.listModel.get(k);
if (item.groupName === modelData)
sectionModel.append(item);
}
}
onDocumentOpened: {
if (document.groupName === modelData)
sectionRepeater.selected(document.documentId, modelData);
}
onNewProject: {
sectionModel.clear();
}
onProjectLoaded: {
addDocToSubModel();
if (modelData === "Contracts")
{
var selItem = projectModel.listModel.get(0);
projectModel.openDocument(selItem.documentId);
sectionRepeater.selected(selItem.documentId, modelData);
}
}
onDocumentAdded:
{
var newDoc = projectModel.getDocument(documentId);
if (newDoc.groupName === modelData)
{
sectionModel.append(newDoc);
projectModel.openDocument(newDoc.documentId);
sectionRepeater.selected(newDoc.documentId, modelData);
}
}
}
}
}
}
}
}
}

1
mix/qml/ProjectModel.qml

@ -41,6 +41,7 @@ Item {
function addExistingFile() { ProjectModelCode.addExistingFile(); }
function newHtmlFile() { ProjectModelCode.newHtmlFile(); }
function newJsFile() { ProjectModelCode.newJsFile(); }
function newCssFile() { ProjectModelCode.newCssFile(); }
//function newContract() { ProjectModelCode.newContract(); }
function openDocument(documentId) { ProjectModelCode.openDocument(documentId); }
function openNextDocument() { ProjectModelCode.openNextDocument(); }

58
mix/qml/StateList.qml

@ -3,60 +3,66 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
Rectangle {
color: "#ededed"
Window {
id: stateListContainer
focus: true
anchors.topMargin: 10
anchors.left: parent.left
height: parent.height
width: parent.width
modality: Qt.WindowModal
ListView {
id: list
anchors.top: parent.top
height: parent.height
width: parent.width
model: projectModel.stateListModel
delegate: renderDelegate
}
width: 640
height: 480
Button {
anchors.bottom: parent.bottom
action: addStateAction
visible: false
ColumnLayout
{
anchors.fill: parent
TableView {
id: list
Layout.fillHeight: true
Layout.fillWidth: true
model: projectModel.stateListModel
itemDelegate: renderDelegate
headerDelegate: null
TableViewColumn {
role: "title"
title: qsTr("State")
width: list.width
}
}
Button {
anchors.bottom: parent.bottom
action: addStateAction
}
}
Component {
id: renderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
RowLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
Layout.fillHeight: true
text: title
text: styleData.value
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
ToolButton {
text: qsTr("Edit");
Layout.fillHeight: true
onClicked: list.model.editState(index);
onClicked: list.model.editState(styleData.row);
}
ToolButton {
visible: list.model.count - 1 != index
visible: list.model.defaultStateIndex !== styleData.row
text: qsTr("Delete");
Layout.fillHeight: true
onClicked: list.model.deleteState(index);
onClicked: list.model.deleteState(styleData.row);
}
ToolButton {
text: qsTr("Run");
Layout.fillHeight: true
onClicked: list.model.runState(index);
onClicked: list.model.runState(styleData.row);
}
}
}

10
mix/qml/StateListModel.qml

@ -8,7 +8,6 @@ import "js/QEtherHelper.js" as QEtherHelper
Item {
property int defaultStateIndex: 0
property alias model: stateListModel
property var stateList: []
@ -111,7 +110,7 @@ Item {
for(var i = 0; i < stateListModel.count; i++) {
projectData.states.push(toPlainStateItem(stateList[i]));
}
projectData.defaultStateIndex = defaultStateIndex;
projectData.defaultStateIndex = stateListModel.defaultStateIndex;
}
onNewProject: {
var state = toPlainStateItem(stateListModel.createDefaultState());
@ -127,12 +126,12 @@ Item {
var item = stateDialog.getItem();
if (stateDialog.stateIndex < stateListModel.count) {
if (stateDialog.isDefault)
defaultStateIndex = stateIndex;
stateListModel.defaultStateIndex = stateIndex;
stateList[stateDialog.stateIndex] = item;
stateListModel.set(stateDialog.stateIndex, item);
} else {
if (stateDialog.isDefault)
defaultStateIndex = 0;
stateListModel.defaultStateIndex = 0;
stateList.push(item);
stateListModel.append(item);
}
@ -149,8 +148,10 @@ Item {
ListModel {
id: stateListModel
property int defaultStateIndex: 0
signal defaultStateChanged;
signal stateListModelReady;
signal stateRun(int index)
function defaultTransactionItem() {
return {
@ -205,6 +206,7 @@ Item {
function runState(index) {
var item = stateList[index];
clientModel.setupState(item);
stateRun(index);
}
function deleteState(index) {

30
mix/qml/Style.qml

@ -0,0 +1,30 @@
pragma Singleton
import QtQuick 2.0
/*
* Project Files
*/
QtObject {
property QtObject general: QtObject {
property int leftMargin: 45
}
property QtObject title: QtObject {
property string color: "#808080"
property string background: "#f0f0f0"
property int height: 55
property int pointSize: 18
}
property QtObject documentsList: QtObject {
property string background: "#f7f7f7"
property string color: "#4d4d4d"
property string sectionColor: "#808080"
property string selectedColor: "white"
property string highlightColor: "#4a90e2"
property int height: 25
property int fileNameHeight: 30
property int fontSize: 13
property int sectionFontSize: 13
}
}

58
mix/qml/TransactionDialog.qml

@ -60,15 +60,39 @@ Window {
else
{
var parameters = codeModel.code.contract.constructor.parameters;
for (var p = 0; p < parameters.length; p++) {
var pname = parameters[p].name;
paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname].value() : "" });
}
for (var p = 0; p < parameters.length; p++)
loadParameter(parameters[p]);
}
visible = true;
valueField.focus = true;
}
function loadParameter(parameter)
{
var type = parameter.type;
var pname = parameter.name;
var varComponent;
if (type.indexOf("int") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QIntType.qml");
else if (type.indexOf("real") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QRealType.qml");
else if (type.indexOf("string") !== -1 || type.indexOf("text") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QStringType.qml");
else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QHashType.qml");
else if (type.indexOf("bool") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QBoolType.qml");
var param = varComponent.createObject(modalTransactionDialog);
var value = itemParams[pname] !== undefined ? itemParams[pname] : "";
param.setValue(value);
param.setDeclaration(parameter);
qType.push({ name: pname, value: param });
paramsModel.append({ name: pname, type: type, value: value });
}
function loadParameters() {
paramsModel.clear();
if (!paramsModel)
@ -76,30 +100,8 @@ Window {
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var func = codeModel.code.contract.functions[functionComboBox.currentIndex];
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++) {
var pname = parameters[p].name;
var varComponent;
var type = parameters[p].type;
if (type.indexOf("int") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QIntType.qml");
else if (type.indexOf("real") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QRealType.qml");
else if (type.indexOf("string") !== -1 || type.indexOf("text") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QStringType.qml");
else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QHashType.qml");
else if (type.indexOf("bool") !== -1)
varComponent = Qt.createComponent("qrc:/qml/QBoolType.qml");
var param = varComponent.createObject(modalTransactionDialog);
var value = itemParams[pname] !== undefined ? itemParams[pname] : "";
param.setValue(value);
param.setDeclaration(parameters[p]);
qType.push({ name: pname, value: param });
paramsModel.append({ name: pname, type: parameters[p].type, value: value });
}
for (var p = 0; p < parameters.length; p++)
loadParameter(parameters[p]);
}
}

111
mix/qml/TransactionLog.qml

@ -5,33 +5,93 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
Item {
property bool showLogs: true
property ListModel fullModel: ListModel{}
property ListModel transactionModel: ListModel{}
onShowLogsChanged: {
logTable.model = showLogs ? fullModel : transactionModel
}
Action {
id: addStateAction
text: "Add State"
shortcut: "Ctrl+Alt+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: projectModel.stateListModel.addState();
}
Action {
id: editStateAction
text: "Edit State"
shortcut: "Ctrl+Alt+T"
enabled: codeModel.hasContract && !clientModel.running && statesCombo.currentIndex >= 0 && projectModel.stateListModel.count > 0;
onTriggered: projectModel.stateListModel.editState(statesCombo.currentIndex);
}
ColumnLayout {
anchors.fill: parent
CheckBox {
id: recording
text: qsTr("Record transactions");
checked: true
Layout.fillWidth: true
RowLayout {
ComboBox {
id: statesCombo
model: projectModel.stateListModel
width: 150
editable: false
textRole: "title"
onActivated: {
model.runState(index);
}
Connections {
target: projectModel.stateListModel
onStateRun: {
if (statesCombo.currentIndex !== index)
statesCombo.currentIndex = index;
}
}
}
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: editStateAction
}
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: addStateAction
}
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: mineAction
}
CheckBox {
id: recording
text: qsTr("Record transactions");
checked: true
Layout.fillWidth: true
}
}
TableView {
id: logTable
Layout.fillWidth: true
Layout.fillHeight: true
model: logModel
model: fullModel
TableViewColumn {
role: "block"
title: qsTr("Block")
width: 40
}
TableViewColumn {
role: "tindex"
role: "transactionIndex"
title: qsTr("Index")
width: 40
}
TableViewColumn {
role: "contract"
title: qsTr("Contract")
width: 120
width: 100
}
TableViewColumn {
role: "function"
@ -41,7 +101,7 @@ Item {
TableViewColumn {
role: "value"
title: qsTr("Value")
width: 120
width: 60
}
TableViewColumn {
role: "address"
@ -54,30 +114,31 @@ Item {
width: 120
}
onActivated: {
var item = logModel.get(row);
clientModel.debugTransaction(item.block, item.tindex);
var item = logTable.model.get(row);
clientModel.debugRecord(item.recordIndex);
}
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logModel.count) {
var item = logModel.get(currentRow);
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow);
appContext.toClipboard(item.returned);
}
}
}
}
ListModel {
id: logModel
}
Connections {
target: clientModel
onStateCleared: {
logModel.clear();
fullModel.clear();
transactionModel.clear();
}
onNewTransaction: {
onNewRecord: {
if (recording.checked)
logModel.append(_tr);
{
fullModel.append(_r);
if (!_r.call)
transactionModel.append(_r);
}
}
}

3
mix/qml/WebPreview.qml

@ -114,7 +114,8 @@ Item {
onClientConnected: {
//filter polling spam
//TODO: do it properly
var log = _request.content.indexOf("eth_changed") < 0;
//var log = _request.content.indexOf("eth_changed") < 0;
var log = true;
if (log)
console.log(_request.content);
var response = clientModel.apiCall(_request.content);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save