From f1d66c148f5310cf19861072fdbae5ed9d1c3d74 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 25 May 2014 02:17:17 +0200 Subject: [PATCH] Macros and definitions in LLL. --- alethzero/MainWin.cpp | 25 +- eth/EthStubServer.cpp | 4 +- libethereum/Instruction.cpp | 1088 ++++------------------------------- libethereum/Instruction.h | 83 ++- libqethereum/QEthereum.cpp | 6 +- test/vm.cpp | 4 +- walleth/MainWin.cpp | 2 - 7 files changed, 212 insertions(+), 1000 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index ab252d154..5b1473d89 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -45,9 +45,6 @@ using eth::Executive; // functions using eth::toHex; -using eth::assemble; -using eth::pushLiteral; -using eth::compileLisp; using eth::compileLLL; using eth::disassemble; using eth::formatBalance; @@ -869,18 +866,20 @@ void Main::on_data_textChanged() m_data = initBytes; if (bodyBytes.size()) { + eth::CodeFragment c(bodyBytes); + unsigned s = bodyBytes.size(); - auto ss = pushLiteral(m_data, s); + unsigned ss = c.appendPush(s); unsigned p = m_data.size() + 4 + 2 + 1 + ss + 2 + 1; - pushLiteral(m_data, p); - pushLiteral(m_data, 0); - m_data.push_back((byte)Instruction::CODECOPY); - pushLiteral(m_data, s); - pushLiteral(m_data, 0); - m_data.push_back((byte)Instruction::RETURN); - while (m_data.size() < p) - m_data.push_back(0); - for (auto b: bodyBytes) + c.appendPush(p); + c.appendPush(0); + c.appendInstruction(Instruction::CODECOPY); + c.appendPush(s); + c.appendPush(0); + c.appendInstruction(Instruction::RETURN); + while (c.size() < p) + c.appendInstruction(Instruction::STOP); + for (auto b: c.code()) m_data.push_back(b); } diff --git a/eth/EthStubServer.cpp b/eth/EthStubServer.cpp index 467001256..4c60afd68 100644 --- a/eth/EthStubServer.cpp +++ b/eth/EthStubServer.cpp @@ -90,9 +90,7 @@ std::string EthStubServer::create(const std::string& _bCode, const std::string& std::string EthStubServer::lll(const std::string& _s) { - bytes ret; - eth::compileLisp(_s, true, ret); - return "0x" + toHex(ret); + return "0x" + toHex(eth::compileLLL(_s)); } std::string EthStubServer::gasPrice() diff --git a/libethereum/Instruction.cpp b/libethereum/Instruction.cpp index 401c977d9..f0f058181 100644 --- a/libethereum/Instruction.cpp +++ b/libethereum/Instruction.cpp @@ -215,872 +215,6 @@ const std::map eth::c_instructionInfo = { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } }; -static string readQuoted(char const*& o_d, char const* _e) -{ - string ret; - bool escaped = 0; - for (++o_d; o_d != _e && (escaped || *o_d != '"'); ++o_d) - if (!escaped && *o_d == '\\') - escaped = true; - else - ret.push_back(*o_d); - if (o_d != _e) - ++o_d; // skip last " - return ret; -} - -static u256 readNumeric(string _v, bool _quiet) -{ - u256 x = 1; - for (auto const& i: units()) - if (boost::algorithm::ends_with(_v, i.second)) - { - _v = _v.substr(0, _v.size() - i.second.size()); - x = i.first; - break; - } - try - { - return x * u256(_v); - } - catch (...) - { - if (!_quiet) - cwarn << "Invalid numeric" << _v; - } - return 0; -} - -bytes eth::assemble(std::string const& _code, bool _quiet) -{ - bytes ret; - map known; - map req; - char const* d = _code.data(); - char const* e = _code.data() + _code.size(); - while (d != e) - { - // skip to next token - for (; d != e && !isalnum(*d) && *d != '_' /*&& *d != ':' && *d != '"'*/; ++d) {} - if (d == e) - break; - -/* if (*d == '"') - { - string s = readQuoted(d, e); - if (s.size() > 32) - { - if (!_quiet) - cwarn << "String literal > 32 characters. Cropping."; - s.resize(32); - } - h256 valHash; - memcpy(valHash.data(), s.data(), s.size()); - memset(valHash.data() + s.size(), 0, 32 - s.size()); - ret.push_back((u256)valHash); - } - else*/ - { - char const* s = d; - for (; d != e && (isalnum(*d) || *d == '_'/* || *d == ':' || *d == '"'*/); ++d) {} - - string t = string(s, d - s); - if (isdigit(t[0])) - ret.push_back((byte)readNumeric(t, _quiet)); -/* else if (t.back() == ':') - known[t.substr(0, t.size() - 1)] = (unsigned)ret.size(); - else - { - auto it = c_instructions.find(boost::algorithm::to_upper_copy(t)); - if (it != c_instructions.end()) - ret.push_back((u256)it->second); - else - { - req[(unsigned)ret.size()] = t; - ret.push_back(0); - } - }*/ - } - } - for (auto i: req) - if (known.count(i.second)) - ret[i.first] = known[i.second]; - else - if (!_quiet) - cwarn << "Unknown assembler token" << i.second << "at address" << i.first; - - return ret; -} - -/// @returns the number of addition bytes required for the PUSH. -static void increaseLocation(bytes& o_code, unsigned _pos, unsigned _inc) -{ - assert(o_code[_pos] == (byte)Instruction::PUSH4); - bytesRef r(&o_code[1 + _pos], 4); - toBigEndian(fromBigEndian(bytesConstRef(&o_code[1 + _pos], 4)) + _inc, r); -} - -static void pushLocation(bytes& o_code, uint32_t _locationValue) -{ - o_code.push_back((byte)Instruction::PUSH4); - o_code.resize(o_code.size() + 4); - bytesRef r(&o_code[o_code.size() - 4], 4); - toBigEndian(_locationValue, r); -} - -unsigned eth::pushLiteral(bytes& o_code, u256 _literalValue) -{ - unsigned br = max(1, bytesRequired(_literalValue)); - o_code.push_back((byte)Instruction::PUSH1 + br - 1); - o_code.resize(o_code.size() + br); - for (unsigned i = 0; i < br; ++i) - { - o_code[o_code.size() - 1 - i] = (byte)(_literalValue & 0xff); - _literalValue >>= 8; - } - return br + 1; -} - -static void appendCode(bytes& o_code, vector& o_locs, bytes _code, vector& _locs) -{ - o_locs.reserve(o_locs.size() + _locs.size()); - for (auto i: _locs) - { - increaseLocation(_code, i, (unsigned)o_code.size()); - o_locs.push_back(i + (unsigned)o_code.size()); - } - o_code.reserve(o_code.size() + _code.size()); - for (auto i: _code) - o_code.push_back(i); -} - -static int compileLispFragment(char const*& d, char const* e, bool _quiet, bytes& o_code, vector& o_locs, map& _vars) -{ - std::map const c_arith = { { "+", Instruction::ADD }, { "-", Instruction::SUB }, { "*", Instruction::MUL }, { "/", Instruction::DIV }, { "%", Instruction::MOD }, { "&", Instruction::AND }, { "|", Instruction::OR }, { "^", Instruction::XOR } }; - std::map> const c_binary = { { "<", { Instruction::LT, false } }, { "<=", { Instruction::GT, true } }, { ">", { Instruction::GT, false } }, { ">=", { Instruction::LT, true } }, { "S<", { Instruction::SLT, false } }, { "S<=", { Instruction::SGT, true } }, { "S>", { Instruction::SGT, false } }, { "S>=", { Instruction::SLT, true } }, { "=", { Instruction::EQ, false } }, { "!=", { Instruction::EQ, true } } }; - std::map const c_unary = { { "!", Instruction::NOT } }; - std::set const c_allowed = { '+', '-', '*', '/', '%', '<', '>', '=', '!', '&', '|', '~' }; - - bool exec = false; - int outs = 0; - bool seq = false; - - while (d != e) - { - // skip to next token - for (; d != e && !isalnum(*d) && *d != '(' && *d != ')' && *d != '{' && *d != '}' && *d != '"' && *d != '@' && *d != '[' && !c_allowed.count(*d) && *d != ';'; ++d) {} - if (d == e) - break; - - switch (*d) - { - case ';': - for (; d != e && *d != '\n'; ++d) {} - break; - case '(': - exec = true; - ++d; - break; - case '{': - ++d; - while (d != e) - { - bytes codes; - vector locs; - outs = 0; - int o; - if ((o = compileLispFragment(d, e, _quiet, codes, locs, _vars)) > -1) - { - for (int i = 0; i < outs; ++i) - o_code.push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on). - outs = o; - appendCode(o_code, o_locs, codes, locs); - } - else - break; - } - seq = true; - break; - case '}': - if (seq) - { - ++d; - return outs; - } - return -1; - case ')': - if (exec) - { - ++d; - return outs; - } - else - // unexpected - return false as we don't know what to do with it. - return -1; - - case '@': - { - if (exec) - return -1; - bool store = false; - ++d; - if (*d == '@') - { - ++d; - store = true; - } - bytes codes; - vector locs; - if (compileLispFragment(d, e, _quiet, codes, locs, _vars) != 1) - return -1; - while (d != e && isspace(*d)) - ++d; - appendCode(o_code, o_locs, codes, locs); - o_code.push_back((byte)(store ? Instruction::SLOAD : Instruction::MLOAD)); - return 1; - } - case '[': - { - if (exec) - return -1; - bool store = false; - ++d; - if (*d == '[') - { - ++d; - store = true; - } - bytes codes; - vector locs; - if (compileLispFragment(d, e, _quiet, codes, locs, _vars) != 1) - return -1; - while (d != e && isspace(*d)) - ++d; - - if (*d != ']') - return -1; - ++d; - if (store) - { - if (*d != ']') - return -1; - ++d; - } - - if (compileLispFragment(d, e, _quiet, o_code, o_locs, _vars) != 1) - return -1; - - appendCode(o_code, o_locs, codes, locs); - o_code.push_back((byte)(store ? Instruction::SSTORE: Instruction::MSTORE)); - return 0; - } - default: - { - bool haveLiteral = false; - u256 literalValue = 0; - string t; - - if (*d == '"') - { - string s = readQuoted(d, e); - if (s.size() > 32) - { - if (!_quiet) - cwarn << "String literal > 32 characters. Cropping."; - s.resize(32); - } - h256 valHash; - memcpy(valHash.data(), s.data(), s.size()); - memset(valHash.data() + s.size(), 0, 32 - s.size()); - literalValue = (u256)valHash; - haveLiteral = true; - } - else - { - char const* s = d; - for (; d != e && (isalnum(*d) || *d == '_' || c_allowed.count(*d)); ++d) {} - t = string(s, d - s); - if (isdigit(t[0])) - { - literalValue = readNumeric(t, _quiet); - haveLiteral = true; - } - } - - if (haveLiteral) - { - bool bareLoad = true; - if (exec) - { - bytes codes; - vector locs; - if (compileLispFragment(d, e, _quiet, codes, locs, _vars) != -1) - { - appendCode(o_code, o_locs, codes, locs); - while (compileLispFragment(d, e, _quiet, codes, locs, _vars) != -1) - if (!_quiet) - cwarn << "Additional items in bare store. Ignoring."; - bareLoad = false; - } - } - pushLiteral(o_code, literalValue); - if (exec) - o_code.push_back(bareLoad ? (byte)Instruction::SLOAD : (byte)Instruction::SSTORE); - outs = bareLoad ? 1 : 0; - } - else - { - boost::algorithm::to_upper(t); - if (t == "IF") - { - // Compile all the code... - bytes codes[4]; - vector locs[4]; - for (int i = 0; i < 3; ++i) - { - int o = compileLispFragment(d, e, _quiet, codes[i], locs[i], _vars); - if (i == 1) - outs = o; - if ((i == 0 && o != 1) || o == -1 || (i == 2 && o != outs)) - return -1; - } - if (compileLispFragment(d, e, _quiet, codes[3], locs[3], _vars) != -1) - return false; - - // First fragment - predicate - appendCode(o_code, o_locs, codes[0], locs[0]); - - // Push the positive location. - unsigned posLocation = (unsigned)o_code.size(); - o_locs.push_back(posLocation); - pushLocation(o_code, 0); - - // Jump to negative if false. - o_code.push_back((byte)Instruction::JUMPI); - - // Second fragment - negative. - appendCode(o_code, o_locs, codes[2], locs[2]); - - // Jump to end after negative. - unsigned endLocation = (unsigned)o_code.size(); - o_locs.push_back(endLocation); - pushLocation(o_code, 0); - o_code.push_back((byte)Instruction::JUMP); - - // Third fragment - positive. - increaseLocation(o_code, posLocation, o_code.size()); - appendCode(o_code, o_locs, codes[1], locs[1]); - - // At end now. - increaseLocation(o_code, endLocation, o_code.size()); - } - else if (t == "WHEN" || t == "UNLESS") - { - outs = 0; - // Compile all the code... - bytes codes[3]; - vector locs[3]; - for (int i = 0; i < 2; ++i) - { - int o = compileLispFragment(d, e, _quiet, codes[i], locs[i], _vars); - if (o == -1 || (i == 0 && o != 1)) - return false; - if (i == 1) - for (int j = 0; j < o; ++j) - codes[i].push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on). - } - if (compileLispFragment(d, e, _quiet, codes[2], locs[2], _vars) != -1) - return false; - - // First fragment - predicate - appendCode(o_code, o_locs, codes[0], locs[0]); - if (t == "WHEN") - o_code.push_back((byte)Instruction::NOT); - - // Push the positive location. - unsigned endLocation = (unsigned)o_code.size(); - o_locs.push_back(endLocation); - pushLocation(o_code, 0); - - // Jump to end... - o_code.push_back((byte)Instruction::JUMPI); - - // Second fragment - negative. - appendCode(o_code, o_locs, codes[1], locs[1]); - - // At end now. - increaseLocation(o_code, endLocation, o_code.size()); - } - else if (t == "WHILE") - { - outs = 0; - // Compile all the code... - bytes codes[3]; - vector locs[3]; - for (int i = 0; i < 2; ++i) - { - int o = compileLispFragment(d, e, _quiet, codes[i], locs[i], _vars); - if (o == -1 || (i == 0 && o != 1)) - return false; - if (i == 1) - for (int j = 0; j < o; ++j) - codes[i].push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on). - } - if (compileLispFragment(d, e, _quiet, codes[2], locs[2], _vars) != -1) - return false; - - unsigned startLocation = (unsigned)o_code.size(); - - // First fragment - predicate - appendCode(o_code, o_locs, codes[0], locs[0]); - o_code.push_back((byte)Instruction::NOT); - - // Push the positive location. - unsigned endInsertion = (unsigned)o_code.size(); - o_locs.push_back(endInsertion); - pushLocation(o_code, 0); - - // Jump to positive if true. - o_code.push_back((byte)Instruction::JUMPI); - - // Second fragment - negative. - appendCode(o_code, o_locs, codes[1], locs[1]); - - // Jump to end after negative. - o_locs.push_back((unsigned)o_code.size()); - pushLocation(o_code, startLocation); - o_code.push_back((byte)Instruction::JUMP); - - // At end now. - increaseLocation(o_code, endInsertion, o_code.size()); - } - else if (t == "FOR") - { - compileLispFragment(d, e, _quiet, o_code, o_locs, _vars); - outs = 0; - // Compile all the code... - bytes codes[4]; - vector locs[4]; - for (int i = 0; i < 3; ++i) - { - int o = compileLispFragment(d, e, _quiet, codes[i], locs[i], _vars); - if (o == -1 || (i == 0 && o != 1)) - return false; - if (i > 0) - for (int j = 0; j < o; ++j) - codes[i].push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on). - } - if (compileLispFragment(d, e, _quiet, codes[3], locs[3], _vars) != -1) - return false; - - unsigned startLocation = (unsigned)o_code.size(); - - // First fragment - predicate - appendCode(o_code, o_locs, codes[0], locs[0]); - o_code.push_back((byte)Instruction::NOT); - - // Push the positive location. - unsigned endInsertion = (unsigned)o_code.size(); - o_locs.push_back(endInsertion); - pushLocation(o_code, 0); - - // Jump to positive if true. - o_code.push_back((byte)Instruction::JUMPI); - - // Second fragment - negative. - appendCode(o_code, o_locs, codes[2], locs[2]); - - // Third fragment - incrementor. - appendCode(o_code, o_locs, codes[1], locs[1]); - - // Jump to beginning afterwards. - o_locs.push_back((unsigned)o_code.size()); - pushLocation(o_code, startLocation); - o_code.push_back((byte)Instruction::JUMP); - - // At end now. - increaseLocation(o_code, endInsertion, o_code.size()); - } - else if (t == "SEQ") - { - while (d != e) - { - bytes codes; - vector locs; - outs = 0; - int o; - if ((o = compileLispFragment(d, e, _quiet, codes, locs, _vars)) > -1) - { - for (int i = 0; i < outs; ++i) - o_code.push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on). - outs = o; - appendCode(o_code, o_locs, codes, locs); - } - else - break; - } - } - /*else if (t == "CALL") - { - if (exec) - { - vector>> codes(1); - int totalArgs = 0; - while (d != e) - { - int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second, _vars); - if (o < 1) - break; - codes.push_back(pair>()); - totalArgs += o; - } - if (totalArgs < 7) - { - cwarn << "Expected at least 7 arguments to CALL; got" << totalArgs << "."; - break; - } - - for (auto it = codes.rbegin(); it != codes.rend(); ++it) - appendCode(o_code, o_locs, it->first, it->second); - o_code.push_back((byte)Instruction::CALL); - outs = 1; - } - }*/ - else if (t == "MULTI") - { - while (d != e) - { - bytes codes; - vector locs; - outs = 0; - int o; - if ((o = compileLispFragment(d, e, _quiet, codes, locs, _vars)) > -1) - { - outs += o; - appendCode(o_code, o_locs, codes, locs); - } - else - break; - } - } - else if (t == "LLL") - { - bytes codes; - vector locs; - map vars; - if (compileLispFragment(d, e, _quiet, codes, locs, _vars) == -1) - return false; - unsigned codeLoc = o_code.size() + 5 + 1; - o_locs.push_back(o_code.size()); - pushLocation(o_code, codeLoc + codes.size()); - o_code.push_back((byte)Instruction::JUMP); - for (auto b: codes) - o_code.push_back(b); - - bytes ncode[1]; - vector nlocs[1]; - if (compileLispFragment(d, e, _quiet, ncode[0], nlocs[0], _vars) != 1) - return false; - - pushLiteral(o_code, codes.size()); - o_code.push_back((byte)Instruction::DUP); - int o = compileLispFragment(d, e, _quiet, o_code, o_locs, _vars); - if (o == 1) - { - o_code.push_back((byte)Instruction::LT); - o_code.push_back((byte)Instruction::NOT); - o_code.push_back((byte)Instruction::MUL); - o_code.push_back((byte)Instruction::DUP); - } - else if (o != -1) - return false; - - o_locs.push_back(o_code.size()); - pushLocation(o_code, codeLoc); - appendCode(o_code, o_locs, ncode[0], nlocs[0]); - o_code.push_back((byte)Instruction::CODECOPY); - outs = 1; - } - else if (t == "&&") - { - vector codes; - vector> locs; - while (d != e) - { - codes.resize(codes.size() + 1); - locs.resize(locs.size() + 1); - int o = compileLispFragment(d, e, _quiet, codes.back(), locs.back(), _vars); - if (o == -1) - break; - if (o != 1) - return false; - } - - // last one is empty. - if (codes.size() < 2) - return false; - - codes.pop_back(); - locs.pop_back(); - - vector ends; - - if (codes.size() > 1) - { - pushLiteral(o_code, 0); - - for (unsigned i = 1; i < codes.size(); ++i) - { - // Check if true - predicate - appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]); - o_code.push_back((byte)Instruction::NOT); - - // Push the false location. - ends.push_back((unsigned)o_code.size()); - o_locs.push_back(ends.back()); - pushLocation(o_code, 0); - - // Jump to end... - o_code.push_back((byte)Instruction::JUMPI); - } - o_code.push_back((byte)Instruction::POP); - } - - // Check if true - predicate - appendCode(o_code, o_locs, codes.back(), locs.back()); - - // At end now. - for (auto i: ends) - increaseLocation(o_code, i, o_code.size()); - outs = 1; - } - else if (t == "~") - { - if (compileLispFragment(d, e, _quiet, o_code, o_locs, _vars) == 1) - { - bytes codes; - vector locs; - if (compileLispFragment(d, e, _quiet, codes, locs, _vars) != -1) - return false; - pushLiteral(o_code, 1); - pushLiteral(o_code, 0); - o_code.push_back((byte)Instruction::SUB); - o_code.push_back((byte)Instruction::SUB); - outs = 1; - } - } - else if (t == "||") - { - vector codes; - vector> locs; - while (d != e) - { - codes.resize(codes.size() + 1); - locs.resize(locs.size() + 1); - { - int o = compileLispFragment(d, e, _quiet, codes.back(), locs.back(), _vars); - if (o == -1) - break; - if (o != 1) - return false; - } - } - - // last one is empty. - if (codes.size() < 2) - return false; - - codes.pop_back(); - locs.pop_back(); - - vector ends; - - if (codes.size() > 1) - { - pushLiteral(o_code, 1); - - for (unsigned i = 1; i < codes.size(); ++i) - { - // Check if true - predicate - appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]); - - // Push the false location. - ends.push_back((unsigned)o_code.size()); - o_locs.push_back(ends.back()); - pushLocation(o_code, 0); - - // Jump to end... - o_code.push_back((byte)Instruction::JUMPI); - } - o_code.push_back((byte)Instruction::POP); - } - - // Check if true - predicate - appendCode(o_code, o_locs, codes.back(), locs.back()); - - // At end now. - for (auto i: ends) - increaseLocation(o_code, i, o_code.size()); - outs = 1; - } - else - { - auto it = c_instructions.find(t); - if (it != c_instructions.end()) - { - if (exec) - { - vector>> codes(1); - int totalArgs = 0; - while (d != e) - { - int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second, _vars); - if (o < 1) - break; - codes.push_back(pair>()); - totalArgs += o; - } - int ea = c_instructionInfo.at(it->second).args; - if ((ea >= 0 && totalArgs != ea) || (ea < 0 && totalArgs < -ea)) - { - cwarn << "Expected " << (ea < 0 ? "at least" : "exactly") << abs(ea) << "arguments to operation" << t << "; got" << totalArgs << "."; - break; - } - - for (auto it = codes.rbegin(); it != codes.rend(); ++it) - appendCode(o_code, o_locs, it->first, it->second); - o_code.push_back((byte)it->second); - outs = c_instructionInfo.at(it->second).ret; - } - else - { - o_code.push_back((byte)Instruction::PUSH1); - o_code.push_back((byte)it->second); - outs = 1; - } - } - else - { - auto it = c_arith.find(t); - if (it != c_arith.end()) - { - vector>> codes(1); - int totalArgs = 0; - while (d != e) - { - int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second, _vars); - if (o < 1) - break; - codes.push_back(pair>()); - totalArgs += o; - } - codes.pop_back(); - if (!totalArgs) - { - cwarn << "Expected at least one argument to operation" << t; - break; - } - for (auto jt = codes.rbegin(); jt != codes.rend(); ++jt) - appendCode(o_code, o_locs, jt->first, jt->second); - o_code.push_back((byte)it->second); - outs = 1; - } - else - { - auto it = c_binary.find(t); - if (it != c_binary.end()) - { - vector>> codes(1); - int totalArgs = 0; - while (d != e) - { - int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second, _vars); - if (o < 1) - break; - codes.push_back(pair>()); - totalArgs += o; - } - codes.pop_back(); -// int i = (int)codes.size(); - if (totalArgs != 2) - { - cwarn << "Expected two arguments to binary operator" << t << "; got" << totalArgs << "."; - break; - } - for (auto jt = codes.rbegin(); jt != codes.rend(); ++jt) - appendCode(o_code, o_locs, jt->first, jt->second); - o_code.push_back((byte)it->second.first); - if (it->second.second) - o_code.push_back((byte)Instruction::NOT); - outs = 1; - } - else - { - auto it = c_unary.find(t); - if (it != c_unary.end()) - { - vector>> codes(1); - int totalArgs = 0; - while (d != e) - { - int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second, _vars); - if (o == -1) - break; - totalArgs += o; - codes.push_back(pair>()); - } - codes.pop_back(); -// int i = (int)codes.size(); - if (totalArgs != 1) - { - cwarn << "Expected one argument to unary operator" << t << "; got" << totalArgs << "."; - break; - } - for (auto it = codes.rbegin(); it != codes.rend(); ++it) - appendCode(o_code, o_locs, it->first, it->second); - o_code.push_back((byte)it->second); - outs = 1; - } - else - { - auto it = _vars.find(t); - if (it == _vars.end()) - { - bool ok; - tie(it, ok) = _vars.insert(make_pair(t, _vars.size() * 32)); - } - pushLiteral(o_code, it->second); - outs = 1; - // happens when it's an actual literal, escapes with -1 :-( - } - } - } - } - } - } - - if (!exec) - return outs; - } - } - } - return -1; -} - -bytes eth::compileLisp(std::string const& _code, bool _quiet, bytes& _init) -{ - char const* d = _code.data(); - char const* e = _code.data() + _code.size(); - bytes body; - vector locs; - map vars; - compileLispFragment(d, e, _quiet, _init, locs, vars); - locs.clear(); - vars.clear(); - compileLispFragment(d, e, _quiet, body, locs, vars); - return body; -} - void killBigints(sp::utree const& _this) { switch (_this.which()) @@ -1125,78 +259,24 @@ void parseLLL(string const& _s, sp::utree& o_out) } } -struct CompileState +namespace eth { - map vars; -}; -class CodeFragment; - -class CodeLocation +struct Macro { - friend class CodeFragment; - -public: - CodeLocation(CodeFragment* _f); - CodeLocation(CodeFragment* _f, unsigned _p): m_f(_f), m_pos(_p) {} - - unsigned get() const; - void increase(unsigned _val); - void set(unsigned _val); - void set(CodeLocation _loc) { assert(_loc.m_f == m_f); set(_loc.m_pos); } - void anchor(); - - CodeLocation operator+(unsigned _i) const { return CodeLocation(m_f, m_pos + _i); } - -private: - CodeFragment* m_f; - unsigned m_pos; + std::vector args; + sp::utree code; }; -class CodeFragment +struct CompilerState { - friend class CodeLocation; - -public: - CodeFragment(sp::utree const& _t, CompileState& _s, bool _allowASM = false); - - bytes const& code() const { return m_code; } - -protected: - unsigned appendPush(u256 _l); - void appendFragment(CodeFragment const& _f); - void appendFragment(CodeFragment const& _f, unsigned _i); - void appendInstruction(Instruction _i); - - CodeLocation appendPushLocation(unsigned _l = 0); - void appendPushLocation(CodeLocation _l) { assert(_l.m_f == this); appendPushLocation(_l.m_pos); } - - CodeLocation appendJump() { auto ret = appendPushLocation(0); appendInstruction(Instruction::JUMP); return ret; } - CodeLocation appendJumpI() { auto ret = appendPushLocation(0); appendInstruction(Instruction::JUMPI); return ret; } - CodeLocation appendJump(CodeLocation _l) { auto ret = appendPushLocation(_l.m_pos); appendInstruction(Instruction::JUMP); return ret; } - CodeLocation appendJumpI(CodeLocation _l) { auto ret = appendPushLocation(_l.m_pos); appendInstruction(Instruction::JUMPI); return ret; } - - void onePath() { assert(!m_totalDeposit && !m_baseDeposit); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; } - void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; } - void donePaths() { donePath(); m_totalDeposit = m_baseDeposit = 0; } - void ignored() { m_baseDeposit = m_deposit; } - void endIgnored() { m_deposit = m_baseDeposit; m_baseDeposit = 0; } - - unsigned size() const { return m_code.size(); } - -private: - template void error() { throw T(); } - void constructOperation(sp::utree const& _t, CompileState& _s); - - void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) error(); } - - int m_deposit = 0; - int m_baseDeposit = 0; - int m_totalDeposit = 0; - bytes m_code; - vector m_locs; + std::map vars; + std::map defs; + std::map macros; }; +} + CodeLocation::CodeLocation(CodeFragment* _f) { m_f = _f; @@ -1317,7 +397,7 @@ void debugOutAST(ostream& _out, sp::utree const& _this) } } -CodeFragment::CodeFragment(sp::utree const& _t, CompileState& _s, bool _allowASM) +CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM) { switch (_t.which()) { @@ -1338,10 +418,10 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompileState& _s, bool _allowASM } case sp::utree_type::symbol_type: { + auto sr = _t.get, sp::utree_type::symbol_type>>(); + string s(sr.begin(), sr.end()); if (_allowASM) { - auto sr = _t.get, sp::utree_type::symbol_type>>(); - string s(sr.begin(), sr.end()); boost::algorithm::to_upper(s); if (c_instructions.count(s)) { @@ -1352,8 +432,11 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompileState& _s, bool _allowASM else error(); } + else if (_s.defs.count(s)) + appendFragment(_s.defs.at(s)); else - error(); + error(); + break; } case sp::utree_type::any_type: @@ -1368,7 +451,9 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompileState& _s, bool _allowASM } } -void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) +// (define plus1 (a) (+ a 1)) + +void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { if (_t.empty()) error(); @@ -1377,34 +462,35 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) else { string s; + string us; switch (_t.tag()) { case 0: { auto sr = _t.front().get, sp::utree_type::symbol_type>>(); s = string(sr.begin(), sr.end()); - boost::algorithm::to_upper(s); + us = boost::algorithm::to_upper_copy(s); break; } case 1: - s = "MLOAD"; + us = "MLOAD"; break; case 2: - s = "SLOAD"; + us = "SLOAD"; break; case 3: - s = "MSTORE"; + us = "MSTORE"; break; case 4: - s = "SSTORE"; + us = "SSTORE"; break; case 5: - s = "SEQ"; + us = "SEQ"; break; default:; } - if (s == "ASM") + if (us == "ASM") { int c = 0; for (auto const& i: _t) @@ -1412,25 +498,80 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendFragment(CodeFragment(i, _s, true)); return; } + + if (us == "MACRO") + { + string n; + unsigned ii = 0; + if (_t.size() != 4) + error(); + for (auto const& i: _t) + { + if (ii == 1) + { + if (i.tag() != 0 || i.which() != sp::utree_type::symbol_type) + error(); + auto sr = i.get, sp::utree_type::symbol_type>>(); + n = string(sr.begin(), sr.end()); + } + else if (ii == 2) + { + for (auto const& j: i) + { + if (j.tag() != 0 || j.which() != sp::utree_type::symbol_type) + error(); + auto sr = j.get, sp::utree_type::symbol_type>>(); + _s.macros[n].args.push_back(string(sr.begin(), sr.end())); + } + } + else if (ii == 3) + _s.macros[n].code = i; + ++ii; + } + return; + } + + if (us == "DEF") + { + string n; + unsigned ii = 0; + if (_t.size() != 3) + error(); + for (auto const& i: _t) + { + if (ii == 1) + { + if (i.tag() != 0 || i.which() != sp::utree_type::symbol_type) + error(); + auto sr = i.get, sp::utree_type::symbol_type>>(); + n = string(sr.begin(), sr.end()); + } + else if (ii == 2) + _s.defs[n] = CodeFragment(i, _s); + ++ii; + } + return; + } + std::map const c_arith = { { "+", Instruction::ADD }, { "-", Instruction::SUB }, { "*", Instruction::MUL }, { "/", Instruction::DIV }, { "%", Instruction::MOD }, { "&", Instruction::AND }, { "|", Instruction::OR }, { "^", Instruction::XOR } }; std::map> const c_binary = { { "<", { Instruction::LT, false } }, { "<=", { Instruction::GT, true } }, { ">", { Instruction::GT, false } }, { ">=", { Instruction::LT, true } }, { "S<", { Instruction::SLT, false } }, { "S<=", { Instruction::SGT, true } }, { "S>", { Instruction::SGT, false } }, { "S>=", { Instruction::SLT, true } }, { "=", { Instruction::EQ, false } }, { "!=", { Instruction::EQ, true } } }; std::map const c_unary = { { "!", Instruction::NOT } }; vector code; - CompileState ns = _s; + CompilerState ns = _s; ns.vars.clear(); int c = _t.tag() ? 1 : 0; for (auto const& i: _t) if (c++) - code.push_back(CodeFragment(i, (s == "LLL" && c == 1) ? ns : _s)); + code.push_back(CodeFragment(i, (us == "LLL" && c == 1) ? ns : _s)); auto requireSize = [&](unsigned s) { if (code.size() != s) error(); }; auto requireMinSize = [&](unsigned s) { if (code.size() < s) error(); }; auto requireMaxSize = [&](unsigned s) { if (code.size() > s) error(); }; auto requireDeposit = [&](unsigned i, int s) { if (code[i].m_deposit != s) error(); }; - if (c_instructions.count(s)) + if (c_instructions.count(us)) { - auto it = c_instructions.find(s); + auto it = c_instructions.find(us); int ea = c_instructionInfo.at(it->second).args; if (ea >= 0) requireSize(ea); @@ -1441,9 +582,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendFragment(code[i - 1], 1); appendInstruction(it->second); } - else if (c_arith.count(s)) + else if (c_arith.count(us)) { - auto it = c_arith.find(s); + auto it = c_arith.find(us); requireMinSize(1); for (unsigned i = code.size(); i; --i) { @@ -1453,9 +594,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) for (unsigned i = 1; i < code.size(); ++i) appendInstruction(it->second); } - else if (c_binary.count(s)) + else if (c_binary.count(us)) { - auto it = c_binary.find(s); + auto it = c_binary.find(us); requireSize(2); requireDeposit(0, 1); requireDeposit(1, 1); @@ -1465,15 +606,15 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) if (it->second.second) appendInstruction(Instruction::NOT); } - else if (c_unary.count(s)) + else if (c_unary.count(us)) { - auto it = c_unary.find(s); + auto it = c_unary.find(us); requireSize(1); requireDeposit(0, 1); appendFragment(code[0], 1); appendInstruction(it->second); } - else if (s == "IF") + else if (us == "IF") { requireSize(3); requireDeposit(0, 1); @@ -1488,12 +629,12 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) donePaths(); end.anchor(); } - else if (s == "WHEN" || s == "UNLESS") + else if (us == "WHEN" || us == "UNLESS") { requireSize(2); requireDeposit(0, 1); appendFragment(code[0]); - if (s == "WHEN") + if (us == "WHEN") appendInstruction(Instruction::NOT); auto end = appendJumpI(); onePath(); @@ -1502,7 +643,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) donePaths(); end.anchor(); } - else if (s == "WHILE") + else if (us == "WHILE") { requireSize(2); requireDeposit(1, 1); @@ -1514,7 +655,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendJump(begin); end.anchor(); } - else if (s == "FOR") + else if (us == "FOR") { requireSize(4); requireDeposit(1, 1); @@ -1528,7 +669,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendJump(begin); end.anchor(); } - else if (s == "FOR") + /*else if (s == "FOR") { requireSize(4); requireDeposit(1, 1); @@ -1541,8 +682,8 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendFragment(code[2], 0); appendJump(begin); end.anchor(); - } - else if (s == "LLL") + }*/ + else if (us == "LLL") { requireMinSize(2); requireMaxSize(3); @@ -1568,7 +709,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendFragment(code[1], 1); appendInstruction(Instruction::CODECOPY); } - else if (s == "&&" || s == "||") + else if (us == "&&" || us == "||") { requireMinSize(1); for (unsigned i = 0; i < code.size(); ++i) @@ -1577,12 +718,12 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) vector ends; if (code.size() > 1) { - appendPush(s == "||" ? 1 : 0); + appendPush(us == "||" ? 1 : 0); for (unsigned i = 1; i < code.size(); ++i) { // Check if true - predicate appendFragment(code[i - 1], 1); - if (s == "&&") + if (us == "&&") appendInstruction(Instruction::NOT); ends.push_back(appendJumpI()); } @@ -1596,7 +737,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) for (auto& i: ends) i.anchor(); } - else if (s == "~") + else if (us == "~") { requireSize(1); requireDeposit(0, 1); @@ -1606,7 +747,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) appendInstruction(Instruction::SUB); appendInstruction(Instruction::SUB); } - else if (s == "SEQ") + else if (us == "SEQ") { unsigned ii = 0; for (auto const& i: code) @@ -1615,6 +756,18 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompileState& _s) else appendFragment(i); } + else if (_s.macros.count(s)) + { + Macro const& m = _s.macros.at(s); + CompilerState cs = _s; + requireSize(m.args.size()); + for (unsigned i = 0; i < m.args.size(); ++i) + { + requireDeposit(i, 1); + cs.defs[m.args[i]] = code[i]; + } + appendFragment(CodeFragment(m.code, cs)); + } else { auto it = _s.vars.find(s); @@ -1635,11 +788,12 @@ bytes eth::compileLLL(string const& _s, vector* _errors) sp::utree o; parseLLL(_s, o); debugOutAST(cerr, o); + cerr << endl; bytes ret; if (!o.empty()) try { - CompileState cs; + CompilerState cs; ret = CodeFragment(o, cs).code(); } catch (CompilerException const& _e) @@ -1651,8 +805,6 @@ bytes eth::compileLLL(string const& _s, vector* _errors) return ret; } -//try compileLLL("(((69wei) 'hello) (xyz \"abc\") (>= 0x69 ether))"); - string eth::disassemble(bytes const& _mem) { stringstream ret; diff --git a/libethereum/Instruction.h b/libethereum/Instruction.h index e4cab4d91..45b742552 100644 --- a/libethereum/Instruction.h +++ b/libethereum/Instruction.h @@ -24,6 +24,9 @@ #include #include "Exceptions.h" +namespace boost { namespace spirit { class utree; } } +namespace sp = boost::spirit; + namespace eth { @@ -143,14 +146,10 @@ extern const std::map c_instructionInfo; /// Convert from string mnemonic to Instruction type. extern const std::map c_instructions; -/// Convert from simple EVM assembly language to EVM code. -bytes assemble(std::string const& _code, bool _quiet = false); - /// Convert from EVM code to simple EVM assembly language. std::string disassemble(bytes const& _mem); /// Compile a Low-level Lisp-like Language program into EVM-code. -bytes compileLisp(std::string const& _code, bool _quiet, bytes& _init); class CompilerException: public Exception {}; class InvalidOperation: public CompilerException {}; class SymbolNotFirst: public CompilerException {}; @@ -161,9 +160,79 @@ class DataNotExecutable: public CompilerException {}; class IncorrectParameterCount: public CompilerException {}; class InvalidDeposit: public CompilerException {}; class InvalidOpCode: public CompilerException {}; -bytes compileLLL(std::string const& _s, std::vector* _errors); +class InvalidName: public CompilerException {}; +class InvalidMacroArgs: public CompilerException {}; +class BareSymbol: public CompilerException {}; +bytes compileLLL(std::string const& _s, std::vector* _errors = nullptr); + +class CompilerState; +class CodeFragment; + +class CodeLocation +{ + friend class CodeFragment; + +public: + CodeLocation(CodeFragment* _f); + CodeLocation(CodeFragment* _f, unsigned _p): m_f(_f), m_pos(_p) {} -/// Append an appropriate PUSH instruction together with the literal value onto the given code. -unsigned pushLiteral(bytes& o_code, u256 _literalValue); + unsigned get() const; + void increase(unsigned _val); + void set(unsigned _val); + void set(CodeLocation _loc) { assert(_loc.m_f == m_f); set(_loc.m_pos); } + void anchor(); + + CodeLocation operator+(unsigned _i) const { return CodeLocation(m_f, m_pos + _i); } + +private: + CodeFragment* m_f; + unsigned m_pos; +}; + +class CompilerState; + +class CodeFragment +{ + friend class CodeLocation; + +public: + CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM = false); + CodeFragment(bytes const& _c = bytes()): m_code(_c) {} + + bytes const& code() const { return m_code; } + + unsigned appendPush(u256 _l); + void appendFragment(CodeFragment const& _f); + void appendFragment(CodeFragment const& _f, unsigned _i); + void appendInstruction(Instruction _i); + + CodeLocation appendPushLocation(unsigned _l = 0); + void appendPushLocation(CodeLocation _l) { assert(_l.m_f == this); appendPushLocation(_l.m_pos); } + + CodeLocation appendJump() { auto ret = appendPushLocation(0); appendInstruction(Instruction::JUMP); return ret; } + CodeLocation appendJumpI() { auto ret = appendPushLocation(0); appendInstruction(Instruction::JUMPI); return ret; } + CodeLocation appendJump(CodeLocation _l) { auto ret = appendPushLocation(_l.m_pos); appendInstruction(Instruction::JUMP); return ret; } + CodeLocation appendJumpI(CodeLocation _l) { auto ret = appendPushLocation(_l.m_pos); appendInstruction(Instruction::JUMPI); return ret; } + + void onePath() { assert(!m_totalDeposit && !m_baseDeposit); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; } + void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; } + void donePaths() { donePath(); m_totalDeposit = m_baseDeposit = 0; } + void ignored() { m_baseDeposit = m_deposit; } + void endIgnored() { m_deposit = m_baseDeposit; m_baseDeposit = 0; } + + unsigned size() const { return m_code.size(); } + +private: + template void error() { throw T(); } + void constructOperation(sp::utree const& _t, CompilerState& _s); + + void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) error(); } + + int m_deposit = 0; + int m_baseDeposit = 0; + int m_totalDeposit = 0; + bytes m_code; + std::vector m_locs; +}; } diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 2c4925e4c..806c288ac 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -30,8 +30,6 @@ using eth::Transaction; // functions using eth::toHex; -using eth::assemble; -using eth::compileLisp; using eth::disassemble; using eth::formatBalance; using eth::fromHex; @@ -270,9 +268,7 @@ Client* QEthereum::client() const QString QEthereum::lll(QString _s) const { - bytes ret; - eth::compileLisp(_s.toStdString(), true, ret); - return asQString(ret); + return asQString(eth::compileLLL(_s.toStdString())); } QString QEthereum::sha3(QString _s) const diff --git a/test/vm.cpp b/test/vm.cpp index a7ad8daa0..890040984 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -162,7 +162,7 @@ public: thisTxCode.clear(); if (_o["code"].type() == str_type) - compileLisp(_o["code"].get_str(), false, thisTxCode); + thisTxCode = compileLLL(_o["code"].get_str(), nullptr); else for (auto const& j: _o["code"].get_array()) thisTxCode.push_back(toByte(j)); @@ -277,7 +277,7 @@ public: get<2>(a)[adr++] = toInt(k); } if (o["code"].type() == str_type) - compileLisp(o["code"].get_str(), false, get<3>(a)); + get<3>(a) = compileLLL(o["code"].get_str(), nullptr); else { get<3>(a).clear(); diff --git a/walleth/MainWin.cpp b/walleth/MainWin.cpp index a04b6a99f..acbafa23a 100644 --- a/walleth/MainWin.cpp +++ b/walleth/MainWin.cpp @@ -38,8 +38,6 @@ using eth::Transaction; // functions using eth::toHex; -using eth::assemble; -using eth::compileLisp; using eth::disassemble; using eth::formatBalance; using eth::fromHex;