Browse Source

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

cl-refactor
Paweł Bylica 10 years ago
parent
commit
c2f2b0094e
  1. 2
      CodingStandards.txt
  2. 1
      alethzero/MainWin.cpp
  3. 5
      libdevcore/CommonJS.h
  4. 2
      libdevcrypto/MemoryDB.h
  5. 7
      libethash/CMakeLists.txt
  6. 4
      libethash/data_sizes.h
  7. 111
      libethash/ethash.h
  8. 38
      libethash/internal.c
  9. 6
      libethash/internal.h
  10. 89
      libethash/io.c
  11. 116
      libethash/io.h
  12. 76
      libethash/io_posix.c
  13. 73
      libethash/io_win32.c
  14. 2
      libethcore/Common.cpp
  15. 25
      libethcore/Ethasher.cpp
  16. 27
      libethcore/Ethasher.h
  17. 17
      libethereum/BlockChain.cpp
  18. 2
      libethereum/State.cpp
  19. 2
      libevm/VM.h
  20. 55
      libevmcore/Assembly.cpp
  21. 2
      libevmcore/Assembly.h
  22. 5
      libevmcore/AssemblyItem.h
  23. 332
      libevmcore/CommonSubexpressionEliminator.cpp
  24. 99
      libevmcore/CommonSubexpressionEliminator.h
  25. 94
      libevmcore/ExpressionClasses.cpp
  26. 20
      libevmcore/ExpressionClasses.h
  27. 107
      libevmcore/SemanticInformation.cpp
  28. 50
      libevmcore/SemanticInformation.h
  29. 49
      libp2p/Host.cpp
  30. 16
      libsolidity/AST.cpp
  31. 17
      libsolidity/Types.cpp
  32. 2
      libsolidity/Types.h
  33. 3
      libtestutils/CMakeLists.txt
  34. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  35. 21
      mix/CodeModel.cpp
  36. 1
      mix/qml.qrc
  37. 1
      mix/qml/CodeEditorView.qml
  38. 27
      mix/qml/Debugger.qml
  39. 556
      mix/qml/LogsPane.qml
  40. 18
      mix/qml/LogsPaneStyle.qml
  41. 1
      mix/qml/ProjectModel.qml
  42. 260
      mix/qml/StatesComboBox.qml
  43. 61
      mix/qml/StatusPane.qml
  44. 59
      mix/qml/TransactionLog.qml
  45. 7
      mix/qml/WebPreview.qml
  46. 41
      mix/qml/html/codeeditor.js
  47. BIN
      mix/qml/img/broom.png
  48. BIN
      mix/qml/img/clearicon.png
  49. BIN
      mix/qml/img/cleariconactive.png
  50. BIN
      mix/qml/img/copyicon.png
  51. BIN
      mix/qml/img/copyiconactive.png
  52. BIN
      mix/qml/img/edit_combox.png
  53. BIN
      mix/qml/img/pause_button.png
  54. BIN
      mix/qml/img/pause_button2x.png
  55. BIN
      mix/qml/img/play_button.png
  56. BIN
      mix/qml/img/play_button2x.png
  57. BIN
      mix/qml/img/searchicon.png
  58. BIN
      mix/qml/img/stop_button2x.png
  59. 10
      mix/qml/js/ProjectModel.js
  60. 12
      mix/res.qrc
  61. 19
      mix/style.xml
  62. 35
      test/ClientBase.cpp
  63. 14
      test/SolidityNameAndTypeResolution.cpp
  64. 265
      test/SolidityOptimizer.cpp
  65. 4
      test/dagger.cpp
  66. 9
      test/peer.cpp
  67. 114
      test/stMemoryTestFiller.json

2
CodingStandards.txt

@ -13,7 +13,7 @@ c. Don't use braces for condition-body one-liners.
d. Never place condition bodies on same line as condition. d. Never place condition bodies on same line as condition.
e. Space between first paren and keyword, but *not* following first paren or preceeding final paren. e. Space between first paren and keyword, but *not* following first paren or preceeding final paren.
f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity.
g. No spaces for subscripting. g. No spaces for subscripting or unary operators.
h. No space before ':' but one after it, except in the ternary operator: one on both sides. h. No space before ':' but one after it, except in the ternary operator: one on both sides.
i. Space all other operators. i. Space all other operators.
j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope.

1
alethzero/MainWin.cpp

@ -1774,6 +1774,7 @@ void Main::on_net_triggered()
else else
{ {
ui->downloadView->setDownloadMan(nullptr); ui->downloadView->setDownloadMan(nullptr);
writeSettings();
web3()->stopNetwork(); web3()->stopNetwork();
} }
} }

5
libdevcore/CommonJS.h

@ -38,7 +38,10 @@ template <unsigned S> std::string toJS(FixedHash<S> const& _h)
template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n) template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n)
{ {
return "0x" + toHex(toCompactBigEndian(_n, 1)); std::string h = toHex(toCompactBigEndian(_n, 1));
// remove first 0, if it is necessary;
std::string res = h[0] != '0' ? h : h.substr(1);
return "0x" + res;
} }
inline std::string toJS(bytes const& _n, std::size_t _padding = 0) inline std::string toJS(bytes const& _n, std::size_t _padding = 0)

2
libdevcrypto/MemoryDB.h

@ -32,8 +32,10 @@ namespace dev
{ {
struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; }; struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; };
struct DBWarn: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 1; };
#define dbdebug clog(DBChannel) #define dbdebug clog(DBChannel)
#define dbwarn clog(DBWarn)
class MemoryDB class MemoryDB
{ {

7
libethash/CMakeLists.txt

@ -12,6 +12,7 @@ endif()
set(FILES util.c set(FILES util.c
util.h util.h
io.c
internal.c internal.c
ethash.h ethash.h
endian.h endian.h
@ -19,6 +20,12 @@ set(FILES util.c
fnv.h fnv.h
data_sizes.h) data_sizes.h)
if (MSVC)
list(APPEND FILES io_win32.c)
else()
list(APPEND FILES io_posix.c)
endif()
if (NOT CRYPTOPP_FOUND) if (NOT CRYPTOPP_FOUND)
find_package(CryptoPP 5.6.2) find_package(CryptoPP 5.6.2)
endif() endif()

4
libethash/data_sizes.h

@ -48,7 +48,7 @@ extern "C" {
// Sow[i*HashBytes]; j++]]]][[2]][[1]] // Sow[i*HashBytes]; j++]]]][[2]][[1]]
static const size_t dag_sizes[2048] = { static const uint64_t dag_sizes[2048] = {
1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U, 1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U,
1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U, 1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U,
1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U, 1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U,
@ -477,7 +477,7 @@ static const size_t dag_sizes[2048] = {
// While[! PrimeQ[i], i--]; // While[! PrimeQ[i], i--];
// Sow[i*HashBytes]; j++]]]][[2]][[1]] // Sow[i*HashBytes]; j++]]]][[2]][[1]]
const size_t cache_sizes[2048] = { const uint64_t cache_sizes[2048] = {
16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U, 16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U,
17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U, 17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U,
18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U, 18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U,

111
libethash/ethash.h

@ -24,27 +24,28 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h>
#include "compiler.h" #include "compiler.h"
#define REVISION 23 #define ETHASH_REVISION 23
#define DATASET_BYTES_INIT 1073741824U // 2**30 #define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30
#define DATASET_BYTES_GROWTH 8388608U // 2**23 #define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23
#define CACHE_BYTES_INIT 1073741824U // 2**24 #define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24
#define CACHE_BYTES_GROWTH 131072U // 2**17 #define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17
#define EPOCH_LENGTH 30000U #define ETHASH_EPOCH_LENGTH 30000U
#define MIX_BYTES 128 #define ETHASH_MIX_BYTES 128
#define HASH_BYTES 64 #define ETHASH_HASH_BYTES 64
#define DATASET_PARENTS 256 #define ETHASH_DATASET_PARENTS 256
#define CACHE_ROUNDS 3 #define ETHASH_CACHE_ROUNDS 3
#define ACCESSES 64 #define ETHASH_ACCESSES 64
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef struct ethash_params { typedef struct ethash_params {
size_t full_size; // Size of full data set (in bytes, multiple of mix size (128)). uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
size_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)). uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params; } ethash_params;
typedef struct ethash_return_value { typedef struct ethash_return_value {
@ -52,8 +53,8 @@ typedef struct ethash_return_value {
uint8_t mix_hash[32]; uint8_t mix_hash[32];
} ethash_return_value; } ethash_return_value;
size_t ethash_get_datasize(const uint32_t block_number); uint64_t ethash_get_datasize(const uint32_t block_number);
size_t ethash_get_cachesize(const uint32_t block_number); uint64_t ethash_get_cachesize(const uint32_t block_number);
// initialize the parameters // initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) { static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
@ -61,55 +62,77 @@ static inline void ethash_params_init(ethash_params *params, const uint32_t bloc
params->cache_size = ethash_get_cachesize(block_number); params->cache_size = ethash_get_cachesize(block_number);
} }
typedef struct ethash_cache { /***********************************
void *mem; * OLD API *************************
} ethash_cache; ***********************************
******************** (deprecated) *
***********************************/
void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]);
void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache const *cache);
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number); void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number);
void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]);
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache);
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) { /***********************************
ethash_cache c; * NEW API *************************
c.mem = cache; ***********************************/
ethash_mkcache(&c, params, seed);
} // TODO: compute params and seed in ethash_new_light; it should take only block_number
// TODO: store params in ethash_light_t/ethash_full_t to avoid having to repass into compute/new_full
typedef uint8_t const ethash_seedhash_t[32];
static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { typedef void const* ethash_light_t;
ethash_cache c; static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) {
c.mem = (void *) cache; void* ret = malloc(params->cache_size);
ethash_light(ret, &c, params, header_hash, nonce); ethash_mkcache(ret, params, seed);
return ret;
}
static inline void ethash_compute_light(ethash_return_value *ret, ethash_light_t light, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_light(ret, light, params, header_hash, nonce);
}
static inline void ethash_delete_light(ethash_light_t light) {
free((void*)light);
} }
typedef void const* ethash_full_t;
static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) {
void* ret = malloc(params->full_size);
ethash_compute_full_data(ret, params, light);
return ret;
}
static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) { static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) {
ethash_cache c; ethash_compute_full_data(full, params, cache);
c.mem = (void *) cache;
ethash_compute_full_data(full, params, &c);
} }
static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_full(ret, full, params, header_hash, nonce); ethash_full(ret, full, params, header_hash, nonce);
} }
// Returns if hash is less than or equal to difficulty /// @brief Compare two s256-bit big-endian values.
static inline int ethash_check_difficulty( /// @returns 1 if @a a is less than or equal to @a b, 0 otherwise.
const uint8_t hash[32], /// Both parameters are 256-bit big-endian values.
const uint8_t difficulty[32]) { static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) {
// Difficulty is big endian // Boundary is big endian
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (hash[i] == difficulty[i]) continue; if (a[i] == b[i])
return hash[i] < difficulty[i]; continue;
return a[i] < b[i];
} }
return 1; return 1;
} }
int ethash_quick_check_difficulty( /// Perofrms a cursory check on the validity of the nonce.
/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary.
/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian.
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32], const uint8_t header_hash[32],
const uint64_t nonce, const uint64_t nonce,
const uint8_t mix_hash[32], const uint8_t mix_hash[32],
const uint8_t difficulty[32]); const uint8_t boundary[32]);
#define ethash_quick_check_difficulty ethash_preliminary_check_boundary
#define ethash_check_difficulty ethash_leq_be256
#ifdef __cplusplus #ifdef __cplusplus
} }

38
libethash/internal.c

@ -37,14 +37,14 @@
#include "sha3.h" #include "sha3.h"
#endif // WITH_CRYPTOPP #endif // WITH_CRYPTOPP
size_t ethash_get_datasize(const uint32_t block_number) { uint64_t ethash_get_datasize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048); assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return dag_sizes[block_number / EPOCH_LENGTH]; return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
} }
size_t ethash_get_cachesize(const uint32_t block_number) { uint64_t ethash_get_cachesize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048); assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return cache_sizes[block_number / EPOCH_LENGTH]; return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
} }
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) // Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
@ -63,7 +63,7 @@ void static ethash_compute_cache_nodes(
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64); SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
} }
for (unsigned j = 0; j != CACHE_ROUNDS; j++) { for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) { for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes; uint32_t const idx = nodes[i].words[0] % num_nodes;
node data; node data;
@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes(
} }
void ethash_mkcache( void ethash_mkcache(
ethash_cache *cache, void *cache,
ethash_params const *params, ethash_params const *params,
const uint8_t seed[32]) { const uint8_t seed[32]) {
node *nodes = (node *) cache->mem; node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed); ethash_compute_cache_nodes(nodes, params, seed);
} }
@ -96,10 +96,10 @@ void ethash_calculate_dag_item(
node *const ret, node *const ret,
const unsigned node_index, const unsigned node_index,
const struct ethash_params *params, const struct ethash_params *params,
const struct ethash_cache *cache) { const void *cache) {
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node)); uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache->mem; node const *cache_nodes = (node const *) cache;
node const *init = &cache_nodes[node_index % num_parent_nodes]; node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node)); memcpy(ret, init, sizeof(node));
@ -114,7 +114,7 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3]; __m128i xmm3 = ret->xmm[3];
#endif #endif
for (unsigned i = 0; i != DATASET_PARENTS; ++i) { for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes; uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index]; node const *parent = &cache_nodes[parent_index];
@ -150,7 +150,7 @@ void ethash_calculate_dag_item(
void ethash_compute_full_data( void ethash_compute_full_data(
void *mem, void *mem,
ethash_params const *params, ethash_params const *params,
ethash_cache const *cache) { void const *cache) {
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0); assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0); assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem; node *full_nodes = mem;
@ -164,7 +164,7 @@ void ethash_compute_full_data(
static void ethash_hash( static void ethash_hash(
ethash_return_value *ret, ethash_return_value *ret,
node const *full_nodes, node const *full_nodes,
ethash_cache const *cache, void const *cache,
ethash_params const *params, ethash_params const *params,
const uint8_t header_hash[32], const uint8_t header_hash[32],
const uint64_t nonce) { const uint64_t nonce) {
@ -201,7 +201,7 @@ static void ethash_hash(
num_full_pages = (unsigned) (params->full_size / page_size); num_full_pages = (unsigned) (params->full_size / page_size);
for (unsigned i = 0; i != ACCESSES; ++i) { for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages; uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) { for (unsigned n = 0; n != MIX_NODES; ++n) {
@ -275,12 +275,12 @@ void ethash_quick_hash(
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) { void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
memset(seedhash, 0, 32); memset(seedhash, 0, 32);
const uint32_t epochs = block_number / EPOCH_LENGTH; const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i) for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, seedhash, 32); SHA3_256(seedhash, seedhash, 32);
} }
int ethash_quick_check_difficulty( int ethash_preliminary_check_boundary(
const uint8_t header_hash[32], const uint8_t header_hash[32],
const uint64_t nonce, const uint64_t nonce,
const uint8_t mix_hash[32], const uint8_t mix_hash[32],
@ -288,13 +288,13 @@ int ethash_quick_check_difficulty(
uint8_t return_hash[32]; uint8_t return_hash[32];
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash); ethash_quick_hash(return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(return_hash, difficulty); return ethash_leq_be256(return_hash, difficulty);
} }
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce); ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce);
} }
void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, NULL, cache, params, previous_hash, nonce); ethash_hash(ret, NULL, cache, params, previous_hash, nonce);
} }

6
libethash/internal.h

@ -3,7 +3,7 @@
#include "endian.h" #include "endian.h"
#include "ethash.h" #include "ethash.h"
#define ENABLE_SSE 1 #define ENABLE_SSE 0
#if defined(_M_X64) && ENABLE_SSE #if defined(_M_X64) && ENABLE_SSE
#include <smmintrin.h> #include <smmintrin.h>
@ -15,7 +15,7 @@ extern "C" {
// compile time settings // compile time settings
#define NODE_WORDS (64/4) #define NODE_WORDS (64/4)
#define MIX_WORDS (MIX_BYTES/4) #define MIX_WORDS (ETHASH_MIX_BYTES/4)
#define MIX_NODES (MIX_WORDS / NODE_WORDS) #define MIX_NODES (MIX_WORDS / NODE_WORDS)
#include <stdint.h> #include <stdint.h>
@ -34,7 +34,7 @@ void ethash_calculate_dag_item(
node *const ret, node *const ret,
const unsigned node_index, const unsigned node_index,
ethash_params const *params, ethash_params const *params,
ethash_cache const *cache void const *cache
); );
void ethash_quick_hash( void ethash_quick_hash(

89
libethash/io.c

@ -0,0 +1,89 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <string.h>
#include <stdio.h>
// silly macro to save some typing
#define PASS_ARR(c_) (c_), sizeof(c_)
static bool ethash_io_write_file(char const *dirname,
char const* filename,
size_t filename_length,
void const* data,
size_t data_size)
{
bool ret = false;
char *fullname = ethash_io_create_filename(dirname, filename, filename_length);
if (!fullname) {
return false;
}
FILE *f = fopen(fullname, "wb");
if (!f) {
goto free_name;
}
if (data_size != fwrite(data, 1, data_size, f)) {
goto close;
}
ret = true;
close:
fclose(f);
free_name:
free(fullname);
return ret;
}
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size)
{
char info_buffer[DAG_MEMO_BYTESIZE];
// allocate the bytes
uint8_t *temp_data_ptr = malloc(params->full_size);
if (!temp_data_ptr) {
goto end;
}
ethash_compute_full_data(temp_data_ptr, params, cache);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, params->full_size)) {
goto fail_free;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, info_buffer);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) {
goto fail_free;
}
*data = temp_data_ptr;
*data_size = params->full_size;
return true;
fail_free:
free(temp_data_ptr);
end:
return false;
}
#undef PASS_ARR

116
libethash/io.h

@ -0,0 +1,116 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "ethash.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t;
static const char DAG_FILE_NAME[] = "full";
static const char DAG_MEMO_NAME[] = "full.info";
// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :(
#define DAG_MEMO_BYTESIZE 36
/// Possible return values of @see ethash_io_prepare
enum ethash_io_rc {
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch
ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything
};
/**
* Prepares io for ethash
*
* Create the DAG directory if it does not exist, and check if the memo file matches.
* If it does not match then it's deleted to pave the way for @ref ethash_io_write()
*
* @param dirname A null terminated c-string of the path of the ethash
* data directory. If it does not exist it's created.
* @param seedhash The seedhash of the current block number
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash);
/**
* Fully computes data and writes it to the file on disk.
*
* This function should be called after @see ethash_io_prepare() and only if
* its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data
* and the memo file.
*
* @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. Has to exist.
* @param[in] params An ethash_params object containing the full size
* and the cache size
* @param[in] seedhash The seedhash of the current block number
* @param[in] cache The cache data. Would have usually been calulated by
* @see ethash_prep_light().
* @param[out] data Pass a pointer to uint8_t by reference here. If the
* function is succesfull then this point to the allocated
* data calculated by @see ethash_prep_full(). Memory
* ownership is transfered to the callee. Remember that
* you eventually need to free this with a call to free().
* @param[out] data_size Pass a size_t by value. If the function is succesfull
* then this will contain the number of bytes allocated
* for @a data.
* @return True for success and false in case of failure.
*/
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size);
static inline void ethash_io_serialize_info(uint32_t revision,
ethash_blockhash_t seed_hash,
char *output)
{
// if .info is only consumed locally we don't really care about endianess
memcpy(output, &revision, 4);
memcpy(output + 4, &seed_hash, 32);
}
static inline char *ethash_io_create_filename(char const *dirname,
char const* filename,
size_t filename_length)
{
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char *name = (char*)malloc(strlen(dirname) + filename_length);
if (!name) {
return NULL;
}
name[0] = '\0';
strcat(name, dirname);
strcat(name, filename);
return name;
}
#ifdef __cplusplus
}
#endif

76
libethash/io_posix.c

@ -0,0 +1,76 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io_posix.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <unistd.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists, full owner permissions and read/search for others
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}

73
libethash/io_win32.c

@ -0,0 +1,73 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io_win32.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <direct.h>
#include <errno.h>
#include <stdio.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists
int rc = _mkdir(dirname);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (_unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}

2
libethcore/Common.cpp

@ -34,7 +34,7 @@ namespace eth
{ {
const unsigned c_ethashVersion = c_ethashRevision; const unsigned c_ethashVersion = c_ethashRevision;
const unsigned c_protocolVersion = 59; const unsigned c_protocolVersion = 60;
const unsigned c_databaseBaseVersion = 8; const unsigned c_databaseBaseVersion = 8;
#if ETH_FATDB #if ETH_FATDB
const unsigned c_databaseVersionModifier = 1; const unsigned c_databaseVersionModifier = 1;

25
libethcore/Ethasher.cpp

@ -41,7 +41,23 @@ using namespace eth;
Ethasher* dev::eth::Ethasher::s_this = nullptr; Ethasher* dev::eth::Ethasher::s_this = nullptr;
bytes const& Ethasher::cache(BlockInfo const& _header) Ethasher::~Ethasher()
{
while (!m_caches.empty())
killCache(m_caches.begin()->first);
}
void Ethasher::killCache(h256 const& _s)
{
RecursiveGuard l(x_this);
if (m_caches.count(_s))
{
ethash_delete_light(m_caches.at(_s));
m_caches.erase(_s);
}
}
void const* Ethasher::cache(BlockInfo const& _header)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048) if (_header.number > c_ethashEpochLength * 2048)
@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header)
if (!m_caches.count(_header.seedHash())) if (!m_caches.count(_header.seedHash()))
{ {
ethash_params p = params((unsigned)_header.number); ethash_params p = params((unsigned)_header.number);
m_caches[_header.seedHash()].resize(p.cache_size); m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data());
} }
return m_caches[_header.seedHash()]; return m_caches[_header.seedHash()];
} }
@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
ethash_params p = params((unsigned)_header.number); ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = cache(_header); auto c = cache(_header);
ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c.data()); ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]); writeFile(memoFile, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info); writeFile(memoFile + ".info", info);
} }
@ -147,7 +162,7 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{ {
auto p = Ethasher::params(_header); auto p = Ethasher::params(_header);
ethash_return_value r; ethash_return_value r;
ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
} }

27
libethcore/Ethasher.h

@ -30,21 +30,8 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libethash/ethash.h> // TODO: REMOVE once everything merged into this class and an opaque API can be provided. #include <libethash/ethash.h> // TODO: REMOVE once everything merged into this class and an opaque API can be provided.
static const unsigned c_ethashRevision = REVISION; static const unsigned c_ethashRevision = ETHASH_REVISION;
static const unsigned c_ethashEpochLength = EPOCH_LENGTH; static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH;
#undef REVISION
#undef DATASET_BYTES_INIT
#undef DATASET_BYTES_GROWTH
#undef CACHE_BYTES_INIT
#undef CACHE_BYTES_GROWTH
#undef DAGSIZE_BYTES_INIT
#undef DAG_GROWTH
#undef EPOCH_LENGTH
#undef MIX_BYTES
#undef HASH_BYTES
#undef DATASET_PARENTS
#undef CACHE_ROUNDS
#undef ACCESSES
#include "Common.h" #include "Common.h"
#include "BlockInfo.h" #include "BlockInfo.h"
@ -57,10 +44,14 @@ class Ethasher
{ {
public: public:
Ethasher() {} Ethasher() {}
~Ethasher();
static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; }
bytes const& cache(BlockInfo const& _header); using LightType = void const*;
using FullType = void const*;
LightType cache(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header); bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header); static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n); static ethash_params params(unsigned _n);
@ -104,9 +95,11 @@ public:
}; };
private: private:
void killCache(h256 const& _s);
static Ethasher* s_this; static Ethasher* s_this;
RecursiveMutex x_this; RecursiveMutex x_this;
std::map<h256, bytes> m_caches; std::map<h256, LightType> m_caches;
std::map<h256, bytesRef> m_fulls; std::map<h256, bytesRef> m_fulls;
}; };

17
libethereum/BlockChain.cpp

@ -339,6 +339,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
#endif #endif
// All ok - insert into DB // All ok - insert into DB
{ {
// ensure parent is cached for later addition.
// TODO: this is a bit horrible would be better refactored into an enveloping UpgradableGuard
// together with an "ensureCachedWithUpdatableLock(l)" method.
// This is safe in practice since the caches don't get flushed nearly often enough to be
// done here.
details(bi.parentHash);
WriteGuard l(x_details); WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash); m_details[bi.parentHash].children.push_back(newHash);
@ -455,14 +462,14 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const
{ {
// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to) if (!_from || !_to)
return h256s(); return h256s();
h256s ret; h256s ret;
h256s back; h256s back;
unsigned fn = details(_from).number; unsigned fn = details(_from).number;
unsigned tn = details(_to).number; unsigned tn = details(_to).number;
// cdebug << "treeRoute" << fn << "..." << tn; cdebug << "treeRoute" << fn << "..." << tn;
h256 from = _from; h256 from = _from;
while (fn > tn) while (fn > tn)
{ {
@ -470,7 +477,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
ret.push_back(from); ret.push_back(from);
from = details(from).parent; from = details(from).parent;
fn--; fn--;
// cdebug << "from:" << fn << _from.abridged(); cdebug << "from:" << fn << _from.abridged();
} }
h256 to = _to; h256 to = _to;
while (fn < tn) while (fn < tn)
@ -479,11 +486,13 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
back.push_back(to); back.push_back(to);
to = details(to).parent; to = details(to).parent;
tn--; tn--;
// cdebug << "to:" << tn << _to.abridged(); cdebug << "to:" << tn << _to.abridged();
} }
while (from != to) while (from != to)
{ {
if (!from)
assert(from); assert(from);
if (!to)
assert(to); assert(to);
from = details(from).parent; from = details(from).parent;
to = details(to).parent; to = details(to).parent;

2
libethereum/State.cpp

@ -725,7 +725,7 @@ void State::commitToMine(BlockChain const& _bc)
uncommitToMine(); uncommitToMine();
// cnote << "Committing to mine on block" << m_previousBlock.hash.abridged(); // cnote << "Committing to mine on block" << m_previousBlock.hash.abridged();
#ifdef ETH_PARANOIA #if ETH_PARANOIA && 0
commit(); commit();
cnote << "Pre-reward stateRoot:" << m_state.root(); cnote << "Pre-reward stateRoot:" << m_state.root();
#endif #endif

2
libevm/VM.h

@ -56,7 +56,7 @@ public:
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d >= c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } } void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 curPC() const { return m_curPC; } u256 curPC() const { return m_curPC; }

55
libevmcore/Assembly.cpp

@ -34,9 +34,9 @@ void Assembly::append(Assembly const& _a)
for (AssemblyItem i: _a.m_items) for (AssemblyItem i: _a.m_items)
{ {
if (i.type() == Tag || i.type() == PushTag) if (i.type() == Tag || i.type() == PushTag)
i.m_data += m_usedTags; i.setData(i.data() + m_usedTags);
else if (i.type() == PushSub || i.type() == PushSubSize) else if (i.type() == PushSub || i.type() == PushSubSize)
i.m_data += m_subs.size(); i.setData(i.data() + m_usedTags);
append(i); append(i);
} }
m_deposit = newDeposit; m_deposit = newDeposit;
@ -106,34 +106,34 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const&
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
{ {
_out << _prefix; _out << _prefix;
switch (i.m_type) switch (i.type())
{ {
case Operation: case Operation:
_out << " " << instructionInfo(i.instruction()).name << "\t" << i.getJumpTypeAsString(); _out << " " << instructionInfo(i.instruction()).name << "\t" << i.getJumpTypeAsString();
break; break;
case Push: case Push:
_out << " PUSH " << i.m_data; _out << " PUSH " << i.data();
break; break;
case PushString: case PushString:
_out << " PUSH \"" << m_strings.at((h256)i.m_data) << "\""; _out << " PUSH \"" << m_strings.at((h256)i.data()) << "\"";
break; break;
case PushTag: case PushTag:
_out << " PUSH [tag" << i.m_data << "]"; _out << " PUSH [tag" << i.data() << "]";
break; break;
case PushSub: case PushSub:
_out << " PUSH [$" << h256(i.m_data).abridged() << "]"; _out << " PUSH [$" << h256(i.data()).abridged() << "]";
break; break;
case PushSubSize: case PushSubSize:
_out << " PUSH #[$" << h256(i.m_data).abridged() << "]"; _out << " PUSH #[$" << h256(i.data()).abridged() << "]";
break; break;
case PushProgramSize: case PushProgramSize:
_out << " PUSHSIZE"; _out << " PUSHSIZE";
break; break;
case Tag: case Tag:
_out << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST"; _out << "tag" << i.data() << ": " << endl << _prefix << " JUMPDEST";
break; break;
case PushData: case PushData:
_out << " PUSH [" << hex << (unsigned)i.m_data << "]"; _out << " PUSH [" << hex << (unsigned)i.data() << "]";
break; break;
default: default:
BOOST_THROW_EXCEPTION(InvalidOpcode()); BOOST_THROW_EXCEPTION(InvalidOpcode());
@ -187,20 +187,9 @@ Assembly& Assembly::optimise(bool _enable)
{ {
if (!_enable) if (!_enable)
return *this; return *this;
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules = std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules;
{
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
};
// jump to next instruction // jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data() == m[2].data()) return {m[2]}; else return m.toVector(); }});
unsigned total = 0; unsigned total = 0;
for (unsigned count = 1; count > 0; total += count) for (unsigned count = 1; count > 0; total += count)
@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable)
*orig = move(*moveIter); *orig = move(*moveIter);
iter = m_items.erase(orig, iter); iter = m_items.erase(orig, iter);
} }
if (iter != m_items.end())
++iter;
} }
for (unsigned i = 0; i < m_items.size(); ++i) for (unsigned i = 0; i < m_items.size(); ++i)
@ -344,16 +331,16 @@ bytes Assembly::assemble() const
// m_data must not change from here on // m_data must not change from here on
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
switch (i.m_type) switch (i.type())
{ {
case Operation: case Operation:
ret.push_back((byte)i.m_data); ret.push_back((byte)i.data());
break; break;
case PushString: case PushString:
{ {
ret.push_back((byte)Instruction::PUSH32); ret.push_back((byte)Instruction::PUSH32);
unsigned ii = 0; unsigned ii = 0;
for (auto j: m_strings.at((h256)i.m_data)) for (auto j: m_strings.at((h256)i.data()))
if (++ii > 32) if (++ii > 32)
break; break;
else else
@ -364,30 +351,30 @@ bytes Assembly::assemble() const
} }
case Push: case Push:
{ {
byte b = max<unsigned>(1, dev::bytesRequired(i.m_data)); byte b = max<unsigned>(1, dev::bytesRequired(i.data()));
ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.push_back((byte)Instruction::PUSH1 - 1 + b);
ret.resize(ret.size() + b); ret.resize(ret.size() + b);
bytesRef byr(&ret.back() + 1 - b, b); bytesRef byr(&ret.back() + 1 - b, b);
toBigEndian(i.m_data, byr); toBigEndian(i.data(), byr);
break; break;
} }
case PushTag: case PushTag:
{ {
ret.push_back(tagPush); ret.push_back(tagPush);
tagRef[ret.size()] = (unsigned)i.m_data; tagRef[ret.size()] = (unsigned)i.data();
ret.resize(ret.size() + bytesPerTag); ret.resize(ret.size() + bytesPerTag);
break; break;
} }
case PushData: case PushSub: case PushData: case PushSub:
{ {
ret.push_back(dataRefPush); ret.push_back(dataRefPush);
dataRef.insert(make_pair((h256)i.m_data, ret.size())); dataRef.insert(make_pair((h256)i.data(), ret.size()));
ret.resize(ret.size() + bytesPerDataRef); ret.resize(ret.size() + bytesPerDataRef);
break; break;
} }
case PushSubSize: case PushSubSize:
{ {
auto s = m_data[i.m_data].size(); auto s = m_data[i.data()].size();
byte b = max<unsigned>(1, dev::bytesRequired(s)); byte b = max<unsigned>(1, dev::bytesRequired(s));
ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.push_back((byte)Instruction::PUSH1 - 1 + b);
ret.resize(ret.size() + b); ret.resize(ret.size() + b);
@ -403,7 +390,7 @@ bytes Assembly::assemble() const
break; break;
} }
case Tag: case Tag:
tagPos[(unsigned)i.m_data] = ret.size(); tagPos[(unsigned)i.data()] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST); ret.push_back((byte)Instruction::JUMPDEST);
break; break;
default: default:

2
libevmcore/Assembly.h

@ -65,7 +65,7 @@ public:
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
AssemblyItems const& getItems() const { return m_items; } AssemblyItems const& getItems() const { return m_items; }
AssemblyItem const& back() const { return m_items.back(); } AssemblyItem const& back() const { return m_items.back(); }
std::string backString() const { return m_items.size() && m_items.back().m_type == PushString ? m_strings.at((h256)m_items.back().m_data) : std::string(); } std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
void onePath() { if (asserts(!m_totalDeposit && !m_baseDeposit)) BOOST_THROW_EXCEPTION(InvalidDeposit()); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; } void onePath() { if (asserts(!m_totalDeposit && !m_baseDeposit)) BOOST_THROW_EXCEPTION(InvalidDeposit()); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; }
void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; } void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; }

5
libevmcore/AssemblyItem.h

@ -40,8 +40,6 @@ class Assembly;
class AssemblyItem class AssemblyItem
{ {
friend class Assembly;
public: public:
enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
@ -54,6 +52,9 @@ public:
AssemblyItemType type() const { return m_type; } AssemblyItemType type() const { return m_type; }
u256 const& data() const { return m_data; } u256 const& data() const { return m_data; }
void setType(AssemblyItemType const _type) { m_type = _type; }
void setData(u256 const& _data) { m_data = _data; }
/// @returns the instruction of this item (only valid if type() == Operation) /// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { return Instruction(byte(m_data)); } Instruction instruction() const { return Instruction(byte(m_data)); }

332
libevmcore/CommonSubexpressionEliminator.cpp

@ -24,7 +24,7 @@
#include <functional> #include <functional>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/CommonSubexpressionEliminator.h> #include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/Assembly.h> #include <libevmcore/AssemblyItem.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -32,6 +32,8 @@ using namespace dev::eth;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{ {
optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents; map<int, ExpressionClasses::Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents; map<int, ExpressionClasses::Id> targetStackContents;
int minHeight = m_stackHeight + 1; int minHeight = m_stackHeight + 1;
@ -45,19 +47,27 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
// Debug info: // Debug info:
//stream(cout, initialStackContents, targetStackContents); //stream(cout, initialStackContents, targetStackContents);
return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents); AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode(
initialStackContents,
targetStackContents
);
if (m_breakingItem)
items.push_back(*m_breakingItem);
return items;
} }
ostream& CommonSubexpressionEliminator::stream( ostream& CommonSubexpressionEliminator::stream(
ostream& _out, ostream& _out,
map<int, ExpressionClasses::Id> _currentStack, map<int, ExpressionClasses::Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack map<int, ExpressionClasses::Id> _targetStack
) const ) const
{ {
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
{ {
auto const& expr = m_expressionClasses.representative(_id); auto const& expr = m_expressionClasses.representative(_id);
_out << " " << _id << ": " << *expr.item; _out << " " << dec << _id << ": " << *expr.item;
if (expr.sequenceNumber)
_out << "@" << dec << expr.sequenceNumber;
_out << "("; _out << "(";
for (ExpressionClasses::Id arg: expr.arguments) for (ExpressionClasses::Id arg: expr.arguments)
_out << dec << arg << ","; _out << dec << arg << ",";
@ -66,18 +76,12 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl; _out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl; _out << "Final stack height: " << dec << m_stackHeight << endl;
_out << "Stack elements: " << endl;
for (auto const& it: m_stackElements)
{
_out << " " << dec << it.first << " = ";
streamExpressionClass(_out, it.second);
}
_out << "Equivalence classes: " << endl; _out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass); streamExpressionClass(_out, eqClass);
_out << "Current stack: " << endl; _out << "Initial stack: " << endl;
for (auto const& it: _currentStack) for (auto const& it: _initialStack)
{ {
_out << " " << dec << it.first << ": "; _out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second); streamExpressionClass(_out, it.second);
@ -92,13 +96,12 @@ ostream& CommonSubexpressionEliminator::stream(
return _out; return _out;
} }
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem)
{ {
if (_item.type() != Operation) if (_item.type() != Operation)
{ {
if (_item.deposit() != 1) assertThrow(_item.deposit() == 1, InvalidDeposit, "");
BOOST_THROW_EXCEPTION(InvalidDeposit()); setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem));
setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}));
} }
else else
{ {
@ -119,12 +122,47 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
vector<ExpressionClasses::Id> arguments(info.args); vector<ExpressionClasses::Id> arguments(info.args);
for (int i = 0; i < info.args; ++i) for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - i); arguments[i] = stackElement(m_stackHeight - i);
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments)); if (_item.instruction() == Instruction::SSTORE)
storeInStorage(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::SLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0]));
else if (_item.instruction() == Instruction::MSTORE)
storeInMemory(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0]));
else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem));
} }
m_stackHeight += _item.deposit(); m_stackHeight += _item.deposit();
} }
} }
void CommonSubexpressionEliminator::optimizeBreakingItem()
{
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return;
using Id = ExpressionClasses::Id;
static AssemblyItem s_jump = Instruction::JUMP;
Id condition = stackElement(m_stackHeight - 1);
Id zero = m_expressionClasses.find(u256(0));
if (m_expressionClasses.knownToBeDifferent(condition, zero))
{
feedItem(Instruction::SWAP1, true);
feedItem(Instruction::POP, true);
m_breakingItem = &s_jump;
return;
}
Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition});
if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero))
{
feedItem(Instruction::POP, true);
feedItem(Instruction::POP, true);
m_breakingItem = nullptr;
}
}
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
{ {
m_stackElements[_stackHeight] = _class; m_stackElements[_stackHeight] = _class;
@ -132,8 +170,7 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Expression
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB)
{ {
if (_stackHeightA == _stackHeightB) assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements.");
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
// ensure they are created // ensure they are created
stackElement(_stackHeightA); stackElement(_stackHeightA);
stackElement(_stackHeightB); stackElement(_stackHeightB);
@ -157,65 +194,68 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
} }
bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
switch (_item.type())
{
default:
case UndefinedItem:
case Tag:
return true;
case Push:
case PushString:
case PushTag:
case PushSub:
case PushSubSize:
case PushProgramSize:
case PushData:
return false;
case Operation:
{
if (isSwapInstruction(_item) || isDupInstruction(_item))
return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes
InstructionInfo info = instructionInfo(_item.instruction());
// the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2;
}
}
}
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
{ {
if (_item.type() != Operation) if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
return false; // do not execute the storage if we know that the value is already there
switch (_item.instruction()) return;
{ m_sequenceNumber++;
case Instruction::ADD: decltype(m_storageContent) storageContents;
case Instruction::MUL: // copy over values at points where we know that they are different from _slot
case Instruction::EQ: for (auto const& storageItem: m_storageContent)
case Instruction::AND: if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot))
case Instruction::OR: storageContents.insert(storageItem);
case Instruction::XOR: m_storageContent = move(storageContents);
return true; ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
default: m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
return false; m_storageContent[_slot] = _value;
} // increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot)
{
if (m_storageContent.count(_slot))
return m_storageContent.at(_slot);
else
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber);
} }
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{ {
if (_item.type() != Operation) if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
return false; // do not execute the store if we know that the value is already there
return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; return;
m_sequenceNumber++;
decltype(m_memoryContent) memoryContents;
// copy over values at points where we know that they are different from _slot by at least 32
for (auto const& memoryItem: m_memoryContent)
if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot)
{
if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot);
else
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber);
} }
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations
):
m_expressionClasses(_expressionClasses)
{ {
if (_item.type() != Operation) for (auto const& store: _storeOperations)
return false; m_storeOperations[make_pair(store.target, store.slot)].push_back(store);
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
} }
AssemblyItems CSECodeGenerator::generateCode( AssemblyItems CSECodeGenerator::generateCode(
@ -230,26 +270,40 @@ AssemblyItems CSECodeGenerator::generateCode(
// @todo: provide information about the positions of copies of class elements // @todo: provide information about the positions of copies of class elements
// generate the dependency graph // generate the dependency graph starting from final storage and memory writes and target stack contents
for (auto const& p: m_storeOperations)
addDependencies(p.second.back().expression);
for (auto const& targetItem: _targetStackContents) for (auto const& targetItem: _targetStackContents)
{ {
m_finalClasses.insert(targetItem.second); m_finalClasses.insert(targetItem.second);
addDependencies(targetItem.second); addDependencies(targetItem.second);
} }
// generate the actual elements // store all needed sequenced expressions
set<pair<unsigned, ExpressionClasses::Id>> sequencedExpressions;
for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
sequencedExpressions.insert(make_pair(seqNr, id));
// Perform all operations on storage and memory in order, if they are needed.
for (auto const& seqAndId: sequencedExpressions)
if (!m_classPositions.count(seqAndId.second))
generateClassElement(seqAndId.second, true);
// generate the target stack elements
for (auto const& targetItem: _targetStackContents) for (auto const& targetItem: _targetStackContents)
{ {
removeStackTopIfPossible();
int position = generateClassElement(targetItem.second); int position = generateClassElement(targetItem.second);
assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first) if (position == targetItem.first)
continue; continue;
if (position < targetItem.first) if (position < targetItem.first)
// it is already at its target, we need another copy // it is already at its target, we need another copy
appendDup(position); appendDup(position);
else else
appendSwapOrRemove(position); appendOrRemoveSwap(position);
appendSwapOrRemove(targetItem.first); appendOrRemoveSwap(targetItem.first);
} }
// remove surplus elements // remove surplus elements
@ -270,23 +324,59 @@ AssemblyItems CSECodeGenerator::generateCode(
// neither initial no target stack, no change in height // neither initial no target stack, no change in height
finalHeight = 0; finalHeight = 0;
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height.");
return m_generatedItems; return m_generatedItems;
} }
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
{ {
if (m_neededBy.count(_c)) if (m_neededBy.count(_c))
return; return; // we already computed the dependencies for _c
for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments) ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments)
{ {
addDependencies(argument); addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c)); m_neededBy.insert(make_pair(argument, _c));
} }
if (expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD ||
expr.item->instruction() == Instruction::MLOAD
))
{
// this loads an unknown value from storage or memory and thus, in addition to its
// arguments, depends on all store operations to addresses where we do not know that
// they are different that occur before this load
StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
StoreOperation::Storage : StoreOperation::Memory;
ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0);
for (auto const& p: m_storeOperations)
{
if (p.first.first != target)
continue;
ExpressionClasses::Id slot = p.first.second;
StoreOperations const& storeOps = p.second;
if (storeOps.front().sequenceNumber > expr.sequenceNumber)
continue;
if (
(target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) ||
(target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom))
)
continue;
// note that store and load never have the same sequence number
ExpressionClasses::Id latestStore = storeOps.front().expression;
for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
if (it->sequenceNumber < expr.sequenceNumber)
latestStore = it->expression;
addDependencies(latestStore);
m_neededBy.insert(make_pair(latestStore, _c));
}
}
} }
int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced)
{ {
// do some cleanup
removeStackTopIfPossible();
if (m_classPositions.count(_c)) if (m_classPositions.count(_c))
{ {
assertThrow( assertThrow(
@ -296,7 +386,13 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
); );
return m_classPositions[_c]; return m_classPositions[_c];
} }
ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments; ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c);
assertThrow(
_allowSequenced || expr.sequenceNumber == 0,
OptimizerException,
"Sequence constrained operation requested out of sequence."
);
ExpressionClasses::Ids const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);
@ -307,42 +403,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
if (arguments.size() == 1) if (arguments.size() == 1)
{ {
if (canBeRemoved(arguments[0], _c)) if (canBeRemoved(arguments[0], _c))
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
else else
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
else if (arguments.size() == 2) else if (arguments.size() == 2)
{ {
if (canBeRemoved(arguments[1], _c)) if (canBeRemoved(arguments[1], _c))
{ {
appendSwapOrRemove(generateClassElement(arguments[1])); appendOrRemoveSwap(classElementPosition(arguments[1]));
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
appendDup(m_stackHeight); appendDup(m_stackHeight);
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
} }
else else
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
else else
{ {
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
{ {
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
appendDup(m_stackHeight); appendDup(m_stackHeight);
} }
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
appendDup(generateClassElement(arguments[1])); appendDup(classElementPosition(arguments[1]));
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
} }
else else
{ {
appendDup(generateClassElement(arguments[1])); appendDup(classElementPosition(arguments[1]));
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
} }
} }
@ -355,21 +451,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
for (size_t i = 0; i < arguments.size(); ++i) for (size_t i = 0; i < arguments.size(); ++i)
assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." );
AssemblyItem const& item = *m_expressionClasses.representative(_c).item; while (SemanticInformation::isCommutativeOperation(*expr.item) &&
while (SemanticInformation::isCommutativeOperation(item) &&
!m_generatedItems.empty() && !m_generatedItems.empty() &&
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
// this will not append a swap but remove the one that is already there // this will not append a swap but remove the one that is already there
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
for (auto arg: arguments) for (auto arg: arguments)
if (canBeRemoved(arg, _c)) if (canBeRemoved(arg, _c))
m_classPositions[arg] = c_invalidPosition; m_classPositions[arg] = c_invalidPosition;
for (size_t i = 0; i < arguments.size(); ++i) for (size_t i = 0; i < arguments.size(); ++i)
m_stack.erase(m_stackHeight - i); m_stack.erase(m_stackHeight - i);
appendItem(*m_expressionClasses.representative(_c).item); appendItem(*expr.item);
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
{
m_stack[m_stackHeight] = _c; m_stack[m_stackHeight] = _c;
return m_classPositions[_c] = m_stackHeight; return m_classPositions[_c] = m_stackHeight;
} }
else
{
assertThrow(
instructionInfo(expr.item->instruction()).ret == 0,
OptimizerException,
"Invalid number of return values."
);
return m_classPositions[_c] = c_invalidPosition;
}
}
int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
{
assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
OptimizerException,
"Element requested but is not present."
);
return m_classPositions.at(_id);
}
bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result) bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
{ {
@ -401,22 +518,23 @@ bool CSECodeGenerator::removeStackTopIfPossible()
void CSECodeGenerator::appendDup(int _fromPosition) void CSECodeGenerator::appendDup(int _fromPosition)
{ {
int nr = 1 + m_stackHeight - _fromPosition; assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
m_stackHeight++; appendItem(AssemblyItem(dupInstruction(instructionNum)));
m_stack[m_stackHeight] = m_stack[_fromPosition]; m_stack[m_stackHeight] = m_stack[_fromPosition];
} }
void CSECodeGenerator::appendSwapOrRemove(int _fromPosition) void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition)
{ {
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
if (_fromPosition == m_stackHeight) if (_fromPosition == m_stackHeight)
return; return;
int nr = m_stackHeight - _fromPosition; int instructionNum = m_stackHeight - _fromPosition;
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); appendItem(AssemblyItem(swapInstruction(instructionNum)));
// The value of a class can be present in multiple locations on the stack. We only update the // The value of a class can be present in multiple locations on the stack. We only update the
// "canonical" one that is tracked by m_classPositions // "canonical" one that is tracked by m_classPositions
if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight)

99
libevmcore/CommonSubexpressionEliminator.h

@ -25,10 +25,13 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <tuple>
#include <ostream> #include <ostream>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libevmcore/ExpressionClasses.h> #include <libevmcore/ExpressionClasses.h>
#include <libevmcore/SemanticInformation.h>
namespace dev namespace dev
{ {
@ -44,9 +47,9 @@ using AssemblyItems = std::vector<AssemblyItem>;
* known to be equal only once. * known to be equal only once.
* *
* The general workings are that for each assembly item that is fed into the eliminator, an * The general workings are that for each assembly item that is fed into the eliminator, an
* equivalence class is derived from the operation and the equivalence class of its arguments and * equivalence class is derived from the operation and the equivalence class of its arguments.
* it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic * DUPi, SWAPi and some arithmetic instructions are used to infer equivalences while these
* instructions are used to infer equivalences while these classes are determined. * classes are determined.
* *
* When the list of optimized items is requested, they are generated in a bottom-up fashion, * When the list of optimized items is requested, they are generated in a bottom-up fashion,
* adding code for equivalence classes that were not yet computed. * adding code for equivalence classes that were not yet computed.
@ -54,6 +57,21 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator class CommonSubexpressionEliminator
{ {
public: public:
struct StoreOperation
{
enum Target { Memory, Storage };
StoreOperation(
Target _target,
ExpressionClasses::Id _slot,
unsigned _sequenceNumber,
ExpressionClasses::Id _expression
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
Target target;
ExpressionClasses::Id slot;
unsigned sequenceNumber;
ExpressionClasses::Id expression;
};
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
/// item that must be fed into a new instance of the eliminator. /// item that must be fed into a new instance of the eliminator.
template <class _AssemblyItemIterator> template <class _AssemblyItemIterator>
@ -65,13 +83,16 @@ public:
/// Streams debugging information to @a _out. /// Streams debugging information to @a _out.
std::ostream& stream( std::ostream& stream(
std::ostream& _out, std::ostream& _out,
std::map<int, ExpressionClasses::Id> _currentStack = std::map<int, ExpressionClasses::Id>(), std::map<int, ExpressionClasses::Id> _initialStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>() std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>()
) const; ) const;
private: private:
/// Feeds the item into the system for analysis. /// Feeds the item into the system for analysis.
void feedItem(AssemblyItem const& _item); void feedItem(AssemblyItem const& _item, bool _copyItem = false);
/// Tries to optimize the item that breaks the basic block at the end.
void optimizeBreakingItem();
/// Simplifies the given item using /// Simplifies the given item using
/// Assigns a new equivalence class to the next sequence number of the given stack element. /// Assigns a new equivalence class to the next sequence number of the given stack element.
@ -85,26 +106,38 @@ private:
/// (must not be positive). /// (must not be positive).
ExpressionClasses::Id initialStackElement(int _stackHeight); ExpressionClasses::Id initialStackElement(int _stackHeight);
/// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot.
void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
/// Retrieves the current value at the given slot in storage or creates a new special sload class.
ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot);
/// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot.
void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
/// Retrieves the current value at the given slot in memory or creates a new special mload class.
ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot);
/// Current stack height, can be negative. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class /// Current stack layout, mapping stack height -> equivalence class
std::map<int, ExpressionClasses::Id> m_stackElements; std::map<int, ExpressionClasses::Id> m_stackElements;
/// Current sequence number, this is incremented with each modification to storage or memory.
unsigned m_sequenceNumber = 1;
/// Knowledge about storage content.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_storageContent;
/// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_memoryContent;
/// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction.
std::vector<StoreOperation> m_storeOperations;
/// Structure containing the classes of equivalent expressions. /// Structure containing the classes of equivalent expressions.
ExpressionClasses m_expressionClasses; ExpressionClasses m_expressionClasses;
};
/** /// The item that breaks the basic block, can be nullptr.
* Helper functions to provide context-independent information about assembly items. /// It is usually appended to the block but can be optimized in some cases.
*/ AssemblyItem const* m_breakingItem = nullptr;
struct SemanticInformation
{
/// @returns true if the given items starts a new basic block
static bool breaksBasicBlock(AssemblyItem const& _item);
/// @returns true if the item is a two-argument operation whose value does not depend on the
/// order of its arguments.
static bool isCommutativeOperation(AssemblyItem const& _item);
static bool isDupInstruction(AssemblyItem const& _item);
static bool isSwapInstruction(AssemblyItem const& _item);
}; };
/** /**
@ -114,14 +147,16 @@ struct SemanticInformation
class CSECodeGenerator class CSECodeGenerator
{ {
public: public:
CSECodeGenerator(ExpressionClasses const& _expressionClasses): using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
m_expressionClasses(_expressionClasses) using StoreOperations = std::vector<StoreOperation>;
{}
/// Initializes the code generator with the given classes and store operations.
/// The store operations have to be sorted by sequence number in ascending order.
CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations);
/// @returns the assembly items generated from the given requirements /// @returns the assembly items generated from the given requirements
/// @param _initialStack current contents of the stack (up to stack height of zero) /// @param _initialStack current contents of the stack (up to stack height of zero)
/// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @param _equivalenceClasses equivalence classes as expressions of how to compute them
/// @note should only be called once on each object. /// @note should only be called once on each object.
AssemblyItems generateCode( AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack, std::map<int, ExpressionClasses::Id> const& _initialStack,
@ -133,8 +168,13 @@ private:
void addDependencies(ExpressionClasses::Id _c); void addDependencies(ExpressionClasses::Id _c);
/// Produce code that generates the given element if it is not yet present. /// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element. /// @returns the stack position of the element or c_invalidPosition if it does not actually
int generateClassElement(ExpressionClasses::Id _c); /// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed
int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false);
/// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not on the stack.
int classElementPosition(ExpressionClasses::Id _id) const;
/// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result.
bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1)); bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1));
@ -146,7 +186,7 @@ private:
void appendDup(int _fromPosition); void appendDup(int _fromPosition);
/// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position.
/// @note this might also remove the last item if it exactly the same swap instruction. /// @note this might also remove the last item if it exactly the same swap instruction.
void appendSwapOrRemove(int _fromPosition); void appendOrRemoveSwap(int _fromPosition);
/// Appends the given assembly item. /// Appends the given assembly item.
void appendItem(AssemblyItem const& _item); void appendItem(AssemblyItem const& _item);
@ -163,7 +203,10 @@ private:
std::map<ExpressionClasses::Id, int> m_classPositions; std::map<ExpressionClasses::Id, int> m_classPositions;
/// The actual eqivalence class items and how to compute them. /// The actual eqivalence class items and how to compute them.
ExpressionClasses const& m_expressionClasses; ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number.
std::map<std::pair<StoreOperation::Target, ExpressionClasses::Id>, StoreOperations> m_storeOperations;
/// The set of equivalence classes that should be present on the stack at the end. /// The set of equivalence classes that should be present on the stack at the end.
std::set<ExpressionClasses::Id> m_finalClasses; std::set<ExpressionClasses::Id> m_finalClasses;
}; };
@ -174,8 +217,10 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
_AssemblyItemIterator _end _AssemblyItemIterator _end
) )
{ {
for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator)
feedItem(*_iterator); feedItem(*_iterator);
if (_iterator != _end)
m_breakingItem = &(*_iterator++);
return _iterator; return _iterator;
} }

94
libevmcore/ExpressionClasses.cpp

@ -39,41 +39,71 @@ bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression cons
{ {
auto type = item->type(); auto type = item->type();
auto otherType = _other.item->type(); auto otherType = _other.item->type();
return std::tie(type, item->data(), arguments) < return std::tie(type, item->data(), arguments, sequenceNumber) <
std::tie(otherType, _other.item->data(), _other.arguments); std::tie(otherType, _other.item->data(), _other.arguments, _other.sequenceNumber);
} }
ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) ExpressionClasses::Id ExpressionClasses::find(
AssemblyItem const& _item,
Ids const& _arguments,
bool _copyItem,
unsigned _sequenceNumber
)
{ {
Expression exp; Expression exp;
exp.id = Id(-1); exp.id = Id(-1);
exp.item = &_item; exp.item = &_item;
exp.arguments = _arguments; exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isCommutativeOperation(_item)) if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end()); sort(exp.arguments.begin(), exp.arguments.end());
//@todo store all class members (not only the representatives) in an efficient data structure to search here auto it = m_expressions.find(exp);
for (Expression const& e: m_representatives) if (it != m_expressions.end())
if (!(e < exp || exp < e)) return it->id;
return e.id;
if (SemanticInformation::isDupInstruction(_item)) if (_copyItem)
{ {
// Special item that refers to values pre-existing on the stack
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item)); m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item));
exp.item = m_spareAssemblyItem.back().get(); exp.item = m_spareAssemblyItem.back().get();
} }
ExpressionClasses::Id id = tryToSimplify(exp); ExpressionClasses::Id id = tryToSimplify(exp);
if (id < m_representatives.size()) if (id < m_representatives.size())
return id; exp.id = id;
else
{
exp.id = m_representatives.size(); exp.id = m_representatives.size();
m_representatives.push_back(exp); m_representatives.push_back(exp);
}
m_expressions.insert(exp);
return exp.id; return exp.id;
} }
bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is a non-zero constant.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
return constant.matches(representative(difference), *this) && constant.d() != u256(0);
}
bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
if (!constant.matches(representative(difference), *this))
return false;
// forbidden interval is ["-31", 31]
return constant.d() + 31 > u256(62);
}
string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
{ {
Expression const& expr = representative(_id); Expression const& expr = representative(_id);
@ -189,27 +219,46 @@ Rules::Rules()
// Moving constants to the outside, order matters here! // Moving constants to the outside, order matters here!
// we need actions that return expressions (or patterns?) here, and we need also reversed rules // we need actions that return expressions (or patterns?) here, and we need also reversed rules
// (X+A)+B -> X+(A+B) // (X+A)+B -> X+(A+B)
m_rules.push_back({ m_rules += vector<pair<Pattern, function<Pattern()>>>{{
{op, {{op, {X, A}}, B}}, {op, {{op, {X, A}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
}); }, {
// X+(Y+A) -> (X+Y)+A // X+(Y+A) -> (X+Y)+A
m_rules.push_back({
{op, {{op, {X, A}}, Y}}, {op, {{op, {X, A}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
}); }, {
// For now, we still need explicit commutativity for the inner pattern // For now, we still need explicit commutativity for the inner pattern
m_rules.push_back({
{op, {{op, {A, X}}, B}}, {op, {{op, {A, X}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
}); }, {
m_rules.push_back({
{op, {{op, {A, X}}, Y}}, {op, {{op, {A, X}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
}); }};
}
// move constants across subtractions
m_rules += vector<pair<Pattern, function<Pattern()>>>{
{
// X - A -> X + (-A)
{Instruction::SUB, {X, A}},
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; }
}, {
// (X + A) - Y -> (X - Y) + A
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
}, {
// (A + X) - Y -> (X - Y) + A
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
}, {
// X - (Y + A) -> (X - Y) + (-A)
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
}, {
// X - (A + Y) -> (X - Y) + (-A)
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
}
}; };
//@todo: (x+8)-3 and other things
} }
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun)
@ -254,8 +303,7 @@ ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate co
Ids arguments; Ids arguments;
for (ExpressionTemplate const& t: _template.arguments) for (ExpressionTemplate const& t: _template.arguments)
arguments.push_back(rebuildExpression(t)); arguments.push_back(rebuildExpression(t));
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_template.item)); return find(_template.item, arguments);
return find(*m_spareAssemblyItem.back(), arguments);
} }

20
libevmcore/ExpressionClasses.h

@ -52,17 +52,33 @@ public:
Id id; Id id;
AssemblyItem const* item; AssemblyItem const* item;
Ids arguments; Ids arguments;
unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions.
/// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber).
bool operator<(Expression const& _other) const; bool operator<(Expression const& _other) const;
}; };
/// Retrieves the id of the expression equivalence class resulting from the given item applied to the /// Retrieves the id of the expression equivalence class resulting from the given item applied to the
/// given classes, might also create a new one. /// given classes, might also create a new one.
Id find(AssemblyItem const& _item, Ids const& _arguments = {}); /// @param _copyItem if true, copies the assembly item to an internal storage instead of just
/// keeping a pointer.
/// The @a _sequenceNumber indicates the current storage or memory access sequence.
Id find(
AssemblyItem const& _item,
Ids const& _arguments = {},
bool _copyItem = true,
unsigned _sequenceNumber = 0
);
/// @returns the canonical representative of an expression class. /// @returns the canonical representative of an expression class.
Expression const& representative(Id _id) const { return m_representatives.at(_id); } Expression const& representative(Id _id) const { return m_representatives.at(_id); }
/// @returns the number of classes. /// @returns the number of classes.
Id size() const { return m_representatives.size(); } Id size() const { return m_representatives.size(); }
/// @returns true if the values of the given classes are known to be different (on every input).
/// @note that this function might still return false for some different inputs.
bool knownToBeDifferent(Id _a, Id _b);
/// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32.
bool knownToBeDifferentBy32(Id _a, Id _b);
std::string fullDAGToString(Id _id) const; std::string fullDAGToString(Id _id) const;
private: private:
@ -78,6 +94,8 @@ private:
/// Expression equivalence class representatives - we only store one item of an equivalence. /// Expression equivalence class representatives - we only store one item of an equivalence.
std::vector<Expression> m_representatives; std::vector<Expression> m_representatives;
/// All expression ever encountered.
std::set<Expression> m_expressions;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem; std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
}; };

107
libevmcore/SemanticInformation.cpp

@ -0,0 +1,107 @@
/*
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 SemanticInformation.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Helper to provide semantic information about assembly items.
*/
#include <libevmcore/SemanticInformation.h>
#include <libevmcore/AssemblyItem.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
{
switch (_item.type())
{
default:
case UndefinedItem:
case Tag:
return true;
case Push:
case PushString:
case PushTag:
case PushSub:
case PushSubSize:
case PushProgramSize:
case PushData:
return false;
case Operation:
{
if (isSwapInstruction(_item) || isDupInstruction(_item))
return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
if (_item.instruction() == Instruction::SHA3)
return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content.
InstructionInfo info = instructionInfo(_item.instruction());
if (_item.instruction() == Instruction::SSTORE)
return false;
if (_item.instruction() == Instruction::MSTORE)
return false;
//@todo: We do not handle the following memory instructions for now:
// calldatacopy, codecopy, extcodecopy, mstore8,
// msize (note that msize also depends on memory read access)
// the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2;
}
}
}
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
switch (_item.instruction())
{
case Instruction::ADD:
case Instruction::MUL:
case Instruction::EQ:
case Instruction::AND:
case Instruction::OR:
case Instruction::XOR:
return true;
default:
return false;
}
}
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16;
}
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
}
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
{
return _item == AssemblyItem(Instruction::JUMP) || _item == AssemblyItem(Instruction::JUMPI);
}

50
libevmcore/SemanticInformation.h

@ -0,0 +1,50 @@
/*
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 SemanticInformation.h
* @author Christian <c@ethdev.com>
* @date 2015
* Helper to provide semantic information about assembly items.
*/
#pragma once
namespace dev
{
namespace eth
{
class AssemblyItem;
/**
* Helper functions to provide context-independent information about assembly items.
*/
struct SemanticInformation
{
/// @returns true if the given items starts a new block for common subexpression analysis.
static bool breaksCSEAnalysisBlock(AssemblyItem const& _item);
/// @returns true if the item is a two-argument operation whose value does not depend on the
/// order of its arguments.
static bool isCommutativeOperation(AssemblyItem const& _item);
static bool isDupInstruction(AssemblyItem const& _item);
static bool isSwapInstruction(AssemblyItem const& _item);
static bool isJumpInstruction(AssemblyItem const& _item);
};
}
}

49
libp2p/Host.cpp

@ -678,9 +678,6 @@ void Host::disconnectLatePeers()
bytes Host::saveNetwork() const bytes Host::saveNetwork() const
{ {
if (!m_nodeTable)
return bytes();
std::list<Peer> peers; std::list<Peer> peers;
{ {
RecursiveGuard l(x_sessions); RecursiveGuard l(x_sessions);
@ -692,29 +689,24 @@ bytes Host::saveNetwork() const
RLPStream network; RLPStream network;
int count = 0; int count = 0;
{
RecursiveGuard l(x_sessions);
for (auto const& p: peers) for (auto const& p: peers)
{ {
// TODO: alpha: Figure out why it ever shares these ports.//p.address.port() >= 30300 && p.address.port() <= 30305 &&
// TODO: alpha: if/how to save private addresses
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address // Only save peers which have connected within 2 days, with properly-advertised port and public IP address
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.peerEndpoint().port() > 0 && p.peerEndpoint().port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(p.endpoint.tcp.address())) // todo: e2e ipv6 support
bi::tcp::endpoint endpoint(p.peerEndpoint());
if (!endpoint.address().is_v4())
continue;
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && endpoint.port() > 0 && endpoint.port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(endpoint.address()))
{ {
network.appendList(10); network.appendList(10);
if (p.peerEndpoint().address().is_v4()) network << endpoint.port() << p.id << p.required
network << p.peerEndpoint().address().to_v4().to_bytes();
else
network << p.peerEndpoint().address().to_v6().to_bytes();
// TODO: alpha: replace 0 with trust-state of node
network << p.peerEndpoint().port() << p.id << 0
<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count() << chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count() << chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating; << p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
count++; count++;
} }
} }
}
if (!!m_nodeTable) if (!!m_nodeTable)
{ {
@ -731,10 +723,13 @@ bytes Host::saveNetwork() const
count++; count++;
} }
} }
// else: TODO: use previous configuration if available
RLPStream ret(3); RLPStream ret(3);
ret << dev::p2p::c_protocolVersion << m_alias.secret(); ret << dev::p2p::c_protocolVersion << m_alias.secret();
ret.appendList(count).appendRaw(network.out(), count); ret.appendList(count);
if (!!count)
ret.appendRaw(network.out(), count);
return ret.out(); return ret.out();
} }
@ -757,22 +752,14 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2]) for (auto i: r[2])
{ {
// todo: e2e ipv6 support
// bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
if (i[0].itemCount() != 4)
continue;
bi::tcp::endpoint tcp; bi::tcp::endpoint tcp;
bi::udp::endpoint udp; bi::udp::endpoint udp;
if (i[0].itemCount() == 4)
{
tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>()); tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>()); udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
}
else
{
tcp = bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
}
// skip private addresses
// todo: to support private addresseses entries must be stored
// and managed externally by host rather than nodetable.
if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address())) if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address()))
continue; continue;
@ -783,6 +770,7 @@ void Host::restoreNetwork(bytesConstRef _b)
{ {
shared_ptr<Peer> p = make_shared<Peer>(); shared_ptr<Peer> p = make_shared<Peer>();
p->id = id; p->id = id;
p->required = i[3].toInt<bool>();
p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>())); p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>()));
p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>())); p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
p->m_failedAttempts = i[6].toInt<unsigned>(); p->m_failedAttempts = i[6].toInt<unsigned>();
@ -792,6 +780,9 @@ void Host::restoreNetwork(bytesConstRef _b)
p->endpoint.tcp = tcp; p->endpoint.tcp = tcp;
p->endpoint.udp = udp; p->endpoint.udp = udp;
m_peers[p->id] = p; m_peers[p->id] = p;
if (p->required)
requirePeer(p->id, p->endpoint.udp.address(), p->endpoint.udp.port());
else
m_nodeTable->addNode(*p.get()); m_nodeTable->addNode(*p.get());
} }
} }
@ -801,7 +792,7 @@ void Host::restoreNetwork(bytesConstRef _b)
KeyPair Host::networkAlias(bytesConstRef _b) KeyPair Host::networkAlias(bytesConstRef _b)
{ {
RLP r(_b); RLP r(_b);
if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<int>() == 1) if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion)
return move(KeyPair(move(Secret(r[1].toBytes())))); return move(KeyPair(move(Secret(r[1].toBytes()))));
else else
return move(KeyPair::create()); return move(KeyPair::create());

16
libsolidity/AST.cpp

@ -337,8 +337,14 @@ void FunctionDefinition::checkTypeRequirements()
{ {
if (!var->getType()->canLiveOutsideStorage()) if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
if (!(var->getType()->externalType()) && getVisibility() >= Visibility::Public) if (getVisibility() >= Visibility::Public && !(var->getType()->externalType()))
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); {
// todo delete when will be implemented arrays as parameter type in internal functions
if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array)
BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions."));
else
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
}
} }
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers) for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements(isConstructor() ? modifier->checkTypeRequirements(isConstructor() ?
@ -379,7 +385,11 @@ void VariableDeclaration::checkTypeRequirements()
m_value->expectType(*m_type); m_value->expectType(*m_type);
if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public)
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables."));
} else
if (!FunctionType(*this).externalType())
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
}
else
{ {
// no type declared and no previous assignment, infer the type // no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements(); m_value->checkTypeRequirements();

17
libsolidity/Types.cpp

@ -1103,11 +1103,18 @@ TypePointer FunctionType::externalType() const
TypePointers paramTypes; TypePointers paramTypes;
TypePointers retParamTypes; TypePointers retParamTypes;
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) for (auto type: m_parameterTypes)
paramTypes.push_back((*it)->externalType()); {
for (auto it = m_returnParameterTypes.cbegin(); it != m_returnParameterTypes.cend(); ++it) if (!type->externalType())
retParamTypes.push_back((*it)->externalType()); return TypePointer();
paramTypes.push_back(type->externalType());
}
for (auto type: m_returnParameterTypes)
{
if (!type->externalType())
return TypePointer();
retParamTypes.push_back(type->externalType());
}
return make_shared<FunctionType>(paramTypes, retParamTypes, m_location, m_arbitraryParameters); return make_shared<FunctionType>(paramTypes, retParamTypes, m_location, m_arbitraryParameters);
} }

2
libsolidity/Types.h

@ -512,6 +512,8 @@ public:
virtual Category getCategory() const override { return Category::Function; } virtual Category getCategory() const override { return Category::Function; }
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function.
/// Returns an empty shared pointer if one of the input/return parameters does not have an externaltype.
virtual TypePointer externalType() const override; virtual TypePointer externalType() const override;
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);

3
libtestutils/CMakeLists.txt

@ -32,7 +32,10 @@ endif()
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} ethereum)
if (JSONRPC)
target_link_libraries(${EXECUTABLE} web3jsonrpc) target_link_libraries(${EXECUTABLE} web3jsonrpc)
endif()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -402,7 +402,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const&
{ {
try try
{ {
return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)), 1); return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)));
} }
catch (...) catch (...)
{ {

21
mix/CodeModel.cpp

@ -93,7 +93,22 @@ private:
bool m_functionScope; bool m_functionScope;
uint m_storageSlot; uint m_storageSlot;
}; };
dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _locations, dev::solidity::ContractDefinition const& _contract, QHash<LocationPair, QString> _functions)
{
dev::eth::AssemblyItems result;
result.reserve(_locations.size());
for (dev::eth::AssemblyItem item : _locations)
{
dev::SourceLocation const& l = item.getLocation();
if (_contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end)))
item.setLocation(dev::SourceLocation(-1, -1, l.sourceName));
result.push_back(item);
} }
return result;
}
} //namespace
void BackgroundWorker::queueCodeChange(int _jobId) void BackgroundWorker::queueCodeChange(int _jobId)
{ {
@ -105,13 +120,11 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
m_sourceHash(qHash(_source)) m_sourceHash(qHash(_source))
{ {
std::string name = _contractName.toStdString(); std::string name = _contractName.toStdString();
auto const& contractDefinition = _compiler.getContractDefinition(name); ContractDefinition const& contractDefinition = _compiler.getContractDefinition(name);
m_contract.reset(new QContractDefinition(nullptr, &contractDefinition)); m_contract.reset(new QContractDefinition(nullptr, &contractDefinition));
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership); QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership);
m_contract->moveToThread(QApplication::instance()->thread()); m_contract->moveToThread(QApplication::instance()->thread());
m_bytes = _compiler.getBytecode(_contractName.toStdString()); m_bytes = _compiler.getBytecode(_contractName.toStdString());
m_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
dev::solidity::InterfaceHandler interfaceHandler; dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition)); m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
@ -122,6 +135,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage); CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage);
contractDefinition.accept(visitor); contractDefinition.accept(visitor);
m_assemblyItems = filterLocations(_compiler.getRuntimeAssemblyItems(name), contractDefinition, m_functions);
m_constructorAssemblyItems = filterLocations(_compiler.getAssemblyItems(name), contractDefinition, m_functions);
} }
QString CompiledContract::codeHex() const QString CompiledContract::codeHex() const

1
mix/qml.qrc

@ -61,5 +61,6 @@
<file>qml/js/TransactionHelper.js</file> <file>qml/js/TransactionHelper.js</file>
<file>qml/main.qml</file> <file>qml/main.qml</file>
<file>qml/qmldir</file> <file>qml/qmldir</file>
<file>qml/StatesComboBox.qml</file>
</qresource> </qresource>
</RCC> </RCC>

1
mix/qml/CodeEditorView.qml

@ -11,6 +11,7 @@ Item {
signal breakpointsChanged(string documentId) signal breakpointsChanged(string documentId)
signal isCleanChanged(var isClean, string documentId) signal isCleanChanged(var isClean, string documentId)
function getDocumentText(documentId) { function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) { for (var i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).documentId === documentId) { if (editorListModel.get(i).documentId === documentId) {

27
mix/qml/Debugger.qml

@ -219,6 +219,33 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
id: jumpButtons id: jumpButtons
spacing: 3 spacing: 3
StepActionImage
{
id: playAction
enabledStateImg: "qrc:/qml/img/play_button.png"
disableStateImg: "qrc:/qml/img/play_button.png"
onClicked: projectModel.stateListModel.runState(transactionLog.selectedStateIndex)
width: 30
height: 30
buttonShortcut: "Ctrl+Shift+F8"
buttonTooltip: qsTr("Start Debugging")
visible: true
}
StepActionImage
{
id: pauseAction
enabledStateImg: "qrc:/qml/img/stop_button2x.png"
disableStateImg: "qrc:/qml/img/stop_button2x.png"
onClicked: Debugger.init(null);
width: 30
height: 30
buttonShortcut: "Ctrl+Shift+F9"
buttonTooltip: qsTr("Stop Debugging")
visible: true
}
StepActionImage StepActionImage
{ {
id: runBackAction; id: runBackAction;

556
mix/qml/LogsPane.qml

@ -7,81 +7,258 @@ import "."
Rectangle Rectangle
{ {
property variant currentStatus;
function push(_level, _type, _content) function push(_level, _type, _content)
{ {
_content = _content.replace(/\n/g, " ") _content = _content.replace(/\n/g, " ")
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level });
}
onVisibleChanged:
{
if (visible && (logsModel.count === 0 || (logsModel.get(0).date !== currentStatus.date && logsModel.get(0).content !== currentStatus.content)))
logsModel.insert(0, { "type": currentStatus.type, "date": currentStatus.date, "content": currentStatus.content, "level": currentStatus.level });
else if (!visible)
{
for (var k = 0; k < logsModel.count; k++)
{
if (logsModel.get(k).type === "Comp") //do not keep compilation logs.
logsModel.remove(k);
}
}
} }
anchors.fill: parent anchors.fill: parent
radius: 5 radius: 10
color: LogsPaneStyle.generic.layout.backgroundColor color: "transparent"
border.color: LogsPaneStyle.generic.layout.borderColor id: logsPane
border.width: LogsPaneStyle.generic.layout.borderWidth Column {
ColumnLayout {
z: 2 z: 2
height: parent.height - rowAction.height
width: parent.width
spacing: 0
ListModel {
id: logsModel
}
ScrollView
{
id: scrollView
height: parent.height height: parent.height
width: parent.width width: parent.width
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
Column
{
id: logsRect
spacing: 0 spacing: 0
Row Repeater {
id: logsRepeater
clip: true
property string frontColor: "transparent"
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state", "comp"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
}
function search(_value)
{ {
id: rowAction filterContent = _value;
Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight }
height: LogsPaneStyle.generic.layout.headerHeight
anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin function toogleFilter(_value)
anchors.left: parent.left
spacing: LogsPaneStyle.generic.layout.headerButtonSpacing
Button
{ {
height: LogsPaneStyle.generic.layout.headerButtonHeight var count = roles.length;
anchors.verticalCenter: parent.verticalCenter for (var i in roles)
action: clearAction {
iconSource: "qrc:/qml/img/broom.png" if (roles[i] === _value)
{
roles.splice(i, 1);
break;
}
} }
if (count === roles.length)
roles.push(_value);
Action { filterType = regEx(proxyModel.roles);
id: clearAction
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsModel.clear()
} }
function regEx(_value)
{
return "(?:" + roles.join('|') + ")";
} }
Button filterType: "(?:javascript|run|state|comp)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
}
Rectangle
{ {
height: LogsPaneStyle.generic.layout.headerButtonHeight width: LogsPaneStyle.generic.layout.dateWidth + LogsPaneStyle.generic.layout.contentWidth + LogsPaneStyle.generic.layout.typeWidth
height: 30
color:
{
var cl;
if (level === "warning" || level === "error")
cl = LogsPaneStyle.generic.layout.errorColor;
else
cl = index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor;
if (index === 0)
logsRepeater.frontColor = cl;
return cl;
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (logContent.elide === Text.ElideNone)
{
logContent.elide = Text.ElideRight;
logContent.wrapMode = Text.NoWrap
parent.height = 30;
}
else
{
logContent.elide = Text.ElideNone;
logContent.wrapMode = Text.WordWrap;
parent.height = logContent.lineCount * 30;
}
}
}
DefaultLabel {
text: date;
font.family: LogsPaneStyle.generic.layout.logLabelFont
width: LogsPaneStyle.generic.layout.dateWidth
font.pointSize: Style.absoluteSize(-1)
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
action: copytoClipBoardAction color: {
iconSource: "qrc:/qml/img/copy.png" parent.getColor(level);
}
} }
Action { DefaultLabel {
id: copytoClipBoardAction text: type;
enabled: logsModel.count > 0 font.family: LogsPaneStyle.generic.layout.logLabelFont
tooltip: qsTr("Copy to Clipboard") width: LogsPaneStyle.generic.layout.typeWidth
onTriggered: { font.pointSize: Style.absoluteSize(-1)
var content = ""; anchors.left: parent.left
for (var k = 0; k < logsModel.count; k++) anchors.leftMargin: 100
anchors.verticalCenter: parent.verticalCenter
color: {
parent.getColor(level);
}
}
Text {
id: logContent
text: content;
font.family: LogsPaneStyle.generic.layout.logLabelFont
width: LogsPaneStyle.generic.layout.contentWidth
font.pointSize: Style.absoluteSize(-1)
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
anchors.left: parent.left
anchors.leftMargin: 230
color: {
parent.getColor(level);
}
}
function getColor()
{ {
var log = logsModel.get(k); if (level === "error")
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; return "red";
else if (level === "warning")
return "orange";
else
return "#808080";
} }
clipboard.text = content; }
}
}
}
Component {
id: itemDelegate
DefaultLabel {
text: styleData.value;
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-1)
color: {
if (proxyModel.get(styleData.row).level === "error")
return "red";
else if (proxyModel.get(styleData.row).level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
Rectangle
{
gradient: Gradient {
GradientStop { position: 0.0; color: "#f1f1f1" }
GradientStop { position: 1.0; color: "#d9d7da" }
}
Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight
height: LogsPaneStyle.generic.layout.headerHeight
width: logsPane.width
anchors.bottom: parent.bottom
Row
{
id: rowAction
anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin
anchors.left: parent.left
spacing: LogsPaneStyle.generic.layout.headerButtonSpacing
height: parent.height
Rectangle
{
color: "transparent"
height: parent.height
width: 40
DefaultLabel
{
anchors.verticalCenter: parent.verticalCenter
color: LogsPaneStyle.generic.layout.logLabelColor
font.pointSize: Style.absoluteSize(-3)
font.family: LogsPaneStyle.generic.layout.logLabelFont
text: qsTr("Show:")
} }
} }
Rectangle { Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 1; width: 1;
height: parent.height - 10 height: parent.height
color : "#808080" color: LogsPaneStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2
} }
ToolButton { ToolButton {
id: javascriptButton id: javascriptButton
checkable: true checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight height: LogsPaneStyle.generic.layout.headerButtonHeight
width: 20
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: true checked: true
onCheckedChanged: { onCheckedChanged: {
@ -97,16 +274,35 @@ Rectangle
font.pointSize: Style.absoluteSize(-3) font.pointSize: Style.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor color: LogsPaneStyle.generic.layout.logLabelColor
anchors.centerIn: parent anchors.centerIn: parent
text: qsTr("JavaScript") text: qsTr("JS")
} }
} }
background:
Rectangle {
color: javascriptButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent"
}
} }
} }
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2
}
ToolButton { ToolButton {
id: runButton id: runButton
checkable: true checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight height: LogsPaneStyle.generic.layout.headerButtonHeight
width: 30
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: true checked: true
onCheckedChanged: { onCheckedChanged: {
@ -125,7 +321,25 @@ Rectangle
text: qsTr("Run") text: qsTr("Run")
} }
} }
background:
Rectangle {
color: runButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent"
}
}
} }
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2
} }
ToolButton { ToolButton {
@ -133,6 +347,7 @@ Rectangle
checkable: true checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 35
checked: true checked: true
onCheckedChanged: { onCheckedChanged: {
proxyModel.toogleFilter("state") proxyModel.toogleFilter("state")
@ -145,133 +360,226 @@ Rectangle
DefaultLabel { DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3) font.pointSize: Style.absoluteSize(-3)
color: "#5391d8" color: LogsPaneStyle.generic.layout.logLabelColor
anchors.centerIn: parent anchors.centerIn: parent
text: qsTr("State") text: qsTr("State")
} }
} }
background:
Rectangle {
color: stateButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent"
}
} }
} }
DefaultTextField Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: LogsPaneStyle.generic.layout.buttonSeparatorColor2
}
}
Row
{
height: parent.height
anchors.right: parent.right
anchors.rightMargin: 10
spacing: 10
Rectangle
{ {
id: searchBox
height: LogsPaneStyle.generic.layout.headerButtonHeight height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: LogsPaneStyle.generic.layout.headerInputWidth color: "transparent"
font.family: LogsPaneStyle.generic.layout.logLabelFont width: 20
font.pointSize: Style.absoluteSize(-3) Button
font.italic: true {
onTextChanged: { id: clearButton
proxyModel.search(text); action: clearAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: LogsPaneStyle.generic.layout.headerButtonHeight
implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight
color: "transparent"
} }
} }
} }
ListModel { Image {
id: logsModel id: clearImage
source: clearAction.enabled ? "qrc:/qml/img/cleariconactive.png" : "qrc:/qml/img/clearicon.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
} }
TableView { Action {
id: logsTable id: clearAction
clip: true enabled: logsModel.count > 0
Layout.fillWidth: true tooltip: qsTr("Clear")
Layout.preferredHeight: parent.height - rowAction.height onTriggered: {
headerVisible : false logsModel.clear();
onDoubleClicked: }
{ }
var log = logsModel.get(logsTable.currentRow);
if (log)
clipboard.text = (log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content);
} }
model: SortFilterProxyModel { Rectangle
id: proxyModel {
source: logsModel height: LogsPaneStyle.generic.layout.headerButtonHeight
property var roles: ["-", "javascript", "run", "state"] anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
id: copyButton
action: copyAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: LogsPaneStyle.generic.layout.headerButtonHeight
implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight
color: "transparent"
}
}
}
Component.onCompleted: { Image {
filterType = regEx(proxyModel.roles); id: copyImage
source: copyAction.enabled ? "qrc:/qml/img/copyiconactive.png" : "qrc:/qml/img/copyicon.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
} }
function search(_value) Action {
id: copyAction
enabled: logsModel.count > 0
tooltip: qsTr("Copy to Clipboard")
onTriggered: {
var content = "";
for (var k = 0; k < logsModel.count; k++)
{ {
filterContent = _value; var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
}
clipboard.text = content;
}
}
} }
function toogleFilter(_value) Rectangle
{
var count = roles.length;
for (var i in roles)
{ {
if (roles[i] === _value) width: 120
radius: 10
height: 25
color: "white"
anchors.verticalCenter: parent.verticalCenter
Image
{ {
roles.splice(i, 1); id: searchImg
break; source: "qrc:/qml/img/searchicon.png"
fillMode: Image.PreserveAspectFit
width: 20
height: 25
z: 3
} }
}
if (count === roles.length)
roles.push(_value);
filterType = regEx(proxyModel.roles); DefaultTextField
{
id: searchBox
z: 2
width: 100
anchors.left: searchImg.right
anchors.leftMargin: -7
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
font.italic: true
text: qsTr(" - Search - ")
onFocusChanged:
{
if (!focus && text === "")
text = qsTr(" - Search - ");
else if (focus && text === qsTr(" - Search - "))
text = "";
} }
function regEx(_value) onTextChanged: {
{ if (text === qsTr(" - Search - "))
return "(?:" + roles.join('|') + ")"; proxyModel.search("");
else
proxyModel.search(text);
} }
filterType: "(?:javascript|run|state)" style:
filterContent: "" TextFieldStyle {
filterSyntax: SortFilterProxyModel.RegExp background: Rectangle {
filterCaseSensitivity: Qt.CaseInsensitive radius: 10
} }
TableViewColumn
{
role: "date"
title: qsTr("date")
width: LogsPaneStyle.generic.layout.dateWidth
delegate: itemDelegate
} }
TableViewColumn
{
role: "type"
title: qsTr("type")
width: LogsPaneStyle.generic.layout.typeWidth
delegate: itemDelegate
} }
TableViewColumn
{
role: "content"
title: qsTr("content")
width: LogsPaneStyle.generic.layout.contentWidth
delegate: itemDelegate
} }
rowDelegate: Item { Rectangle
{
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
id: hideButton
action: hideAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle { Rectangle {
width: logsTable.width - 4 height: LogsPaneStyle.generic.layout.headerButtonHeight
height: 17 implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight
color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor color: "transparent"
} }
} }
} }
Component { Image {
id: itemDelegate id: hideImage
DefaultLabel { source: "qrc:/qml/img/exit.png"
text: styleData.value; anchors.centerIn: parent
font.family: LogsPaneStyle.generic.layout.logLabelFont fillMode: Image.PreserveAspectFit
font.pointSize: Style.absoluteSize(-1) width: 20
color: { height: 25
if (proxyModel.get(styleData.row).level === "error") }
return "red";
else if (proxyModel.get(styleData.row).level === "warning") Action {
return "orange"; id: hideAction
else tooltip: qsTr("Exit")
return "#808080"; onTriggered: {
logsPane.parent.toggle();
} }
} }
} }
} }
} }
}

18
mix/qml/LogsPaneStyle.qml

@ -11,19 +11,21 @@ QtObject {
property QtObject generic: QtObject { property QtObject generic: QtObject {
property QtObject layout: QtObject { property QtObject layout: QtObject {
property string backgroundColor: "#f7f7f7" property string backgroundColor: "#f7f7f7"
property string borderColor: "#5391d8" property int headerHeight: 30
property int borderWidth: 1 property int headerButtonSpacing: 0
property int headerHeight: 35
property int headerButtonSpacing: 5
property int leftMargin: 10 property int leftMargin: 10
property int headerButtonHeight: 30 property int headerButtonHeight: 30
property string logLabelColor: "#5391d8" property string logLabelColor: "#4a4a4a"
property string logLabelFont: "sans serif" property string logLabelFont: "sans serif"
property int headerInputWidth: 200 property int headerInputWidth: 200
property int dateWidth: 150 property int dateWidth: 150
property int typeWidth: 80 property int typeWidth: 150
property int contentWidth: 700 property int contentWidth: 560
property string logAlternateColor: "#f0f0f0" property string logAlternateColor: "#f6f5f6"
property string errorColor: "#fffcd5"
property string buttonSeparatorColor1: "#d3d0d0"
property string buttonSeparatorColor2: "#f2f1f2"
property string buttonSelected: "#dcdcdc"
} }
} }
} }

1
mix/qml/ProjectModel.qml

@ -64,6 +64,7 @@ Item {
function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); } function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); }
function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); } function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); }
function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); } function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); }
function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); } function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); } function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(false); } function deployProject() { ProjectModelCode.deployProject(false); }

260
mix/qml/StatesComboBox.qml

@ -0,0 +1,260 @@
/*
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 StatesComboBox.qml
* @author Ali Mashatan ali@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.0
Rectangle {
id: statesComboBox
width: 200
height: 23
Component.onCompleted: {
var top = dropDownList
while (top.parent) {
top = top.parent
if (top.objectName == "debugPanel")
break
}
var coordinates = dropDownList.mapToItem(top, 0, 0)
//the order is important
dropDownShowdowList.parent = top
dropDownList.parent = top
dropDownShowdowList.x = coordinates.x
dropDownShowdowList.y = coordinates.y
dropDownList.x = coordinates.x
dropDownList.y = coordinates.y
}
signal selectItem(real item)
signal editItem(real item)
signal selectCreate
property int rowHeight: 25
property variant items
property alias selectedItem: chosenItemText.text
property alias selectedIndex: listView.currentRow
function setSelectedIndex(index) {
listView.currentRow = index
chosenItemText.text = statesComboBox.items.get(index).title
}
signal comboClicked
property variant colorItem
property variant colorSelect
SourceSansProRegular
{
id: regularFont
}
SourceSansProBold
{
id: boldFont
}
smooth: true
Rectangle {
id: chosenItem
width: parent.width
height: statesComboBox.height
color: statesComboBox.color
Text {
id: chosenItemText
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
color: statesComboBox.colorItem
text: ""
font.family: regularFont.name
}
MouseArea {
anchors.fill: parent
onClicked: {
statesComboBox.state = statesComboBox.state === "dropDown" ? "" : "dropDown"
}
}
}
Rectangle {
id: dropDownShowdowList
width: statesComboBox.width
opacity: 0.3
height: 0
clip: true
radius: 4
anchors.top: chosenItem.top
anchors.margins: 2
color: "gray"
}
//ToDo: We need scrollbar for items
Rectangle {
id: dropDownList
width: statesComboBox.width
height: 0
clip: true
radius: 4
anchors.top: chosenItem.top
anchors.topMargin: 23
color: statesComboBox.color
ColumnLayout {
spacing: 2
TableView {
id: listView
height: 20
implicitHeight: 0
width: statesComboBox.width
model: statesComboBox.items
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
currentRow: -1
headerVisible: false
backgroundVisible: false
alternatingRowColors: false
frameVisible: false
TableViewColumn {
role: "title"
title: ""
width: statesComboBox.width
delegate: mainItemDelegate
}
rowDelegate: Rectangle {
width: statesComboBox.width
height: statesComboBox.rowHeight
}
Component {
id: mainItemDelegate
Rectangle {
id: itemDelegate
width: statesComboBox.width
height: statesComboBox.height
Text {
id: textItemid
text: styleData.value
color: statesComboBox.colorItem
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 10
anchors.topMargin: 5
font.family: regularFont.name
}
Image {
id: imageItemid
height: 20
width: 20
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 5
visible: false
fillMode: Image.PreserveAspectFit
source: "img/edit_combox.png"
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
imageItemid.visible = true
textItemid.color = statesComboBox.colorSelect
}
onExited: {
imageItemid.visible = false
textItemid.color = statesComboBox.colorItem
}
onClicked: {
if (mouseX > imageItemid.x
&& mouseX < imageItemid.x + imageItemid.width
&& mouseY > imageItemid.y
&& mouseY < imageItemid.y + imageItemid.height)
statesComboBox.editItem(styleData.row)
else {
statesComboBox.state = ""
var prevSelection = chosenItemText.text
chosenItemText.text = styleData.value
listView.currentRow = styleData.row
statesComboBox.selectItem(styleData.row)
}
}
}
} //Item
} //Component
} //Table View
RowLayout {
anchors.top: listView.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: 10
Text {
id: createStateText
width: statesComboBox.width
height: statesComboBox.height
font.family: boldFont.name
color: "#808080"
text: qsTr("Create State ...")
font.weight: Font.DemiBold
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
createStateText.color = statesComboBox.colorSelect
}
onExited: {
createStateText.color = statesComboBox.colorItem
}
onClicked: {
statesComboBox.state = ""
statesComboBox.selectCreate()
}
}
}
}
}
}
states: State {
name: "dropDown"
PropertyChanges {
target: dropDownList
height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1))
}
PropertyChanges {
target: dropDownShowdowList
width: statesComboBox.width + 3
height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + 3
}
PropertyChanges {
target: listView
height: 20
implicitHeight: (statesComboBox.rowHeight * (statesComboBox.items.count))
}
}
}

61
mix/qml/StatusPane.qml

@ -9,7 +9,7 @@ Rectangle {
id: statusHeader id: statusHeader
objectName: "statusPane" objectName: "statusPane"
property variant webPreview property variant webPreview
property alias currentStatus: logPane.currentStatus
function updateStatus(message) function updateStatus(message)
{ {
if (!message) if (!message)
@ -17,6 +17,7 @@ Rectangle {
status.state = ""; status.state = "";
status.text = qsTr("Compile successfully."); status.text = qsTr("Compile successfully.");
debugImg.state = "active"; debugImg.state = "active";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "info" }
} }
else else
{ {
@ -24,6 +25,7 @@ Rectangle {
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
debugImg.state = ""; debugImg.state = "";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" }
} }
debugRunActionIcon.enabled = codeModel.hasContract; debugRunActionIcon.enabled = codeModel.hasContract;
} }
@ -33,6 +35,7 @@ Rectangle {
status.state = ""; status.state = "";
status.text = text status.text = text
logPane.push("info", type, text); logPane.push("info", type, text);
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "info" }
} }
function warningMessage(text, type) function warningMessage(text, type)
@ -40,13 +43,15 @@ Rectangle {
status.state = "warning"; status.state = "warning";
status.text = text status.text = text
logPane.push("warning", type, text); logPane.push("warning", type, text);
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "warning" }
} }
function errorMessage(text, type) function errorMessage(text, type)
{ {
status.state = "error"; status.state = "error";
status.text = text status.text = text;
logPane.push("error", type, text); logPane.push("error", type, text);
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" }
} }
Connections { Connections {
@ -76,7 +81,7 @@ Rectangle {
function format(_message) function format(_message)
{ {
var formatted = _message.match(/(?:<dev::eth::)(.+)(?:>)/); var formatted = _message.match(/(?:<dev::eth::)(.+)(?:>)/);
if (formatted) if (!formatted)
formatted = _message.match(/(?:<dev::)(.+)(?:>)/); formatted = _message.match(/(?:<dev::)(.+)(?:>)/);
if (formatted && formatted.length > 1) if (formatted && formatted.length > 1)
formatted = formatted[1]; formatted = formatted[1];
@ -90,6 +95,7 @@ Rectangle {
return formatted; return formatted;
} }
} }
Connections { Connections {
target:projectModel target:projectModel
onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment"); onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment");
@ -111,32 +117,13 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: 3 radius: 3
width: 500 width: 600
height: 30 height: 30
color: "#fcfbfc" color: "#fcfbfc"
states: [
State {
name: "logsOpened"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 1
}
},
State {
name: "logsClosed"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 0
}
}
]
Text { Text {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: StatusPaneStyle.general.statusFontSize font.pointSize: Style.absoluteSize(-1)
height: 15 height: 15
font.family: "sans serif" font.family: "sans serif"
objectName: "status" objectName: "status"
@ -215,40 +202,39 @@ Rectangle {
{ {
if (logsContainer.state === "opened") if (logsContainer.state === "opened")
{ {
statusContainer.state = "logsClosed";
logsContainer.state = "closed" logsContainer.state = "closed"
} }
else else
{ {
statusContainer.state = "logsOpened";
logsContainer.state = "opened"; logsContainer.state = "opened";
logsContainer.focus = true; logsContainer.focus = true;
forceActiveFocus(); forceActiveFocus();
calCoord();
} }
} }
id: logsContainer id: logsContainer
width: 1000 width: 750
height: 0
anchors.topMargin: 10
anchors.top: statusContainer.bottom anchors.top: statusContainer.bottom
anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 4
visible: false visible: false
radius: 5 radius: 10
Component.onCompleted:
function calCoord()
{ {
var top = logsContainer; var top = logsContainer;
while (top.parent) while (top.parent)
top = top.parent top = top.parent
var coordinates = logsContainer.mapToItem(top, 0, 0) var coordinates = logsContainer.mapToItem(top, 0, 0);
logsContainer.parent = top; logsContainer.parent = top;
logsContainer.x = coordinates.x logsContainer.x = status.x + statusContainer.x - LogsPaneStyle.generic.layout.dateWidth - LogsPaneStyle.generic.layout.typeWidth + 70
logsContainer.y = coordinates.y
} }
LogsPane LogsPane
{ {
id: logPane id: logPane;
} }
states: [ states: [
State { State {
name: "opened"; name: "opened";
@ -257,11 +243,12 @@ Rectangle {
State { State {
name: "closed"; name: "closed";
PropertyChanges { target: logsContainer; height: 0; visible: false } PropertyChanges { target: logsContainer; height: 0; visible: false }
PropertyChanges { target: statusContainer; width: 600; height: 30 }
} }
] ]
transitions: Transition { transitions: Transition {
NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 } NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 }
NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 } NumberAnimation { target: logsContainer; properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 }
} }
} }
} }

59
mix/qml/TransactionLog.qml

@ -6,30 +6,16 @@ import QtQuick.Layouts 1.1
import org.ethereum.qml.RecordLogEntry 1.0 import org.ethereum.qml.RecordLogEntry 1.0
Item { Item {
property ListModel fullModel: ListModel{} property ListModel fullModel: ListModel{}
property ListModel transactionModel: ListModel{} property ListModel transactionModel: ListModel{}
property ListModel callModel: ListModel{} property ListModel callModel: ListModel{}
property int selectedStateIndex: statesCombo.selectedIndex
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 { ColumnLayout {
anchors.fill: parent anchors.fill: parent
RowLayout { RowLayout {
anchors.right: parent.right
anchors.left: parent.left
Connections Connections
{ {
id: compilationStatus id: compilationStatus
@ -61,34 +47,22 @@ Item {
} }
} }
ComboBox { StatesComboBox
{
id: statesCombo id: statesCombo
model: projectModel.stateListModel items: projectModel.stateListModel
width: 150 onSelectCreate: projectModel.stateListModel.addState();
editable: false onEditItem: projectModel.stateListModel.editState(item)
textRole: "title" colorItem: "#808080"
onActivated: { colorSelect: "#4a90e2"
model.runState(index); color: "white"
}
Connections { Connections {
target: projectModel.stateListModel target: projectModel.stateListModel
onStateRun: { onStateRun: {
if (statesCombo.currentIndex !== index) if (statesCombo.selectedIndex !== index)
statesCombo.currentIndex = index; statesCombo.setSelectedIndex( index );
}
}
} }
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: editStateAction
} }
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: addStateAction
} }
Button Button
{ {
@ -163,16 +137,11 @@ Item {
Keys.onPressed: { Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow); var item = logTable.model.get(currentRow);
clipboard.text = item.returned; appContext.toClipboard(item.returned);
} }
} }
} }
Rectangle {
height: 6
color: "transparent"
} }
}
Connections { Connections {
target: clientModel target: clientModel
onStateCleared: { onStateCleared: {

7
mix/qml/WebPreview.qml

@ -155,18 +155,19 @@ Item {
//document request //document request
if (urlPath === "/") if (urlPath === "/")
urlPath = "/index.html"; urlPath = "/index.html";
var documentId = urlPath.substr(urlPath.lastIndexOf("/") + 1); var documentName = urlPath.substr(urlPath.lastIndexOf("/") + 1);
var documentId = projectModel.getDocumentIdByName(documentName);
var content = ""; var content = "";
if (projectModel.codeEditor.isDocumentOpen(documentId)) if (projectModel.codeEditor.isDocumentOpen(documentId))
content = projectModel.codeEditor.getDocumentText(documentId); content = projectModel.codeEditor.getDocumentText(documentId);
else else
{ {
var doc = projectModel.getDocument(documentId); var doc = projectModel.getDocument(documentId);
if (doc !== undefined) if (doc)
content = fileIo.readFile(doc.path); content = fileIo.readFile(doc.path);
} }
if (documentId === urlInput.text.replace(httpServer.url + "/", "")) { if (documentName === urlInput.text.replace(httpServer.url + "/", "")) {
//root page, inject deployment script //root page, inject deployment script
content = "<script>web3=parent.web3;contracts=parent.contracts;</script>\n" + content; content = "<script>web3=parent.web3;contracts=parent.contracts;</script>\n" + content;
_request.setResponseContentType("text/html"); _request.setResponseContentType("text/html");

41
mix/qml/html/codeeditor.js

@ -22,17 +22,11 @@ editor.on("change", function(eMirror, object) {
}); });
var mac = /Mac/.test(navigator.platform); var mac = /Mac/.test(navigator.platform);
var extraKeys = {};
if (mac === true) { if (mac === true) {
editor.setOption("extraKeys", { extraKeys["Cmd-V"] = function(cm) { cm.replaceSelection(clipboard); };
"Cmd-V": function(cm) { extraKeys["Cmd-X"] = function(cm) { window.document.execCommand("cut"); };
cm.replaceSelection(clipboard); extraKeys["Cmd-C"] = function(cm) { window.document.execCommand("copy"); };
},
"Cmd-X": function(cm) {
window.document.execCommand("cut");
},
"Cmd-C": function(cm) {
window.document.execCommand("copy");
}});
} }
makeMarker = function() { makeMarker = function() {
@ -102,16 +96,14 @@ setMode = function(mode) {
if (mode === "javascript") if (mode === "javascript")
{ {
ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]}); ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]});
editor.setOption("extraKeys", { extraKeys["Ctrl-Space"] = function(cm) { ternServer.complete(cm); };
"Ctrl-Space": function(cm) { ternServer.complete(cm); }, extraKeys["Ctrl-I"] = function(cm) { ternServer.showType(cm); };
"Ctrl-I": function(cm) { ternServer.showType(cm); }, extraKeys["Ctrl-O"] = function(cm) { ternServer.showDocs(cm); };
"Ctrl-O": function(cm) { ternServer.showDocs(cm); }, extraKeys["Alt-."] = function(cm) { ternServer.jumpToDef(cm); };
"Alt-.": function(cm) { ternServer.jumpToDef(cm); }, extraKeys["Alt-,"] = function(cm) { ternServer.jumpBack(cm); };
"Alt-,": function(cm) { ternServer.jumpBack(cm); }, extraKeys["Ctrl-Q"] = function(cm) { ternServer.rename(cm); };
"Ctrl-Q": function(cm) { ternServer.rename(cm); }, extraKeys["Ctrl-."] = function(cm) { ternServer.selectName(cm); };
"Ctrl-.": function(cm) { ternServer.selectName(cm); }, extraKeys["'.'"] = function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; };
"'.'": function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; }
})
editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); }); editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); });
} }
else if (mode === "solidity") else if (mode === "solidity")
@ -119,10 +111,9 @@ setMode = function(mode) {
CodeMirror.commands.autocomplete = function(cm) { CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.hint.anyword); CodeMirror.showHint(cm, CodeMirror.hint.anyword);
} }
editor.setOption("extraKeys", { extraKeys["Ctrl-Space"] = "autocomplete";
"Ctrl-Space": "autocomplete"
})
} }
editor.setOption("extraKeys", extraKeys);
}; };
setClipboardBase64 = function(text) { setClipboardBase64 = function(text) {
@ -133,8 +124,6 @@ var executionMark;
highlightExecution = function(start, end) { highlightExecution = function(start, end) {
if (executionMark) if (executionMark)
executionMark.clear(); executionMark.clear();
if (start === 0 && end + 1 === editor.getValue().length)
return; // Do not hightlight the whole document.
if (debugWarning) if (debugWarning)
debugWarning.clear(); debugWarning.clear();
executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" }); executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" });
@ -199,3 +188,5 @@ compilationComplete = function()
} }
compilationCompleteBool = true; compilationCompleteBool = true;
} }
editor.setOption("extraKeys", extraKeys);

BIN
mix/qml/img/broom.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

BIN
mix/qml/img/clearicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
mix/qml/img/cleariconactive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
mix/qml/img/copyicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

BIN
mix/qml/img/copyiconactive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

BIN
mix/qml/img/edit_combox.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

BIN
mix/qml/img/pause_button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

BIN
mix/qml/img/pause_button2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

BIN
mix/qml/img/play_button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

BIN
mix/qml/img/play_button2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

BIN
mix/qml/img/searchicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
mix/qml/img/stop_button2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

10
mix/qml/js/ProjectModel.js

@ -172,7 +172,7 @@ function getDocumentIndex(documentId)
for (var i = 0; i < projectListModel.count; i++) for (var i = 0; i < projectListModel.count; i++)
if (projectListModel.get(i).documentId === documentId) if (projectListModel.get(i).documentId === documentId)
return i; return i;
console.error("Cant find document " + documentId); console.error("Can't find document " + documentId);
return -1; return -1;
} }
@ -291,6 +291,14 @@ function getDocument(documentId) {
return projectListModel.get(i); return projectListModel.get(i);
} }
function getDocumentIdByName(fileName)
{
for (var i = 0; i < projectListModel.count; i++)
if (projectListModel.get(i).fileName === fileName)
return projectListModel.get(i).documentId;
return null;
}
function removeDocument(documentId) { function removeDocument(documentId) {
var i = getDocumentIndex(documentId); var i = getDocumentIndex(documentId);
var document = projectListModel.get(i); var document = projectListModel.get(i);

12
mix/res.qrc

@ -17,7 +17,6 @@
<file>qml/fonts/SourceSerifPro-Semibold.ttf</file> <file>qml/fonts/SourceSerifPro-Semibold.ttf</file>
<file>qml/img/available_updates.png</file> <file>qml/img/available_updates.png</file>
<file>qml/img/b64.png</file> <file>qml/img/b64.png</file>
<file>qml/img/broom.png</file>
<file>qml/img/bugiconactive.png</file> <file>qml/img/bugiconactive.png</file>
<file>qml/img/bugiconinactive.png</file> <file>qml/img/bugiconinactive.png</file>
<file>qml/img/closedtriangleindicator.png</file> <file>qml/img/closedtriangleindicator.png</file>
@ -49,9 +48,20 @@
<file>qml/img/projecticon.png</file> <file>qml/img/projecticon.png</file>
<file>qml/img/run.png</file> <file>qml/img/run.png</file>
<file>qml/img/search_filled.png</file> <file>qml/img/search_filled.png</file>
<file>qml/img/play_button2x.png</file>
<file>qml/img/play_button.png</file>
<file>qml/img/pause_button2x.png</file>
<file>qml/img/pause_button.png</file>
<file>res/mix_256x256x32.png</file> <file>res/mix_256x256x32.png</file>
<file>stdc/config.sol</file> <file>stdc/config.sol</file>
<file>stdc/namereg.sol</file> <file>stdc/namereg.sol</file>
<file>stdc/std.sol</file> <file>stdc/std.sol</file>
<file>qml/img/edit_combox.png</file>
<file>qml/img/clearicon.png</file>
<file>qml/img/cleariconactive.png</file>
<file>qml/img/copyicon.png</file>
<file>qml/img/copyiconactive.png</file>
<file>qml/img/searchicon.png</file>
<file>qml/img/stop_button2x.png</file>
</qresource> </qresource>
</RCC> </RCC>

19
mix/style.xml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorCodeStyle>
<!-- Written by QtCreator 3.3.2, 2015-03-31T14:55:07. -->
<qtcreator>
<data>
<variable>CodeStyleData</variable>
<valuemap type="QVariantMap">
<value type="bool" key="AutoSpacesForTabs">false</value>
<value type="int" key="IndentSize">4</value>
<value type="int" key="PaddingMode">2</value>
<value type="bool" key="SpacesForTabs">false</value>
<value type="int" key="TabSize">4</value>
</valuemap>
</data>
<data>
<variable>DisplayName</variable>
<value type="QString">Eth</value>
</data>
</qtcreator>

35
test/ClientBase.cpp

@ -34,34 +34,45 @@ BOOST_AUTO_TEST_CASE(blocks)
{ {
enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void
{ {
for (string const& name: _json["postState"].getMemberNames()) auto compareState = [&_client](Json::Value const& _o, string const& _name, BlockNumber _blockNumber) -> void
{ {
Json::Value o = _json["postState"][name]; Address address(_name);
Address address(name);
// balanceAt // balanceAt
u256 expectedBalance = u256(o["balance"].asString()); u256 expectedBalance = u256(_o["balance"].asString());
u256 balance = _client.balanceAt(address); u256 balance = _client.balanceAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedBalance, balance); ETH_CHECK_EQUAL(expectedBalance, balance);
// countAt // countAt
u256 expectedCount = u256(o["nonce"].asString()); u256 expectedCount = u256(_o["nonce"].asString());
u256 count = _client.countAt(address); u256 count = _client.countAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedCount, count); ETH_CHECK_EQUAL(expectedCount, count);
// stateAt // stateAt
for (string const& pos: o["storage"].getMemberNames()) for (string const& pos: _o["storage"].getMemberNames())
{ {
u256 expectedState = u256(o["storage"][pos].asString()); u256 expectedState = u256(_o["storage"][pos].asString());
u256 state = _client.stateAt(address, u256(pos)); u256 state = _client.stateAt(address, u256(pos), _blockNumber);
ETH_CHECK_EQUAL(expectedState, state); ETH_CHECK_EQUAL(expectedState, state);
} }
// codeAt // codeAt
bytes expectedCode = fromHex(o["code"].asString()); bytes expectedCode = fromHex(_o["code"].asString());
bytes code = _client.codeAt(address); bytes code = _client.codeAt(address, _blockNumber);
ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(), ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(),
code.begin(), code.end()); code.begin(), code.end());
};
for (string const& name: _json["postState"].getMemberNames())
{
Json::Value o = _json["postState"][name];
compareState(o, name, PendingBlock);
}
for (string const& name: _json["pre"].getMemberNames())
{
Json::Value o = _json["pre"][name];
compareState(o, name, 0);
} }
// number // number

14
test/SolidityNameAndTypeResolution.cpp

@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
uint a; uint a;
} }
contract Test { contract Test {
function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg) external returns (uint ret) { function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg, address[] addresses) external returns (uint ret) {
ret = 5; ret = 5;
} }
})"; })";
@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
auto functions = contract->getDefinedFunctions(); auto functions = contract->getDefinedFunctions();
if (functions.empty()) if (functions.empty())
continue; continue;
BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address)", functions[0]->externalSignature()); BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address,address[])", functions[0]->externalSignature());
} }
} }
@ -503,6 +503,16 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
} }
// todo delete when implemented
BOOST_AUTO_TEST_CASE(arrays_in_internal_functions)
{
char const* text = R"(
contract Test {
function foo(address[] addresses) {}
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion)
{ {
char const* text = R"( char const* text = R"(

265
test/SolidityOptimizer.cpp

@ -303,6 +303,271 @@ BOOST_AUTO_TEST_CASE(cse_associativity2)
checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD}); checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD});
} }
BOOST_AUTO_TEST_CASE(cse_storage)
{
AssemblyItems input{
u256(0),
Instruction::SLOAD,
u256(0),
Instruction::SLOAD,
Instruction::ADD,
u256(0),
Instruction::SSTORE
};
checkCSE(input, {
u256(0),
Instruction::DUP1,
Instruction::SLOAD,
Instruction::DUP1,
Instruction::ADD,
Instruction::SWAP1,
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_noninterleaved_storage)
{
// two stores to the same location should be replaced by only one store, even if we
// read in the meantime
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE,
Instruction::DUP1,
Instruction::SLOAD,
u256(8),
Instruction::DUP3,
Instruction::SSTORE
};
checkCSE(input, {
u256(8),
Instruction::DUP2,
Instruction::SSTORE,
u256(7)
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage)
{
// stores and reads to/from two unknown locations, should not optimize away the first store
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE, // store to "DUP1"
Instruction::DUP2,
Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
u256(0),
Instruction::DUP3,
Instruction::SSTORE // store different value to "DUP1"
};
checkCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value)
{
// stores and reads to/from two unknown locations, should not optimize away the first store
// but it should optimize away the second, since we already know the value will be the same
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE, // store to "DUP1"
Instruction::DUP2,
Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
u256(6),
u256(1),
Instruction::ADD,
Instruction::DUP3,
Instruction::SSTORE // store same value to "DUP1"
};
checkCSE(input, {
u256(7),
Instruction::DUP2,
Instruction::SSTORE,
Instruction::DUP2,
Instruction::SLOAD
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location)
{
// stores and reads to/from two known locations, should optimize away the first store,
// because we know that the location is different
AssemblyItems input{
u256(0x70),
u256(1),
Instruction::SSTORE, // store to 1
u256(2),
Instruction::SLOAD, // read from 2, is different from 1
u256(0x90),
u256(1),
Instruction::SSTORE // store different value at 1
};
checkCSE(input, {
u256(2),
Instruction::SLOAD,
u256(0x90),
u256(1),
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset)
{
// stores and reads to/from two locations which are known to be different,
// should optimize away the first store, because we know that the location is different
AssemblyItems input{
u256(0x70),
Instruction::DUP2,
u256(1),
Instruction::ADD,
Instruction::SSTORE, // store to "DUP1"+1
Instruction::DUP1,
u256(2),
Instruction::ADD,
Instruction::SLOAD, // read from "DUP1"+2, is different from "DUP1"+1
u256(0x90),
Instruction::DUP3,
u256(1),
Instruction::ADD,
Instruction::SSTORE // store different value at "DUP1"+1
};
checkCSE(input, {
u256(2),
Instruction::DUP2,
Instruction::ADD,
Instruction::SLOAD,
u256(0x90),
u256(1),
Instruction::DUP4,
Instruction::ADD,
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_memory_at_known_location_offset)
{
// stores and reads to/from two locations which are known to be different,
// should not optimize away the first store, because the location overlaps with the load,
// but it should optimize away the second, because we know that the location is different by 32
AssemblyItems input{
u256(0x50),
Instruction::DUP2,
u256(2),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+2] = 0x50
u256(0x60),
Instruction::DUP2,
u256(32),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x60
Instruction::DUP1,
Instruction::MLOAD, // read from "DUP1"
u256(0x70),
Instruction::DUP3,
u256(32),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x70
u256(0x80),
Instruction::DUP3,
u256(2),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+2] = 0x80
};
// If the actual code changes too much, we could also simply check that the output contains
// exactly 3 MSTORE and exactly 1 MLOAD instruction.
checkCSE(input, {
u256(0x50),
u256(2),
Instruction::DUP3,
Instruction::ADD,
Instruction::SWAP1,
Instruction::DUP2,
Instruction::MSTORE, // ["DUP1"+2] = 0x50
Instruction::DUP2,
Instruction::MLOAD, // read from "DUP1"
u256(0x70),
u256(32),
Instruction::DUP5,
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x70
u256(0x80),
Instruction::SWAP1,
Instruction::SWAP2,
Instruction::MSTORE // ["DUP1"+2] = 0x80
});
}
BOOST_AUTO_TEST_CASE(cse_deep_stack)
{
AssemblyItems input{
Instruction::ADD,
Instruction::SWAP1,
Instruction::POP,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP8,
Instruction::SWAP5,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
};
checkCSE(input, {
Instruction::SWAP4,
Instruction::SWAP12,
Instruction::SWAP3,
Instruction::SWAP11,
Instruction::POP,
Instruction::SWAP1,
Instruction::SWAP3,
Instruction::ADD,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP6,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
});
}
BOOST_AUTO_TEST_CASE(cse_jumpi_no_jump)
{
AssemblyItems input{
u256(0),
u256(1),
Instruction::DUP2,
AssemblyItem(PushTag, 1),
Instruction::JUMPI
};
checkCSE(input, {
u256(0),
u256(1)
});
}
BOOST_AUTO_TEST_CASE(cse_jumpi_jump)
{
AssemblyItems input{
u256(1),
u256(1),
Instruction::DUP2,
AssemblyItem(PushTag, 1),
Instruction::JUMPI
};
checkCSE(input, {
u256(1),
Instruction::DUP1,
AssemblyItem(PushTag, 1),
Instruction::JUMP
});
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

4
test/dagger.cpp

@ -63,8 +63,8 @@ BOOST_AUTO_TEST_CASE(basic_test)
unsigned cacheSize(o["cache_size"].get_int()); unsigned cacheSize(o["cache_size"].get_int());
h256 cacheHash(o["cache_hash"].get_str()); h256 cacheHash(o["cache_hash"].get_str());
BOOST_REQUIRE_EQUAL(Ethasher::get()->cache(header).size(), cacheSize); BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize);
BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->cache(header)), cacheHash); BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->cache(header), cacheSize)), cacheHash);
#if TEST_FULL #if TEST_FULL
unsigned fullSize(o["full_size"].get_int()); unsigned fullSize(o["full_size"].get_int());

9
test/peer.cpp

@ -57,6 +57,15 @@ BOOST_AUTO_TEST_CASE(host)
g_logVerbosity = oldLogVerbosity; g_logVerbosity = oldLogVerbosity;
} }
BOOST_AUTO_TEST_CASE(networkConfig)
{
Host save("Test", NetworkPreferences(false));
bytes store(save.saveNetwork());
Host restore("Test", NetworkPreferences(false), bytesConstRef(&store));
BOOST_REQUIRE(save.id() == restore.id());
}
BOOST_AUTO_TEST_CASE(save_nodes) BOOST_AUTO_TEST_CASE(save_nodes)
{ {
std::list<Host*> hosts; std::list<Host*> hosts;

114
test/stMemoryTestFiller.json

@ -1461,7 +1461,7 @@
} }
}, },
"stackLimitPush32_1024": { "stackLimitPush32_1023": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1495,7 +1495,7 @@
} }
}, },
"stackLimitPush32_1025": { "stackLimitPush32_1024": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1529,7 +1529,41 @@
} }
}, },
"stackLimitPush31_1024": { "stackLimitPush32_1025": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "42949672960",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "(asm 1023 0x00 MSTORE JUMPDEST 0x0102030405060708090a0102030405060708090a0102030405060708090a0102 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "429496729600",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "100000",
"to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"value" : "10",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"stackLimitPush31_1023": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1563,7 +1597,7 @@
} }
}, },
"stackLimitPush31_1025": { "stackLimitPush31_1024": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1597,7 +1631,41 @@
} }
}, },
"stackLimitGas_1024": { "stackLimitPush31_1025": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "42949672960",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "(asm 1023 0x00 MSTORE JUMPDEST 0x0102030405060708090a0102030405060708090a0102030405060708090a01 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "429496729600",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "100000",
"to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"value" : "10",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"stackLimitGas_1023": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1631,7 +1699,7 @@
} }
}, },
"stackLimitGas_1025": { "stackLimitGas_1024": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1665,6 +1733,40 @@
} }
}, },
"stackLimitGas_1025": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "42949672960",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "(asm 1023 0x00 MSTORE JUMPDEST GAS 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "429496729600",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "100000",
"to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"value" : "10",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"mstroe8_dejavu": { "mstroe8_dejavu": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

Loading…
Cancel
Save