mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Remove the LLL compiler
This commit is contained in:
parent
9d9a7ebe25
commit
1ee4b9dc3b
@ -19,10 +19,7 @@ if (IS_BIG_ENDIAN)
|
||||
message(FATAL_ERROR "${PROJECT_NAME} currently does not support big endian systems.")
|
||||
endif()
|
||||
|
||||
option(LLL "Build LLL" OFF)
|
||||
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
||||
option(LLLC_LINK_STATIC "Link lllc executable statically on supported platforms" OFF)
|
||||
option(INSTALL_LLLC "Include lllc executable in installation" ${LLL})
|
||||
|
||||
# Setup cccache.
|
||||
include(EthCcache)
|
||||
@ -61,10 +58,6 @@ add_subdirectory(libsolc)
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
add_subdirectory(solc)
|
||||
if (LLL)
|
||||
add_subdirectory(liblll)
|
||||
add_subdirectory(lllc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TESTS AND NOT EMSCRIPTEN)
|
||||
|
@ -41,7 +41,6 @@ if (SUPPORT_TOOLS)
|
||||
endif()
|
||||
message("------------------------------------------------------------------ flags")
|
||||
message("-- OSSFUZZ ${OSSFUZZ}")
|
||||
message("-- LLL ${LLL}")
|
||||
message("------------------------------------------------------------------------")
|
||||
message("")
|
||||
endmacro()
|
||||
|
@ -1,14 +0,0 @@
|
||||
set(sources
|
||||
CodeFragment.cpp
|
||||
CodeFragment.h
|
||||
Compiler.cpp
|
||||
Compiler.h
|
||||
CompilerState.cpp
|
||||
CompilerState.h
|
||||
Exceptions.h
|
||||
Parser.cpp
|
||||
Parser.h
|
||||
)
|
||||
|
||||
add_library(lll ${sources})
|
||||
target_link_libraries(lll PUBLIC evmasm solutil)
|
@ -1,758 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file CodeFragment.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#include <liblll/CodeFragment.h>
|
||||
#include <liblll/CompilerState.h>
|
||||
#include <liblll/Parser.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif // defined(__GNUC__)
|
||||
|
||||
#include <boost/spirit/include/support_utree.hpp>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif // defined(__GNUC__)
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::evmasm;
|
||||
using namespace solidity::lll;
|
||||
|
||||
void CodeFragment::finalise(CompilerState const& _cs)
|
||||
{
|
||||
// NOTE: add this as a safeguard in case the user didn't issue an
|
||||
// explicit stop at the end of the sequence
|
||||
m_asm.append(Instruction::STOP);
|
||||
|
||||
if (_cs.usedAlloc && _cs.vars.size() && !m_finalised)
|
||||
{
|
||||
m_finalised = true;
|
||||
m_asm.injectStart(Instruction::MSTORE8);
|
||||
m_asm.injectStart((u256)((_cs.vars.size() + 2) * 32) - 1);
|
||||
m_asm.injectStart((u256)1);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Returns true iff the instruction is valid in "inline assembly".
|
||||
bool validAssemblyInstruction(string us)
|
||||
{
|
||||
auto it = c_instructions.find(us);
|
||||
return !(
|
||||
it == c_instructions.end() ||
|
||||
isPushInstruction(it->second)
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns true iff the instruction is valid as a function.
|
||||
bool validFunctionalInstruction(string us)
|
||||
{
|
||||
auto it = c_instructions.find(us);
|
||||
return !(
|
||||
it == c_instructions.end() ||
|
||||
isPushInstruction(it->second) ||
|
||||
isDupInstruction(it->second) ||
|
||||
isSwapInstruction(it->second) ||
|
||||
it->second == Instruction::JUMPDEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM):
|
||||
m_readFile(_readFile)
|
||||
{
|
||||
/*
|
||||
std::cout << "CodeFragment. Locals:";
|
||||
for (auto const& i: _s.defs)
|
||||
std::cout << i.first << ":" << i.second.m_asm.out();
|
||||
std::cout << "Args:";
|
||||
for (auto const& i: _s.args)
|
||||
std::cout << i.first << ":" << i.second.m_asm.out();
|
||||
std::cout << "Outers:";
|
||||
for (auto const& i: _s.outers)
|
||||
std::cout << i.first << ":" << i.second.m_asm.out();
|
||||
debugOutAST(std::cout, _t);
|
||||
std::cout << endl << flush;
|
||||
*/
|
||||
switch (_t.which())
|
||||
{
|
||||
case sp::utree_type::list_type:
|
||||
constructOperation(_t, _s);
|
||||
break;
|
||||
case sp::utree_type::string_type:
|
||||
{
|
||||
auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
||||
string s(sr.begin(), sr.end());
|
||||
m_asm.append(s);
|
||||
break;
|
||||
}
|
||||
case sp::utree_type::symbol_type:
|
||||
{
|
||||
auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
||||
string s(sr.begin(), sr.end());
|
||||
string us = boost::algorithm::to_upper_copy(s);
|
||||
if (_allowASM && c_instructions.count(us) && validAssemblyInstruction(us))
|
||||
m_asm.append(c_instructions.at(us));
|
||||
else if (_s.defs.count(s))
|
||||
m_asm.append(_s.defs.at(s).m_asm);
|
||||
else if (_s.args.count(s))
|
||||
m_asm.append(_s.args.at(s).m_asm);
|
||||
else if (_s.outers.count(s))
|
||||
m_asm.append(_s.outers.at(s).m_asm);
|
||||
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
|
||||
{
|
||||
auto it = _s.vars.find(s);
|
||||
if (it == _s.vars.end())
|
||||
error<InvalidName>(std::string("Symbol not found: ") + s);
|
||||
m_asm.append((u256)it->second.first);
|
||||
}
|
||||
else
|
||||
error<BareSymbol>(s);
|
||||
|
||||
break;
|
||||
}
|
||||
case sp::utree_type::any_type:
|
||||
{
|
||||
bigint i = *_t.get<bigint*>();
|
||||
if (i < 0 || i > bigint(u256(0) - 1))
|
||||
error<IntegerOutOfRange>(toString(i));
|
||||
m_asm.append((u256)i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error<CompilerException>("Unexpected fragment type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
||||
{
|
||||
if (_t.tag() == 0 && _t.empty())
|
||||
error<EmptyList>();
|
||||
else if (_t.tag() == 0 && _t.front().which() != sp::utree_type::symbol_type)
|
||||
error<DataNotExecutable>();
|
||||
else
|
||||
{
|
||||
string s;
|
||||
string us;
|
||||
switch (_t.tag())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto sr = _t.front().get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
||||
s = string(sr.begin(), sr.end());
|
||||
us = boost::algorithm::to_upper_copy(s);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
us = "MLOAD";
|
||||
break;
|
||||
case 2:
|
||||
us = "SLOAD";
|
||||
break;
|
||||
case 3:
|
||||
us = "MSTORE";
|
||||
break;
|
||||
case 4:
|
||||
us = "SSTORE";
|
||||
break;
|
||||
case 5:
|
||||
us = "SEQ";
|
||||
break;
|
||||
case 6:
|
||||
us = "CALLDATALOAD";
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
auto firstAsString = [&]()
|
||||
{
|
||||
auto i = *++_t.begin();
|
||||
if (i.tag())
|
||||
error<InvalidName>(toString(i));
|
||||
if (i.which() == sp::utree_type::string_type)
|
||||
{
|
||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
||||
return string(sr.begin(), sr.end());
|
||||
}
|
||||
else if (i.which() == sp::utree_type::symbol_type)
|
||||
{
|
||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
||||
return _s.getDef(string(sr.begin(), sr.end())).m_asm.backString();
|
||||
}
|
||||
return string();
|
||||
};
|
||||
|
||||
auto varAddress = [&](string const& n, bool createMissing = false)
|
||||
{
|
||||
if (n.empty())
|
||||
error<InvalidName>("Empty variable name not allowed");
|
||||
auto it = _s.vars.find(n);
|
||||
if (it == _s.vars.end())
|
||||
{
|
||||
if (createMissing)
|
||||
{
|
||||
// Create new variable
|
||||
bool ok;
|
||||
tie(it, ok) = _s.vars.insert(make_pair(n, make_pair(_s.stackSize, 32)));
|
||||
_s.stackSize += 32;
|
||||
}
|
||||
else
|
||||
error<InvalidName>(std::string("Symbol not found: ") + n);
|
||||
}
|
||||
return it->second.first;
|
||||
};
|
||||
|
||||
// Operations who args are not standard stack-pushers.
|
||||
bool nonStandard = true;
|
||||
if (us == "ASM")
|
||||
{
|
||||
int c = 0;
|
||||
for (auto const& i: _t)
|
||||
if (c++)
|
||||
{
|
||||
auto fragment = CodeFragment(i, _s, m_readFile, true).m_asm;
|
||||
if ((m_asm.deposit() + fragment.deposit()) < 0)
|
||||
error<IncorrectParameterCount>("The assembly instruction resulted in stack underflow");
|
||||
m_asm.append(fragment);
|
||||
}
|
||||
}
|
||||
else if (us == "INCLUDE")
|
||||
{
|
||||
if (_t.size() != 2)
|
||||
error<IncorrectParameterCount>(us);
|
||||
string fileName = firstAsString();
|
||||
if (fileName.empty())
|
||||
error<InvalidName>("Empty file name provided");
|
||||
if (!m_readFile)
|
||||
error<InvalidName>("Import callback not present");
|
||||
string contents = m_readFile(fileName);
|
||||
if (contents.empty())
|
||||
error<InvalidName>(std::string("File not found (or empty): ") + fileName);
|
||||
m_asm.append(CodeFragment::compile(std::move(contents), _s, m_readFile).m_asm);
|
||||
}
|
||||
else if (us == "SET")
|
||||
{
|
||||
// TODO: move this to be a stack variable (and not a memory variable)
|
||||
if (_t.size() != 3)
|
||||
error<IncorrectParameterCount>(us);
|
||||
int c = 0;
|
||||
for (auto const& i: _t)
|
||||
if (c++ == 2)
|
||||
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
|
||||
m_asm.append((u256)varAddress(firstAsString(), true));
|
||||
m_asm.append(Instruction::MSTORE);
|
||||
}
|
||||
else if (us == "UNSET")
|
||||
{
|
||||
// TODO: this doesn't actually free up anything, since it is a memory variable (see "SET")
|
||||
if (_t.size() != 2)
|
||||
error<IncorrectParameterCount>();
|
||||
auto it = _s.vars.find(firstAsString());
|
||||
if (it != _s.vars.end())
|
||||
_s.vars.erase(it);
|
||||
}
|
||||
else if (us == "GET")
|
||||
{
|
||||
if (_t.size() != 2)
|
||||
error<IncorrectParameterCount>(us);
|
||||
m_asm.append((u256)varAddress(firstAsString()));
|
||||
m_asm.append(Instruction::MLOAD);
|
||||
}
|
||||
else if (us == "WITH")
|
||||
{
|
||||
if (_t.size() != 4)
|
||||
error<IncorrectParameterCount>();
|
||||
string key = firstAsString();
|
||||
if (_s.vars.find(key) != _s.vars.end())
|
||||
error<InvalidName>(string("Symbol already used: ") + key);
|
||||
|
||||
// Create variable
|
||||
// TODO: move this to be a stack variable (and not a memory variable)
|
||||
size_t c = 0;
|
||||
for (auto const& i: _t)
|
||||
if (c++ == 2)
|
||||
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
|
||||
m_asm.append((u256)varAddress(key, true));
|
||||
m_asm.append(Instruction::MSTORE);
|
||||
|
||||
// Insert sub with variable access, but new state
|
||||
CompilerState ns = _s;
|
||||
c = 0;
|
||||
for (auto const& i: _t)
|
||||
if (c++ == 3)
|
||||
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
|
||||
|
||||
// Remove variable
|
||||
auto it = _s.vars.find(key);
|
||||
if (it != _s.vars.end())
|
||||
_s.vars.erase(it);
|
||||
}
|
||||
else if (us == "REF")
|
||||
m_asm.append((u256)varAddress(firstAsString()));
|
||||
else if (us == "DEF")
|
||||
{
|
||||
string n;
|
||||
unsigned ii = 0;
|
||||
if (_t.size() != 3 && _t.size() != 4)
|
||||
error<IncorrectParameterCount>(us);
|
||||
vector<string> args;
|
||||
for (auto const& i: _t)
|
||||
{
|
||||
if (ii == 1)
|
||||
{
|
||||
if (i.tag())
|
||||
error<InvalidName>(toString(i));
|
||||
if (i.which() == sp::utree_type::string_type)
|
||||
{
|
||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
||||
n = string(sr.begin(), sr.end());
|
||||
}
|
||||
else if (i.which() == sp::utree_type::symbol_type)
|
||||
{
|
||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
||||
n = _s.getDef(string(sr.begin(), sr.end())).m_asm.backString();
|
||||
}
|
||||
}
|
||||
else if (ii == 2)
|
||||
if (_t.size() == 3)
|
||||
{
|
||||
/// NOTE: some compilers could do the assignment first if this is done in a single line
|
||||
CodeFragment code = CodeFragment(i, _s, m_readFile);
|
||||
_s.defs[n] = code;
|
||||
}
|
||||
else
|
||||
for (auto const& j: i)
|
||||
{
|
||||
if (j.tag() || j.which() != sp::utree_type::symbol_type)
|
||||
error<InvalidMacroArgs>();
|
||||
auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
||||
args.emplace_back(sr.begin(), sr.end());
|
||||
}
|
||||
else if (ii == 3)
|
||||
{
|
||||
auto k = make_pair(n, args.size());
|
||||
_s.macros[k].code = i;
|
||||
_s.macros[k].env = _s.outers;
|
||||
_s.macros[k].args = args;
|
||||
for (auto const& i: _s.args)
|
||||
_s.macros[k].env[i.first] = i.second;
|
||||
for (auto const& i: _s.defs)
|
||||
_s.macros[k].env[i.first] = i.second;
|
||||
}
|
||||
++ii;
|
||||
}
|
||||
}
|
||||
else if (us == "LIT")
|
||||
{
|
||||
if (_t.size() < 3)
|
||||
error<IncorrectParameterCount>(us);
|
||||
unsigned ii = 0;
|
||||
CodeFragment pos;
|
||||
bytes data;
|
||||
for (auto const& i: _t)
|
||||
{
|
||||
if (ii == 0)
|
||||
{
|
||||
ii++;
|
||||
continue;
|
||||
}
|
||||
else if (ii == 1)
|
||||
{
|
||||
pos = CodeFragment(i, _s, m_readFile);
|
||||
if (pos.m_asm.deposit() != 1)
|
||||
error<InvalidDeposit>(toString(i));
|
||||
}
|
||||
else if (i.tag() != 0)
|
||||
{
|
||||
error<InvalidLiteral>(toString(i));
|
||||
}
|
||||
else if (i.which() == sp::utree_type::string_type)
|
||||
{
|
||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
||||
data.insert(data.end(), (uint8_t const *)sr.begin(), (uint8_t const*)sr.end());
|
||||
}
|
||||
else if (i.which() == sp::utree_type::any_type)
|
||||
{
|
||||
bigint bi = *i.get<bigint*>();
|
||||
if (bi < 0)
|
||||
error<IntegerOutOfRange>(toString(i));
|
||||
else
|
||||
{
|
||||
bytes tmp = toCompactBigEndian(bi);
|
||||
data.insert(data.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error<InvalidLiteral>(toString(i));
|
||||
}
|
||||
|
||||
ii++;
|
||||
}
|
||||
m_asm.append((u256)data.size());
|
||||
m_asm.append(Instruction::DUP1);
|
||||
m_asm.append(data);
|
||||
m_asm.append(pos.m_asm, 1);
|
||||
m_asm.append(Instruction::CODECOPY);
|
||||
}
|
||||
else
|
||||
nonStandard = false;
|
||||
|
||||
if (nonStandard)
|
||||
return;
|
||||
|
||||
std::map<std::string, Instruction> const c_arith = {
|
||||
{ "+", Instruction::ADD },
|
||||
{ "-", Instruction::SUB },
|
||||
{ "*", Instruction::MUL },
|
||||
{ "/", Instruction::DIV },
|
||||
{ "%", Instruction::MOD },
|
||||
{ "&", Instruction::AND },
|
||||
{ "|", Instruction::OR },
|
||||
{ "^", Instruction::XOR }
|
||||
};
|
||||
std::map<std::string, pair<Instruction, bool>> 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<std::string, Instruction> const c_unary = {
|
||||
{ "!", Instruction::ISZERO },
|
||||
{ "~", Instruction::NOT }
|
||||
};
|
||||
|
||||
vector<CodeFragment> code;
|
||||
CompilerState ns = _s;
|
||||
ns.vars.clear();
|
||||
ns.usedAlloc = false;
|
||||
int c = _t.tag() ? 1 : 0;
|
||||
for (auto const& i: _t)
|
||||
if (c++)
|
||||
{
|
||||
if (us == "LLL" && c == 1)
|
||||
code.emplace_back(i, ns, m_readFile);
|
||||
else
|
||||
code.emplace_back(i, _s, m_readFile);
|
||||
}
|
||||
auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); };
|
||||
auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
|
||||
auto requireMaxSize = [&](unsigned s) { if (code.size() > s) error<IncorrectParameterCount>(us); };
|
||||
auto requireDeposit = [&](unsigned i, int s) { if (code[i].m_asm.deposit() != s) error<InvalidDeposit>(us); };
|
||||
|
||||
if (_s.macros.count(make_pair(s, code.size())))
|
||||
{
|
||||
Macro const& m = _s.macros.at(make_pair(s, code.size()));
|
||||
CompilerState cs = _s;
|
||||
for (auto const& i: m.env)
|
||||
cs.outers[i.first] = i.second;
|
||||
for (auto const& i: cs.defs)
|
||||
cs.outers[i.first] = i.second;
|
||||
cs.defs.clear();
|
||||
for (unsigned i = 0; i < m.args.size(); ++i)
|
||||
{
|
||||
//requireDeposit(i, 1);
|
||||
cs.args[m.args[i]] = code[i];
|
||||
}
|
||||
m_asm.append(CodeFragment(m.code, cs, m_readFile).m_asm);
|
||||
for (auto const& i: cs.defs)
|
||||
_s.defs[i.first] = i.second;
|
||||
for (auto const& i: cs.macros)
|
||||
_s.macros.insert(i);
|
||||
}
|
||||
else if (c_instructions.count(us) && validFunctionalInstruction(us))
|
||||
{
|
||||
auto it = c_instructions.find(us);
|
||||
requireSize(instructionInfo(it->second).args);
|
||||
|
||||
for (unsigned i = code.size(); i; --i)
|
||||
m_asm.append(code[i - 1].m_asm, 1);
|
||||
m_asm.append(it->second);
|
||||
}
|
||||
else if (c_arith.count(us))
|
||||
{
|
||||
auto it = c_arith.find(us);
|
||||
requireMinSize(1);
|
||||
for (unsigned i = code.size(); i; --i)
|
||||
{
|
||||
requireDeposit(i - 1, 1);
|
||||
m_asm.append(code[i - 1].m_asm, 1);
|
||||
}
|
||||
for (unsigned i = 1; i < code.size(); ++i)
|
||||
m_asm.append(it->second);
|
||||
}
|
||||
else if (c_binary.count(us))
|
||||
{
|
||||
auto it = c_binary.find(us);
|
||||
requireSize(2);
|
||||
requireDeposit(0, 1);
|
||||
requireDeposit(1, 1);
|
||||
m_asm.append(code[1].m_asm, 1);
|
||||
m_asm.append(code[0].m_asm, 1);
|
||||
m_asm.append(it->second.first);
|
||||
if (it->second.second)
|
||||
m_asm.append(Instruction::ISZERO);
|
||||
}
|
||||
else if (c_unary.count(us))
|
||||
{
|
||||
auto it = c_unary.find(us);
|
||||
requireSize(1);
|
||||
requireDeposit(0, 1);
|
||||
m_asm.append(code[0].m_asm, 1);
|
||||
m_asm.append(it->second);
|
||||
}
|
||||
else if (us == "IF")
|
||||
{
|
||||
requireSize(3);
|
||||
requireDeposit(0, 1);
|
||||
int minDep = min(code[1].m_asm.deposit(), code[2].m_asm.deposit());
|
||||
|
||||
m_asm.append(code[0].m_asm);
|
||||
auto mainBranch = m_asm.appendJumpI();
|
||||
|
||||
/// The else branch.
|
||||
int startDeposit = m_asm.deposit();
|
||||
m_asm.append(code[2].m_asm, minDep);
|
||||
auto end = m_asm.appendJump();
|
||||
int deposit = m_asm.deposit();
|
||||
m_asm.setDeposit(startDeposit);
|
||||
|
||||
/// The main branch.
|
||||
m_asm << mainBranch.tag();
|
||||
m_asm.append(code[1].m_asm, minDep);
|
||||
m_asm << end.tag();
|
||||
if (m_asm.deposit() != deposit)
|
||||
error<InvalidDeposit>(us);
|
||||
}
|
||||
else if (us == "WHEN" || us == "UNLESS")
|
||||
{
|
||||
requireSize(2);
|
||||
requireDeposit(0, 1);
|
||||
|
||||
m_asm.append(code[0].m_asm);
|
||||
if (us == "WHEN")
|
||||
m_asm.append(Instruction::ISZERO);
|
||||
auto end = m_asm.appendJumpI();
|
||||
m_asm.append(code[1].m_asm, 0);
|
||||
m_asm << end.tag();
|
||||
}
|
||||
else if (us == "WHILE" || us == "UNTIL")
|
||||
{
|
||||
requireSize(2);
|
||||
requireDeposit(0, 1);
|
||||
|
||||
auto begin = m_asm.append(m_asm.newTag());
|
||||
m_asm.append(code[0].m_asm);
|
||||
if (us == "WHILE")
|
||||
m_asm.append(Instruction::ISZERO);
|
||||
auto end = m_asm.appendJumpI();
|
||||
m_asm.append(code[1].m_asm, 0);
|
||||
m_asm.appendJump(begin);
|
||||
m_asm << end.tag();
|
||||
}
|
||||
else if (us == "FOR")
|
||||
{
|
||||
requireSize(4);
|
||||
requireDeposit(1, 1);
|
||||
|
||||
m_asm.append(code[0].m_asm, 0);
|
||||
auto begin = m_asm.append(m_asm.newTag());
|
||||
m_asm.append(code[1].m_asm);
|
||||
m_asm.append(Instruction::ISZERO);
|
||||
auto end = m_asm.appendJumpI();
|
||||
m_asm.append(code[3].m_asm, 0);
|
||||
m_asm.append(code[2].m_asm, 0);
|
||||
m_asm.appendJump(begin);
|
||||
m_asm << end.tag();
|
||||
}
|
||||
else if (us == "SWITCH")
|
||||
{
|
||||
requireMinSize(1);
|
||||
|
||||
bool hasDefault = (code.size() % 2 == 1);
|
||||
int startDeposit = m_asm.deposit();
|
||||
int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0;
|
||||
|
||||
// The conditions
|
||||
evmasm::AssemblyItems jumpTags;
|
||||
for (unsigned i = 0; i < code.size() - 1; i += 2)
|
||||
{
|
||||
requireDeposit(i, 1);
|
||||
m_asm.append(code[i].m_asm);
|
||||
jumpTags.push_back(m_asm.appendJumpI());
|
||||
}
|
||||
|
||||
// The default, if present
|
||||
if (hasDefault)
|
||||
m_asm.append(code[code.size() - 1].m_asm);
|
||||
|
||||
// The targets - appending in reverse makes the top case the most efficient.
|
||||
if (code.size() > 1)
|
||||
{
|
||||
auto end = m_asm.appendJump();
|
||||
for (int i = 2 * (code.size() / 2 - 1); i >= 0; i -= 2)
|
||||
{
|
||||
m_asm << jumpTags[i / 2].tag();
|
||||
requireDeposit(i + 1, targetDeposit);
|
||||
m_asm.append(code[i + 1].m_asm);
|
||||
if (i != 0)
|
||||
m_asm.appendJump(end);
|
||||
}
|
||||
m_asm << end.tag();
|
||||
}
|
||||
|
||||
m_asm.setDeposit(startDeposit + targetDeposit);
|
||||
}
|
||||
else if (us == "ALLOC")
|
||||
{
|
||||
requireSize(1);
|
||||
requireDeposit(0, 1);
|
||||
|
||||
// (alloc N):
|
||||
// - Evaluates to (msize) before the allocation - the start of the allocated memory
|
||||
// - Does not allocate memory when N is zero
|
||||
// - Size of memory allocated is N bytes rounded up to a multiple of 32
|
||||
// - Uses MLOAD to expand MSIZE to avoid modifying memory.
|
||||
|
||||
auto end = m_asm.newTag();
|
||||
m_asm.append(Instruction::MSIZE); // Result will be original top of memory
|
||||
m_asm.append(code[0].m_asm, 1); // The alloc argument N
|
||||
m_asm.append(Instruction::DUP1);
|
||||
m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE
|
||||
m_asm.appendJumpI(end);
|
||||
m_asm.append(u256(1));
|
||||
m_asm.append(Instruction::DUP2); // Copy N
|
||||
m_asm.append(Instruction::SUB); // N-1
|
||||
m_asm.append(u256(0x1f)); // Bit mask
|
||||
m_asm.append(Instruction::NOT); // Invert
|
||||
m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary
|
||||
m_asm.append(Instruction::MSIZE); // MSIZE is cheap
|
||||
m_asm.append(Instruction::ADD);
|
||||
m_asm.append(Instruction::MLOAD); // Updates MSIZE
|
||||
m_asm.append(Instruction::POP); // Discard the result of the MLOAD
|
||||
m_asm.append(end);
|
||||
m_asm.append(Instruction::POP); // Discard duplicate N
|
||||
|
||||
_s.usedAlloc = true;
|
||||
}
|
||||
else if (us == "LLL")
|
||||
{
|
||||
requireMinSize(2);
|
||||
requireMaxSize(3);
|
||||
requireDeposit(1, 1);
|
||||
|
||||
auto subPush = m_asm.appendSubroutine(make_shared<evmasm::Assembly>(code[0].assembly(ns)));
|
||||
m_asm.append(Instruction::DUP1);
|
||||
if (code.size() == 3)
|
||||
{
|
||||
requireDeposit(2, 1);
|
||||
m_asm.append(code[2].m_asm, 1);
|
||||
m_asm.append(Instruction::LT);
|
||||
m_asm.append(Instruction::ISZERO);
|
||||
m_asm.append(Instruction::MUL);
|
||||
m_asm.append(Instruction::DUP1);
|
||||
}
|
||||
m_asm.append(subPush);
|
||||
m_asm.append(code[1].m_asm, 1);
|
||||
m_asm.append(Instruction::CODECOPY);
|
||||
}
|
||||
else if (us == "&&" || us == "||")
|
||||
{
|
||||
requireMinSize(1);
|
||||
for (unsigned i = 0; i < code.size(); ++i)
|
||||
requireDeposit(i, 1);
|
||||
|
||||
auto end = m_asm.newTag();
|
||||
if (code.size() > 1)
|
||||
{
|
||||
m_asm.append((u256)(us == "||" ? 1 : 0));
|
||||
for (unsigned i = 1; i < code.size(); ++i)
|
||||
{
|
||||
// Check if true - predicate
|
||||
m_asm.append(code[i - 1].m_asm, 1);
|
||||
if (us == "&&")
|
||||
m_asm.append(Instruction::ISZERO);
|
||||
m_asm.appendJumpI(end);
|
||||
}
|
||||
m_asm.append(Instruction::POP);
|
||||
}
|
||||
|
||||
// Check if true - predicate
|
||||
m_asm.append(code.back().m_asm, 1);
|
||||
|
||||
// At end now.
|
||||
m_asm.append(end);
|
||||
}
|
||||
else if (us == "SEQ")
|
||||
{
|
||||
unsigned ii = 0;
|
||||
for (auto const& i: code)
|
||||
if (++ii < code.size())
|
||||
m_asm.append(i.m_asm, 0);
|
||||
else
|
||||
m_asm.append(i.m_asm);
|
||||
}
|
||||
else if (us == "RAW")
|
||||
{
|
||||
for (auto const& i: code)
|
||||
m_asm.append(i.m_asm);
|
||||
// Leave only the last item on stack.
|
||||
while (m_asm.deposit() > 1)
|
||||
m_asm.append(Instruction::POP);
|
||||
}
|
||||
else if (us == "BYTECODESIZE")
|
||||
{
|
||||
m_asm.appendProgramSize();
|
||||
}
|
||||
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
|
||||
m_asm.append((u256)varAddress(s));
|
||||
else
|
||||
error<InvalidOperation>("Unsupported keyword: '" + us + "'");
|
||||
}
|
||||
}
|
||||
|
||||
CodeFragment CodeFragment::compile(string _src, CompilerState& _s, ReadCallback const& _readFile)
|
||||
{
|
||||
CodeFragment ret;
|
||||
sp::utree o;
|
||||
parseTreeLLL(std::move(_src), o);
|
||||
if (!o.empty())
|
||||
ret = CodeFragment(o, _s, _readFile);
|
||||
_s.treesToKill.push_back(o);
|
||||
return ret;
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file CodeFragment.h
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblll/Exceptions.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libsolutil/Common.h>
|
||||
|
||||
namespace boost { namespace spirit { class utree; } }
|
||||
namespace sp = boost::spirit;
|
||||
|
||||
namespace solidity::lll
|
||||
{
|
||||
|
||||
struct CompilerState;
|
||||
|
||||
class CodeFragment
|
||||
{
|
||||
public:
|
||||
using ReadCallback = std::function<std::string(std::string const&)>;
|
||||
|
||||
CodeFragment() = default;
|
||||
CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false);
|
||||
|
||||
static CodeFragment compile(std::string _src, CompilerState& _s, ReadCallback const& _readFile);
|
||||
|
||||
/// Consolidates data and compiles code.
|
||||
evmasm::Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; }
|
||||
|
||||
private:
|
||||
void finalise(CompilerState const& _cs);
|
||||
|
||||
template <class T> static void error() { BOOST_THROW_EXCEPTION(T() ); }
|
||||
template <class T> static void error(std::string const& reason) {
|
||||
auto err = T();
|
||||
err << util::errinfo_comment(reason);
|
||||
BOOST_THROW_EXCEPTION(err);
|
||||
}
|
||||
void constructOperation(sp::utree const& _t, CompilerState& _s);
|
||||
|
||||
bool m_finalised = false;
|
||||
evmasm::Assembly m_asm;
|
||||
ReadCallback m_readFile;
|
||||
};
|
||||
|
||||
static CodeFragment const NullCodeFragment;
|
||||
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file Compiler.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#include <liblll/Compiler.h>
|
||||
#include <liblll/Parser.h>
|
||||
#include <liblll/CompilerState.h>
|
||||
#include <liblll/CodeFragment.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::lll;
|
||||
|
||||
bytes solidity::lll::compileLLL(string _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
CompilerState cs;
|
||||
cs.populateStandard();
|
||||
auto assembly = CodeFragment::compile(std::move(_src), cs, _readFile).assembly(cs);
|
||||
if (_opt)
|
||||
assembly = assembly.optimise(true, _evmVersion, true, 200);
|
||||
bytes ret = assembly.assemble().bytecode;
|
||||
for (auto i: cs.treesToKill)
|
||||
killBigints(i);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception const& _e)
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->emplace_back("Parse error.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (std::exception const& _e)
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->emplace_back("Parse exception.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (_errors)
|
||||
_errors->emplace_back("Internal compiler exception.");
|
||||
}
|
||||
return bytes();
|
||||
}
|
||||
|
||||
std::string solidity::lll::compileLLLToAsm(std::string _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
CompilerState cs;
|
||||
cs.populateStandard();
|
||||
auto assembly = CodeFragment::compile(std::move(_src), cs, _readFile).assembly(cs);
|
||||
if (_opt)
|
||||
assembly = assembly.optimise(true, _evmVersion, true, 200);
|
||||
string ret = assembly.assemblyString();
|
||||
for (auto i: cs.treesToKill)
|
||||
killBigints(i);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception const& _e)
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->emplace_back("Parse error.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (std::exception const& _e)
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->emplace_back("Parse exception.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (_errors)
|
||||
_errors->emplace_back("Internal compiler exception.");
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
string solidity::lll::parseLLL(string _src)
|
||||
{
|
||||
sp::utree o;
|
||||
|
||||
try
|
||||
{
|
||||
parseTreeLLL(std::move(_src), o);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
killBigints(o);
|
||||
return string();
|
||||
}
|
||||
|
||||
ostringstream ret;
|
||||
debugOutAST(ret, o);
|
||||
killBigints(o);
|
||||
return ret.str();
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file Compiler.h
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::lll
|
||||
{
|
||||
|
||||
using ReadCallback = std::function<std::string(std::string const&)>;
|
||||
|
||||
std::string parseLLL(std::string _src);
|
||||
std::string compileLLLToAsm(std::string _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
|
||||
bytes compileLLL(std::string _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
|
||||
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file CompilerState.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#include <liblll/CompilerState.h>
|
||||
#include <liblll/CodeFragment.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::lll;
|
||||
|
||||
CompilerState::CompilerState()
|
||||
{
|
||||
}
|
||||
|
||||
CodeFragment const& CompilerState::getDef(std::string const& _s) const
|
||||
{
|
||||
if (defs.count(_s))
|
||||
return defs.at(_s);
|
||||
else if (args.count(_s))
|
||||
return args.at(_s);
|
||||
else if (outers.count(_s))
|
||||
return outers.at(_s);
|
||||
else
|
||||
return NullCodeFragment;
|
||||
}
|
||||
|
||||
void CompilerState::populateStandard()
|
||||
{
|
||||
static string const s = "{"
|
||||
"(def 'panic () (asm INVALID))"
|
||||
// Alternative macro version of alloc, which is currently implemented in the parser
|
||||
// "(def 'alloc (n) (raw (msize) (when n (pop (mload (+ (msize) (& (- n 1) (~ 0x1f))))))))"
|
||||
"(def 'allgas (- (gas) 21))"
|
||||
"(def 'send (to value) (call allgas to value 0 0 0 0))"
|
||||
"(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))"
|
||||
// NOTE: in this macro, memory location 0 is set in order to force msize to be at least 32 bytes.
|
||||
"(def 'msg (gaslimit to value data datasize outsize) { [0]:0 [0]:(msize) (call gaslimit to value data datasize @0 outsize) @0 })"
|
||||
"(def 'msg (gaslimit to value data datasize) { (call gaslimit to value data datasize 0 32) @0 })"
|
||||
"(def 'msg (gaslimit to value data) { [0]:data (msg gaslimit to value 0 32) })"
|
||||
"(def 'msg (to value data) { [0]:data (msg allgas to value 0 32) })"
|
||||
"(def 'msg (to data) { [0]:data (msg allgas to 0 0 32) })"
|
||||
// NOTE: in the create macros, memory location 0 is set in order to force msize to be at least 32 bytes.
|
||||
"(def 'create (value code) { [0]:0 [0]:(msize) (create value @0 (lll code @0)) })"
|
||||
"(def 'create (code) { [0]:0 [0]:(msize) (create 0 @0 (lll code @0)) })"
|
||||
"(def 'sha3 (loc len) (keccak256 loc len))"
|
||||
"(def 'sha3 (val) { [0]:val (sha3 0 32) })"
|
||||
"(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })"
|
||||
"(def 'sha3trip (a b c) { [0]:a [32]:b [64]:c (sha3 0 96) })"
|
||||
"(def 'return (val) { [0]:val (return 0 32) })"
|
||||
"(def 'returnlll (code) (return 0 (lll code 0)) )"
|
||||
"(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )"
|
||||
"(def 'permcount 0)"
|
||||
"(def 'perm (name) { (makeperm name permcount) (def 'permcount (+ permcount 1)) } )"
|
||||
"(def 'ecrecover (hash v r s) { [0] hash [32] v [64] r [96] s (msg allgas 1 0 0 128) })"
|
||||
"(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))"
|
||||
"(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))"
|
||||
"(def 'sha256 (val) { [0]:val (sha256 0 32) })"
|
||||
"(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })"
|
||||
"(def 'wei 1)"
|
||||
"(def 'szabo 1000000000000)"
|
||||
"(def 'finney 1000000000000000)"
|
||||
"(def 'ether 1000000000000000000)"
|
||||
// these could be replaced by native instructions once supported by EVM
|
||||
"(def 'shl (val shift) (mul val (exp 2 shift)))"
|
||||
"(def 'shr (val shift) (div val (exp 2 shift)))"
|
||||
"}";
|
||||
CodeFragment::compile(s, *this, CodeFragment::ReadCallback());
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file CompilerState.h
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblll/CodeFragment.h>
|
||||
#include <boost/spirit/include/support_utree.hpp>
|
||||
|
||||
namespace solidity::lll
|
||||
{
|
||||
|
||||
struct Macro
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
boost::spirit::utree code;
|
||||
std::map<std::string, CodeFragment> env;
|
||||
};
|
||||
|
||||
struct CompilerState
|
||||
{
|
||||
CompilerState();
|
||||
|
||||
CodeFragment const& getDef(std::string const& _s) const;
|
||||
void populateStandard();
|
||||
|
||||
unsigned stackSize = 128;
|
||||
std::map<std::string, std::pair<unsigned, unsigned>> vars; ///< maps name to stack offset & size.
|
||||
std::map<std::string, CodeFragment> defs;
|
||||
std::map<std::string, CodeFragment> args;
|
||||
std::map<std::string, CodeFragment> outers;
|
||||
std::map<std::pair<std::string, unsigned>, Macro> macros;
|
||||
std::vector<boost::spirit::utree> treesToKill;
|
||||
bool usedAlloc = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file Exceptions.h
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
namespace solidity::lll
|
||||
{
|
||||
|
||||
/// Compile a Low-level Lisp-like Language program into EVM-code.
|
||||
class CompilerException: public util::Exception {};
|
||||
class InvalidOperation: public CompilerException {};
|
||||
class IntegerOutOfRange: public CompilerException {};
|
||||
class EmptyList: public CompilerException {};
|
||||
class DataNotExecutable: public CompilerException {};
|
||||
class IncorrectParameterCount: public CompilerException {};
|
||||
class InvalidName: public CompilerException {};
|
||||
class InvalidMacroArgs: public CompilerException {};
|
||||
class InvalidLiteral: public CompilerException {};
|
||||
class BareSymbol: public CompilerException {};
|
||||
class ParserException: public CompilerException {};
|
||||
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file Parser.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#include <liblll/Parser.h>
|
||||
|
||||
#if _MSC_VER
|
||||
#pragma warning(disable:4348)
|
||||
#endif
|
||||
|
||||
#define BOOST_RESULT_OF_USE_DECLTYPE
|
||||
#define BOOST_SPIRIT_USE_PHOENIX_V3
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix.hpp>
|
||||
#include <boost/spirit/include/support_utree.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::lll;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace px = boost::phoenix;
|
||||
namespace sp = boost::spirit;
|
||||
|
||||
void solidity::lll::killBigints(sp::utree const& _this)
|
||||
{
|
||||
switch (_this.which())
|
||||
{
|
||||
case sp::utree_type::list_type: for (auto const& i: _this) killBigints(i); break;
|
||||
case sp::utree_type::any_type: delete _this.get<bigint*>(); break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
void solidity::lll::debugOutAST(ostream& _out, sp::utree const& _this)
|
||||
{
|
||||
switch (_this.which())
|
||||
{
|
||||
case sp::utree_type::list_type:
|
||||
switch (_this.tag())
|
||||
{
|
||||
case 0: _out << "( "; for (auto const& i: _this) { debugOutAST(_out, i); _out << " "; } _out << ")"; break;
|
||||
case 1: _out << "@ "; debugOutAST(_out, _this.front()); break;
|
||||
case 2: _out << "@@ "; debugOutAST(_out, _this.front()); break;
|
||||
case 3: _out << "[ "; debugOutAST(_out, _this.front()); _out << " ] "; debugOutAST(_out, _this.back()); break;
|
||||
case 4: _out << "[[ "; debugOutAST(_out, _this.front()); _out << " ]] "; debugOutAST(_out, _this.back()); break;
|
||||
case 5: _out << "{ "; for (auto const& i: _this) { debugOutAST(_out, i); _out << " "; } _out << "}"; break;
|
||||
case 6: _out << "$ "; debugOutAST(_out, _this.front()); break;
|
||||
default:;
|
||||
}
|
||||
|
||||
break;
|
||||
case sp::utree_type::int_type: _out << _this.get<int>(); break;
|
||||
case sp::utree_type::string_type: _out << "\"" << _this.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>() << "\""; break;
|
||||
case sp::utree_type::symbol_type: _out << _this.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>(); break;
|
||||
case sp::utree_type::any_type: _out << *_this.get<bigint*>(); break;
|
||||
default: _out << "nil";
|
||||
}
|
||||
}
|
||||
|
||||
namespace solidity {
|
||||
namespace lll {
|
||||
namespace parseTreeLLL_ {
|
||||
|
||||
template<unsigned N>
|
||||
struct tagNode
|
||||
{
|
||||
void operator()(sp::utree& n, qi::rule<string::const_iterator, qi::ascii::space_type, sp::utree()>::context_type& c) const
|
||||
{
|
||||
(boost::fusion::at_c<0>(c.attributes) = n).tag(N);
|
||||
}
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
void solidity::lll::parseTreeLLL(string const& _s, sp::utree& o_out)
|
||||
{
|
||||
using qi::standard::space;
|
||||
using qi::standard::space_type;
|
||||
using solidity::lll::parseTreeLLL_::tagNode;
|
||||
using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>;
|
||||
using it = string::const_iterator;
|
||||
|
||||
qi::rule<it, space_type, sp::utree()> element;
|
||||
qi::rule<it, string()> str = '"' > qi::lexeme[+(~qi::char_(std::string("\"") + '\0'))] > '"';
|
||||
qi::rule<it, string()> strsh = '\'' > qi::lexeme[+(~qi::char_(std::string(" ;$@()[]{}:\n\t") + '\0'))];
|
||||
qi::rule<it, symbol_type()> symbol = qi::lexeme[+(~qi::char_(std::string(" $@[]{}:();\"\x01-\x1f\x7f") + '\0'))];
|
||||
qi::rule<it, string()> intstr = qi::lexeme[ qi::no_case["0x"][qi::_val = "0x"] >> +qi::char_("0-9a-fA-F")[qi::_val += qi::_1]] | qi::lexeme[+qi::char_("0-9")[qi::_val += qi::_1]];
|
||||
qi::rule<it, sp::utree()> integer = intstr[qi::_val = px::construct<sp::any_ptr>(px::new_<bigint>(qi::_1))];
|
||||
qi::rule<it, space_type, sp::utree()> atom = integer[qi::_val = qi::_1] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1];
|
||||
qi::rule<it, space_type, sp::utree::list_type()> seq = '{' > *element > '}';
|
||||
qi::rule<it, space_type, sp::utree::list_type()> mload = '@' > element;
|
||||
qi::rule<it, space_type, sp::utree::list_type()> sload = qi::lit("@@") > element;
|
||||
qi::rule<it, space_type, sp::utree::list_type()> mstore = '[' > element > ']' > -qi::lit(":") > element;
|
||||
qi::rule<it, space_type, sp::utree::list_type()> sstore = qi::lit("[[") > element > qi::lit("]]") > -qi::lit(":") > element;
|
||||
qi::rule<it, space_type, sp::utree::list_type()> calldataload = qi::lit("$") > element;
|
||||
qi::rule<it, space_type, sp::utree::list_type()> list = '(' > *element > ')';
|
||||
|
||||
qi::rule<it, space_type, sp::utree()> extra = sload[tagNode<2>()] | mload[tagNode<1>()] | sstore[tagNode<4>()] | mstore[tagNode<3>()] | seq[tagNode<5>()] | calldataload[tagNode<6>()];
|
||||
element = atom | list | extra;
|
||||
|
||||
string s;
|
||||
s.reserve(_s.size());
|
||||
bool incomment = false;
|
||||
bool instring = false;
|
||||
bool insstring = false;
|
||||
for (auto i: _s)
|
||||
{
|
||||
if (i == ';' && !instring && !insstring)
|
||||
incomment = true;
|
||||
else if (i == '\n')
|
||||
incomment = instring = insstring = false;
|
||||
else if (i == '"' && !insstring)
|
||||
instring = !instring;
|
||||
else if (i == '\'')
|
||||
insstring = true;
|
||||
else if (i == ' ')
|
||||
insstring = false;
|
||||
if (!incomment)
|
||||
s.push_back(i);
|
||||
}
|
||||
auto ret = s.cbegin();
|
||||
try
|
||||
{
|
||||
qi::phrase_parse(ret, s.cend(), element, space, qi::skip_flag::dont_postskip, o_out);
|
||||
}
|
||||
catch (qi::expectation_failure<it> const& e)
|
||||
{
|
||||
std::string fragment(e.first, e.last);
|
||||
std::string loc = to_string(std::distance(s.cbegin(), e.first) - 1);
|
||||
std::string reason("Lexer failure at " + loc + ": '" + fragment + "'");
|
||||
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment(reason));
|
||||
}
|
||||
for (auto i = ret; i != s.cend(); ++i)
|
||||
if (!isspace(*i))
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser"));
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file Parser.h
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblll/Exceptions.h>
|
||||
#include <libsolutil/Common.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost { namespace spirit { class utree; } }
|
||||
namespace sp = boost::spirit;
|
||||
|
||||
namespace solidity::lll
|
||||
{
|
||||
|
||||
void killBigints(sp::utree const& _this);
|
||||
void parseTreeLLL(std::string const& _s, sp::utree& o_out);
|
||||
void debugOutAST(std::ostream& _out, sp::utree const& _this);
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
add_executable(lllc main.cpp)
|
||||
target_link_libraries(lllc PRIVATE lll Boost::boost Boost::system)
|
||||
|
||||
if (INSTALL_LLLC)
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS lllc DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(LLLC_LINK_STATIC AND UNIX AND NOT APPLE)
|
||||
# Produce lllc as statically linked binary (includes C/C++ standard libraries)
|
||||
# This is not supported on macOS, see
|
||||
# https://developer.apple.com/library/content/qa/qa1118/_index.html.
|
||||
set_target_properties(
|
||||
lllc PROPERTIES
|
||||
LINK_FLAGS -static
|
||||
LINK_SEARCH_START_STATIC ON
|
||||
LINK_SEARCH_END_STATIC ON
|
||||
)
|
||||
endif()
|
||||
endif()
|
157
lllc/main.cpp
157
lllc/main.cpp
@ -1,157 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file main.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
* Ethereum client.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <clocale>
|
||||
#include <liblll/Compiler.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <solidity/BuildInfo.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::lll;
|
||||
|
||||
static string const VersionString =
|
||||
string(ETH_PROJECT_VERSION) +
|
||||
(string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) +
|
||||
(string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO));
|
||||
|
||||
static void help()
|
||||
{
|
||||
cout
|
||||
<< "Usage lllc [OPTIONS] <file>" << endl
|
||||
<< "Options:" << endl
|
||||
<< " -b,--binary Parse, compile and assemble; output byte code in binary." << endl
|
||||
<< " -x,--hex Parse, compile and assemble; output byte code in hex." << endl
|
||||
<< " -a,--assembly Only parse and compile; show assembly." << endl
|
||||
<< " -t,--parse-tree Only parse; show parse tree." << endl
|
||||
<< " -o,--optimise Turn on/off the optimiser; off by default." << endl
|
||||
<< " -d,--disassemble Disassemble input into an opcode stream." << endl
|
||||
<< " -h,--help Show this help message and exit." << endl
|
||||
<< " -V,--version Show the version and exit." << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void version()
|
||||
{
|
||||
cout << "LLLC, the Lovely Little Language Compiler" << endl;
|
||||
cout << "Version: " << VersionString << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
The equivalent of setlocale(LC_ALL, "C") is called before any user code is run.
|
||||
If the user has an invalid environment setting then it is possible for the call
|
||||
to set locale to fail, so there are only two possible actions, the first is to
|
||||
throw a runtime exception and cause the program to quit (default behaviour),
|
||||
or the second is to modify the environment to something sensible (least
|
||||
surprising behaviour).
|
||||
|
||||
The follow code produces the least surprising behaviour. It will use the user
|
||||
specified default locale if it is valid, and if not then it will modify the
|
||||
environment the process is running in to use a sensible default. This also means
|
||||
that users do not need to install language packs for their OS.
|
||||
*/
|
||||
static void setDefaultOrCLocale()
|
||||
{
|
||||
#if __unix__
|
||||
if (!std::setlocale(LC_ALL, ""))
|
||||
{
|
||||
setenv("LC_ALL", "C", 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
enum Mode { Binary, Hex, Assembly, ParseTree, Disassemble };
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
setDefaultOrCLocale();
|
||||
unsigned optimise = 0;
|
||||
string infile;
|
||||
Mode mode = Hex;
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
string arg = argv[i];
|
||||
if (arg == "-h" || arg == "--help")
|
||||
help();
|
||||
else if (arg == "-b" || arg == "--binary")
|
||||
mode = Binary;
|
||||
else if (arg == "-x" || arg == "--hex")
|
||||
mode = Hex;
|
||||
else if (arg == "-a" || arg == "--assembly")
|
||||
mode = Assembly;
|
||||
else if (arg == "-t" || arg == "--parse-tree")
|
||||
mode = ParseTree;
|
||||
else if (arg == "-o" || arg == "--optimise")
|
||||
optimise = 1;
|
||||
else if (arg == "-d" || arg == "--disassemble")
|
||||
mode = Disassemble;
|
||||
else if (arg == "-V" || arg == "--version")
|
||||
version();
|
||||
else
|
||||
infile = argv[i];
|
||||
}
|
||||
|
||||
string src;
|
||||
if (infile.empty())
|
||||
src = readStandardInput();
|
||||
else
|
||||
src = readFileAsString(infile);
|
||||
|
||||
vector<string> errors;
|
||||
if (src.empty())
|
||||
{
|
||||
errors.push_back("Empty file.");
|
||||
}
|
||||
else if (mode == Disassemble)
|
||||
{
|
||||
cout << evmasm::disassemble(fromHex(src)) << endl;
|
||||
}
|
||||
else if (mode == Binary || mode == Hex)
|
||||
{
|
||||
auto bs = compileLLL(std::move(src), langutil::EVMVersion{}, optimise ? true : false, &errors, readFileAsString);
|
||||
if (mode == Hex)
|
||||
cout << toHex(bs) << endl;
|
||||
else if (mode == Binary)
|
||||
cout.write((char const*)bs.data(), bs.size());
|
||||
}
|
||||
else if (mode == ParseTree)
|
||||
{
|
||||
cout << parseLLL(std::move(src)) << endl;
|
||||
}
|
||||
else if (mode == Assembly)
|
||||
{
|
||||
cout << compileLLLToAsm(std::move(src), langutil::EVMVersion{}, optimise ? true : false, &errors, readFileAsString) << endl;
|
||||
}
|
||||
|
||||
for (auto const& i: errors)
|
||||
cerr << i << endl;
|
||||
if (errors.size())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
@ -51,19 +51,6 @@ set(liblangutil_sources
|
||||
)
|
||||
detect_stray_source_files("${liblangutil_sources}" "liblangutil/")
|
||||
|
||||
if(LLL)
|
||||
set (liblll_sources
|
||||
liblll/Compiler.cpp
|
||||
liblll/EndToEndTest.cpp
|
||||
liblll/ExecutionFramework.cpp
|
||||
liblll/ExecutionFramework.h
|
||||
liblll/LLL_ENS.cpp
|
||||
liblll/LLL_ERC20.cpp
|
||||
liblll/Parser.cpp
|
||||
)
|
||||
detect_stray_source_files("${liblll_sources}" "liblll/")
|
||||
endif(LLL)
|
||||
|
||||
set(libsolidity_sources
|
||||
libsolidity/ABIDecoderTests.cpp
|
||||
libsolidity/ABIEncoderTests.cpp
|
||||
@ -155,7 +142,6 @@ add_executable(soltest ${sources}
|
||||
${liblangutil_sources}
|
||||
${libevmasm_sources}
|
||||
${libyul_sources}
|
||||
${liblll_sources}
|
||||
${libsolidity_sources}
|
||||
${libsolidity_util_sources}
|
||||
)
|
||||
@ -170,11 +156,6 @@ if (MSVC)
|
||||
target_compile_options(soltest PUBLIC "/bigobj")
|
||||
endif()
|
||||
|
||||
if (LLL)
|
||||
target_link_libraries(soltest PRIVATE lll)
|
||||
target_compile_definitions(soltest PRIVATE HAVE_LLL=1)
|
||||
endif()
|
||||
|
||||
if (NOT Boost_USE_STATIC_LIBS)
|
||||
target_compile_definitions(soltest PUBLIC -DBOOST_TEST_DYN_LINK)
|
||||
endif()
|
||||
|
@ -164,11 +164,6 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
||||
"SolidityAuctionRegistrar",
|
||||
"SolidityFixedFeeRegistrar",
|
||||
"SolidityWallet",
|
||||
#if HAVE_LLL
|
||||
"LLLERC20",
|
||||
"LLLENS",
|
||||
"LLLEndToEndTest",
|
||||
#endif
|
||||
"GasMeterTests",
|
||||
"GasCostTests",
|
||||
"SolidityEndToEndTest",
|
||||
|
@ -1,670 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Alex Beregszaszi
|
||||
* @date 2017
|
||||
* Unit tests for the LLL compiler.
|
||||
*/
|
||||
|
||||
#include <test/Options.h>
|
||||
|
||||
#include <libsolutil/FixedHash.h>
|
||||
|
||||
#include <liblll/Compiler.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::util;
|
||||
|
||||
namespace solidity::lll::test
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool successCompile(string const& _sourceCode)
|
||||
{
|
||||
vector<string> errors;
|
||||
bytes bytecode = lll::compileLLL(_sourceCode, solidity::test::Options::get().evmVersion(), false, &errors);
|
||||
if (!errors.empty())
|
||||
return false;
|
||||
if (bytecode.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(LLLCompiler)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(smoke_test)
|
||||
{
|
||||
char const* sourceCode = "1";
|
||||
BOOST_CHECK(successCompile(sourceCode));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(switch_valid)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
(switch (origin))
|
||||
)";
|
||||
BOOST_CHECK(successCompile(sourceCode));
|
||||
sourceCode = R"(
|
||||
(switch
|
||||
1 (panic)
|
||||
2 (panic))
|
||||
)";
|
||||
BOOST_CHECK(successCompile(sourceCode));
|
||||
sourceCode = R"(
|
||||
(switch
|
||||
1 (panic)
|
||||
2 (panic)
|
||||
(panic))
|
||||
)";
|
||||
BOOST_CHECK(successCompile(sourceCode));
|
||||
sourceCode = R"(
|
||||
(switch
|
||||
1 (origin)
|
||||
2 (origin)
|
||||
(origin))
|
||||
)";
|
||||
BOOST_CHECK(successCompile(sourceCode));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(switch_invalid_arg_count)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
(switch)
|
||||
)";
|
||||
BOOST_CHECK(!successCompile(sourceCode));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(switch_inconsistent_return_count)
|
||||
{
|
||||
// cannot return stack items if the default case is not present
|
||||
char const* sourceCode = R"(
|
||||
(switch
|
||||
1 (origin)
|
||||
2 (origin)
|
||||
)";
|
||||
BOOST_CHECK(!successCompile(sourceCode));
|
||||
// return count mismatch
|
||||
sourceCode = R"(
|
||||
(switch
|
||||
1 (origin)
|
||||
2 (origin)
|
||||
(panic))
|
||||
)";
|
||||
BOOST_CHECK(!successCompile(sourceCode));
|
||||
// return count mismatch
|
||||
sourceCode = R"(
|
||||
(switch
|
||||
1 (panic)
|
||||
2 (panic)
|
||||
(origin))
|
||||
)";
|
||||
BOOST_CHECK(!successCompile(sourceCode));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(disallowed_asm_instructions)
|
||||
{
|
||||
for (unsigned i = 1; i <= 32; i++)
|
||||
BOOST_CHECK(!successCompile("(asm PUSH" + to_string(i) + ")"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(disallowed_functional_asm_instructions)
|
||||
{
|
||||
for (unsigned i = 1; i <= 32; i++)
|
||||
BOOST_CHECK(!successCompile("(PUSH" + to_string(i) + ")"));
|
||||
for (unsigned i = 1; i <= 16; i++)
|
||||
BOOST_CHECK(!successCompile("(DUP" + to_string(i) + ")"));
|
||||
for (unsigned i = 1; i <= 16; i++)
|
||||
BOOST_CHECK(!successCompile("(SWAP" + to_string(i) + ")"));
|
||||
BOOST_CHECK(!successCompile("(JUMPDEST)"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(valid_opcodes_functional)
|
||||
{
|
||||
vector<string> opcodes_bytecode {
|
||||
"0000",
|
||||
"600060000100",
|
||||
"600060000200",
|
||||
"600060000300",
|
||||
"600060000400",
|
||||
"600060000500",
|
||||
"600060000600",
|
||||
"600060000700",
|
||||
"6000600060000800",
|
||||
"6000600060000900",
|
||||
"600060000a00",
|
||||
"600060000b00",
|
||||
"600060001000",
|
||||
"600060001100",
|
||||
"600060001200",
|
||||
"600060001300",
|
||||
"600060001400",
|
||||
"60001500",
|
||||
"600060001600",
|
||||
"600060001700",
|
||||
"600060001800",
|
||||
"60001900",
|
||||
"600060001a00",
|
||||
"600060002000",
|
||||
"3000",
|
||||
"60003100",
|
||||
"3200",
|
||||
"3300",
|
||||
"3400",
|
||||
"60003500",
|
||||
"3600",
|
||||
"6000600060003700",
|
||||
"3800",
|
||||
"6000600060003900",
|
||||
"3a00",
|
||||
"60003b00",
|
||||
"60006000600060003c00",
|
||||
"3d00",
|
||||
"6000600060003e00",
|
||||
"60003f00",
|
||||
"60004000",
|
||||
"4100",
|
||||
"4200",
|
||||
"4300",
|
||||
"4400",
|
||||
"4500",
|
||||
"4600",
|
||||
"4700",
|
||||
"60005000",
|
||||
"60005100",
|
||||
"600060005200",
|
||||
"600060005300",
|
||||
"60005400",
|
||||
"600060005500",
|
||||
"60005600",
|
||||
"600060005700",
|
||||
"5800",
|
||||
"5900",
|
||||
"5a00",
|
||||
"60ff00",
|
||||
"61ffff00",
|
||||
"62ffffff00",
|
||||
"63ffffffff00",
|
||||
"64ffffffffff00",
|
||||
"65ffffffffffff00",
|
||||
"66ffffffffffffff00",
|
||||
"67ffffffffffffffff00",
|
||||
"68ffffffffffffffffff00",
|
||||
"69ffffffffffffffffffff00",
|
||||
"6affffffffffffffffffffff00",
|
||||
"6bffffffffffffffffffffffff00",
|
||||
"6cffffffffffffffffffffffffff00",
|
||||
"6dffffffffffffffffffffffffffff00",
|
||||
"6effffffffffffffffffffffffffffff00",
|
||||
"6fffffffffffffffffffffffffffffffff00",
|
||||
"70ffffffffffffffffffffffffffffffffff00",
|
||||
"71ffffffffffffffffffffffffffffffffffff00",
|
||||
"72ffffffffffffffffffffffffffffffffffffff00",
|
||||
"73ffffffffffffffffffffffffffffffffffffffff00",
|
||||
"74ffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"75ffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"76ffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"77ffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"78ffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"79ffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7affffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"60006000a000",
|
||||
"600060006000a100",
|
||||
"6000600060006000a200",
|
||||
"60006000600060006000a300",
|
||||
"600060006000600060006000a400",
|
||||
"600060006000f000",
|
||||
"6000600060006000600060006000f100",
|
||||
"6000600060006000600060006000f200",
|
||||
"60006000f300",
|
||||
"600060006000600060006000f400",
|
||||
"600060006000600060006000fa00",
|
||||
"60006000fd00",
|
||||
"fe00",
|
||||
"6000ff00"
|
||||
};
|
||||
|
||||
vector<string> opcodes_lll {
|
||||
"(STOP)",
|
||||
"(ADD 0 0)",
|
||||
"(MUL 0 0)",
|
||||
"(SUB 0 0)",
|
||||
"(DIV 0 0)",
|
||||
"(SDIV 0 0)",
|
||||
"(MOD 0 0)",
|
||||
"(SMOD 0 0)",
|
||||
"(ADDMOD 0 0 0)",
|
||||
"(MULMOD 0 0 0)",
|
||||
"(EXP 0 0)",
|
||||
"(SIGNEXTEND 0 0)",
|
||||
"(LT 0 0)",
|
||||
"(GT 0 0)",
|
||||
"(SLT 0 0)",
|
||||
"(SGT 0 0)",
|
||||
"(EQ 0 0)",
|
||||
"(ISZERO 0)",
|
||||
"(AND 0 0)",
|
||||
"(OR 0 0)",
|
||||
"(XOR 0 0)",
|
||||
"(NOT 0)",
|
||||
"(BYTE 0 0)",
|
||||
"(KECCAK256 0 0)",
|
||||
"(ADDRESS)",
|
||||
"(BALANCE 0)",
|
||||
"(ORIGIN)",
|
||||
"(CALLER)",
|
||||
"(CALLVALUE)",
|
||||
"(CALLDATALOAD 0)",
|
||||
"(CALLDATASIZE)",
|
||||
"(CALLDATACOPY 0 0 0)",
|
||||
"(CODESIZE)",
|
||||
"(CODECOPY 0 0 0)",
|
||||
"(GASPRICE)",
|
||||
"(EXTCODESIZE 0)",
|
||||
"(EXTCODECOPY 0 0 0 0)",
|
||||
"(RETURNDATASIZE)",
|
||||
"(RETURNDATACOPY 0 0 0)",
|
||||
"(EXTCODEHASH 0)",
|
||||
"(BLOCKHASH 0)",
|
||||
"(COINBASE)",
|
||||
"(TIMESTAMP)",
|
||||
"(NUMBER)",
|
||||
"(DIFFICULTY)",
|
||||
"(GASLIMIT)",
|
||||
"(CHAINID)",
|
||||
"(SELFBALANCE)",
|
||||
"(POP 0)",
|
||||
"(MLOAD 0)",
|
||||
"(MSTORE 0 0)",
|
||||
"(MSTORE8 0 0)",
|
||||
"(SLOAD 0)",
|
||||
"(SSTORE 0 0)",
|
||||
"(JUMP 0)",
|
||||
"(JUMPI 0 0)",
|
||||
"(PC)",
|
||||
"(MSIZE)",
|
||||
"(GAS)",
|
||||
"0xff",
|
||||
"0xffff",
|
||||
"0xffffff",
|
||||
"0xffffffff",
|
||||
"0xffffffffff",
|
||||
"0xffffffffffff",
|
||||
"0xffffffffffffff",
|
||||
"0xffffffffffffffff",
|
||||
"0xffffffffffffffffff",
|
||||
"0xffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"(LOG0 0 0)",
|
||||
"(LOG1 0 0 0)",
|
||||
"(LOG2 0 0 0 0)",
|
||||
"(LOG3 0 0 0 0 0)",
|
||||
"(LOG4 0 0 0 0 0 0)",
|
||||
"(CREATE 0 0 0)",
|
||||
"(CALL 0 0 0 0 0 0 0)",
|
||||
"(CALLCODE 0 0 0 0 0 0 0)",
|
||||
"(RETURN 0 0)",
|
||||
"(DELEGATECALL 0 0 0 0 0 0)",
|
||||
"(STATICCALL 0 0 0 0 0 0)",
|
||||
"(REVERT 0 0)",
|
||||
"(INVALID)",
|
||||
"(SELFDESTRUCT 0)"
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < opcodes_bytecode.size(); i++)
|
||||
{
|
||||
vector<string> errors;
|
||||
bytes code = lll::compileLLL(opcodes_lll[i], solidity::test::Options::get().evmVersion(), false, &errors);
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]);
|
||||
|
||||
BOOST_CHECK_EQUAL(toHex(code), opcodes_bytecode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(valid_opcodes_asm)
|
||||
{
|
||||
vector<string> opcodes_bytecode {
|
||||
"0000",
|
||||
"600060000100",
|
||||
"600060000200",
|
||||
"600060000300",
|
||||
"600060000400",
|
||||
"600060000500",
|
||||
"600060000600",
|
||||
"600060000700",
|
||||
"6000600060000800",
|
||||
"6000600060000900",
|
||||
"600060000a00",
|
||||
"600060000b00",
|
||||
"600060001000",
|
||||
"600060001100",
|
||||
"600060001200",
|
||||
"600060001300",
|
||||
"600060001400",
|
||||
"60001500",
|
||||
"600060001600",
|
||||
"600060001700",
|
||||
"600060001800",
|
||||
"60001900",
|
||||
"600060001a00",
|
||||
"600060002000",
|
||||
"3000",
|
||||
"60003100",
|
||||
"3200",
|
||||
"3300",
|
||||
"3400",
|
||||
"60003500",
|
||||
"3600",
|
||||
"6000600060003700",
|
||||
"3800",
|
||||
"6000600060003900",
|
||||
"3a00",
|
||||
"60003b00",
|
||||
"60006000600060003c00",
|
||||
"3d00",
|
||||
"6000600060003e00",
|
||||
"60003f00",
|
||||
"4000",
|
||||
"4100",
|
||||
"4200",
|
||||
"4300",
|
||||
"4400",
|
||||
"4500",
|
||||
"4600",
|
||||
"4700",
|
||||
"60005000",
|
||||
"60005100",
|
||||
"600060005200",
|
||||
"600060005300",
|
||||
"60005400",
|
||||
"600060005500",
|
||||
"60005600",
|
||||
"600060005700",
|
||||
"5800",
|
||||
"5900",
|
||||
"5a00",
|
||||
"5b00",
|
||||
"60ff00",
|
||||
"61ffff00",
|
||||
"62ffffff00",
|
||||
"63ffffffff00",
|
||||
"64ffffffffff00",
|
||||
"65ffffffffffff00",
|
||||
"66ffffffffffffff00",
|
||||
"67ffffffffffffffff00",
|
||||
"68ffffffffffffffffff00",
|
||||
"69ffffffffffffffffffff00",
|
||||
"6affffffffffffffffffffff00",
|
||||
"6bffffffffffffffffffffffff00",
|
||||
"6cffffffffffffffffffffffffff00",
|
||||
"6dffffffffffffffffffffffffffff00",
|
||||
"6effffffffffffffffffffffffffffff00",
|
||||
"6fffffffffffffffffffffffffffffffff00",
|
||||
"70ffffffffffffffffffffffffffffffffff00",
|
||||
"71ffffffffffffffffffffffffffffffffffff00",
|
||||
"72ffffffffffffffffffffffffffffffffffffff00",
|
||||
"73ffffffffffffffffffffffffffffffffffffffff00",
|
||||
"74ffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"75ffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"76ffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"77ffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"78ffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"79ffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7affffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008000",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008100",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008200",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008300",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008400",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008500",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008600",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008700",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008800",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008900",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008a00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008b00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008c00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008d00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008e00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060008f00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009000",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009100",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009200",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009300",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009400",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009500",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009600",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009700",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009800",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009900",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009a00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009b00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009c00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009d00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009e00",
|
||||
"60006000600060006000600060006000600060006000600060006000600060009f00",
|
||||
"60006000a000",
|
||||
"600060006000a100",
|
||||
"6000600060006000a200",
|
||||
"60006000600060006000a300",
|
||||
"600060006000600060006000a400",
|
||||
"600060006000f000",
|
||||
"600060006000600060006000f100",
|
||||
"600060006000600060006000f200",
|
||||
"60006000f300",
|
||||
"60006000600060006000f400",
|
||||
"60006000600060006000fa00",
|
||||
"60006000fd00",
|
||||
"fe00",
|
||||
"6000ff00"
|
||||
};
|
||||
|
||||
vector<string> opcodes_lll {
|
||||
"(asm STOP)",
|
||||
"(asm 0 0 ADD)",
|
||||
"(asm 0 0 MUL)",
|
||||
"(asm 0 0 SUB)",
|
||||
"(asm 0 0 DIV)",
|
||||
"(asm 0 0 SDIV)",
|
||||
"(asm 0 0 MOD)",
|
||||
"(asm 0 0 SMOD)",
|
||||
"(asm 0 0 0 ADDMOD)",
|
||||
"(asm 0 0 0 MULMOD)",
|
||||
"(asm 0 0 EXP)",
|
||||
"(asm 0 0 SIGNEXTEND)",
|
||||
"(asm 0 0 LT)",
|
||||
"(asm 0 0 GT)",
|
||||
"(asm 0 0 SLT)",
|
||||
"(asm 0 0 SGT)",
|
||||
"(asm 0 0 EQ)",
|
||||
"(asm 0 ISZERO)",
|
||||
"(asm 0 0 AND)",
|
||||
"(asm 0 0 OR)",
|
||||
"(asm 0 0 XOR)",
|
||||
"(asm 0 NOT)",
|
||||
"(asm 0 0 BYTE)",
|
||||
"(asm 0 0 KECCAK256)",
|
||||
"(asm ADDRESS)",
|
||||
"(asm 0 BALANCE)",
|
||||
"(asm ORIGIN)",
|
||||
"(asm CALLER)",
|
||||
"(asm CALLVALUE)",
|
||||
"(asm 0 CALLDATALOAD)",
|
||||
"(asm CALLDATASIZE)",
|
||||
"(asm 0 0 0 CALLDATACOPY)",
|
||||
"(asm CODESIZE)",
|
||||
"(asm 0 0 0 CODECOPY)",
|
||||
"(asm GASPRICE)",
|
||||
"(asm 0 EXTCODESIZE)",
|
||||
"(asm 0 0 0 0 EXTCODECOPY)",
|
||||
"(asm RETURNDATASIZE)",
|
||||
"(asm 0 0 0 RETURNDATACOPY)",
|
||||
"(asm 0 EXTCODEHASH)",
|
||||
"(asm BLOCKHASH)",
|
||||
"(asm COINBASE)",
|
||||
"(asm TIMESTAMP)",
|
||||
"(asm NUMBER)",
|
||||
"(asm DIFFICULTY)",
|
||||
"(asm GASLIMIT)",
|
||||
"(asm CHAINID)",
|
||||
"(asm SELFBALANCE)",
|
||||
"(asm 0 POP)",
|
||||
"(asm 0 MLOAD)",
|
||||
"(asm 0 0 MSTORE)",
|
||||
"(asm 0 0 MSTORE8)",
|
||||
"(asm 0 SLOAD)",
|
||||
"(asm 0 0 SSTORE)",
|
||||
"(asm 0 JUMP)",
|
||||
"(asm 0 0 JUMPI)",
|
||||
"(asm PC)",
|
||||
"(asm MSIZE)",
|
||||
"(asm GAS)",
|
||||
"(asm JUMPDEST)",
|
||||
"(asm 0xff)",
|
||||
"(asm 0xffff)",
|
||||
"(asm 0xffffff)",
|
||||
"(asm 0xffffffff)",
|
||||
"(asm 0xffffffffff)",
|
||||
"(asm 0xffffffffffff)",
|
||||
"(asm 0xffffffffffffff)",
|
||||
"(asm 0xffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP1)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP2)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP3)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP4)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP5)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP6)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP7)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP8)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP9)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP10)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP11)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP12)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP13)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP14)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP15)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP16)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP1)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP2)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP3)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP4)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP5)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP6)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP7)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP8)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP9)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP10)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP11)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP12)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP13)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP14)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP15)",
|
||||
"(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP16)",
|
||||
"(asm 0 0 LOG0)",
|
||||
"(asm 0 0 0 LOG1)",
|
||||
"(asm 0 0 0 0 LOG2)",
|
||||
"(asm 0 0 0 0 0 LOG3)",
|
||||
"(asm 0 0 0 0 0 0 LOG4)",
|
||||
"(asm 0 0 0 CREATE)",
|
||||
"(asm 0 0 0 0 0 0 CALL)",
|
||||
"(asm 0 0 0 0 0 0 CALLCODE)",
|
||||
"(asm 0 0 RETURN)",
|
||||
"(asm 0 0 0 0 0 DELEGATECALL)",
|
||||
"(asm 0 0 0 0 0 STATICCALL)",
|
||||
"(asm 0 0 REVERT)",
|
||||
"(asm INVALID)",
|
||||
"(asm 0 SELFDESTRUCT)"
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < opcodes_bytecode.size(); i++)
|
||||
{
|
||||
vector<string> errors;
|
||||
bytes code = lll::compileLLL(opcodes_lll[i], solidity::test::Options::get().evmVersion(), false, &errors);
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]);
|
||||
|
||||
BOOST_CHECK_EQUAL(toHex(code), opcodes_bytecode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // end namespaces
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Alex Beregszaszi
|
||||
* @date 2016
|
||||
* Framework for executing LLL contracts and testing them via RPC.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <boost/test/framework.hpp>
|
||||
#include <test/liblll/ExecutionFramework.h>
|
||||
|
||||
using namespace solidity::test;
|
||||
using namespace solidity::lll::test;
|
||||
|
||||
LLLExecutionFramework::LLLExecutionFramework() :
|
||||
ExecutionFramework()
|
||||
{
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Alex Beregszaszi
|
||||
* @date 2016
|
||||
* Framework for executing LLL contracts and testing them via RPC.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <test/ExecutionFramework.h>
|
||||
|
||||
#include <liblll/Compiler.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace solidity::lll::test
|
||||
{
|
||||
|
||||
class LLLExecutionFramework: public solidity::test::ExecutionFramework
|
||||
{
|
||||
public:
|
||||
LLLExecutionFramework();
|
||||
|
||||
virtual bytes const& compileAndRunWithoutCheck(
|
||||
std::string const& _sourceCode,
|
||||
u256 const& _value = 0,
|
||||
std::string const& _contractName = "",
|
||||
bytes const& _arguments = bytes(),
|
||||
std::map<std::string, solidity::test::Address> const& _libraryAddresses = {}
|
||||
) override
|
||||
{
|
||||
BOOST_REQUIRE(_contractName.empty());
|
||||
BOOST_REQUIRE(_libraryAddresses.empty());
|
||||
|
||||
std::vector<std::string> errors;
|
||||
bytes bytecode = lll::compileLLL(
|
||||
_sourceCode,
|
||||
solidity::test::Options::get().evmVersion(),
|
||||
m_optimiserSettings == solidity::frontend::OptimiserSettings::standard(),
|
||||
&errors
|
||||
);
|
||||
if (!errors.empty())
|
||||
{
|
||||
for (auto const& error: errors)
|
||||
std::cerr << error << std::endl;
|
||||
BOOST_ERROR("Compiling contract failed");
|
||||
}
|
||||
sendMessage(bytecode + _arguments, true, _value);
|
||||
return m_output;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespaces
|
@ -1,503 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Ben Edgington <ben@benjaminion.xyz>
|
||||
* @date 2017
|
||||
* Tests for the deployed ENS Registry implementation written in LLL
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <test/liblll/ExecutionFramework.h>
|
||||
#include <liblll/Compiler.h>
|
||||
|
||||
#define ACCOUNT(n) h256(account(n), h256::AlignRight)
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::lll;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::test;
|
||||
|
||||
namespace solidity::lll::test
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static char const* ensCode = R"DELIMITER(
|
||||
;;; ---------------------------------------------------------------------------
|
||||
;;; @title The Ethereum Name Service registry.
|
||||
;;; @author Daniel Ellison <daniel@syrinx.net>
|
||||
|
||||
(seq
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Constant definitions.
|
||||
|
||||
;; Memory layout.
|
||||
(def 'node-bytes 0x00)
|
||||
(def 'label-bytes 0x20)
|
||||
(def 'call-result 0x40)
|
||||
|
||||
;; Struct: Record
|
||||
(def 'resolver 0x00) ; address
|
||||
(def 'owner 0x20) ; address
|
||||
(def 'ttl 0x40) ; uint64
|
||||
|
||||
;; Precomputed function IDs.
|
||||
(def 'get-node-owner 0x02571be3) ; owner(bytes32)
|
||||
(def 'get-node-resolver 0x0178b8bf) ; resolver(bytes32)
|
||||
(def 'get-node-ttl 0x16a25cbd) ; ttl(bytes32)
|
||||
(def 'set-node-owner 0x5b0fc9c3) ; setOwner(bytes32,address)
|
||||
(def 'set-subnode-owner 0x06ab5923) ; setSubnodeOwner(bytes32,bytes32,address)
|
||||
(def 'set-node-resolver 0x1896f70a) ; setResolver(bytes32,address)
|
||||
(def 'set-node-ttl 0x14ab9038) ; setTTL(bytes32,uint64)
|
||||
|
||||
;; Jumping here causes an EVM error.
|
||||
(def 'invalid-location 0x02)
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Shifts the leftmost 4 bytes of a 32-byte number right by 28 bytes.
|
||||
;; @param input A 32-byte number.
|
||||
|
||||
(def 'shift-right (input)
|
||||
(div input (exp 2 224)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Determines whether the supplied function ID matches a known
|
||||
;; function hash and executes <code-body> if so.
|
||||
;; @dev The function ID is in the leftmost four bytes of the call data.
|
||||
;; @param function-hash The four-byte hash of a known function signature.
|
||||
;; @param code-body The code to run in the case of a match.
|
||||
|
||||
(def 'function (function-hash code-body)
|
||||
(when (= (shift-right (calldataload 0x00)) function-hash)
|
||||
code-body))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Calculates record location for the node and label passed in.
|
||||
;; @param node The parent node.
|
||||
;; @param label The hash of the subnode label.
|
||||
|
||||
(def 'get-record (node label)
|
||||
(seq
|
||||
(mstore node-bytes node)
|
||||
(mstore label-bytes label)
|
||||
(sha3 node-bytes 64)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Retrieves owner from node record.
|
||||
;; @param node Get owner of this node.
|
||||
|
||||
(def 'get-owner (node)
|
||||
(sload (+ node owner)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Stores new owner in node record.
|
||||
;; @param node Set owner of this node.
|
||||
;; @param new-owner New owner of this node.
|
||||
|
||||
(def 'set-owner (node new-owner)
|
||||
(sstore (+ node owner) new-owner))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Stores new subnode owner in node record.
|
||||
;; @param node Set owner of this node.
|
||||
;; @param label The hash of the label specifying the subnode.
|
||||
;; @param new-owner New owner of the subnode.
|
||||
|
||||
(def 'set-subowner (node label new-owner)
|
||||
(sstore (+ (get-record node label) owner) new-owner))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Retrieves resolver from node record.
|
||||
;; @param node Get resolver of this node.
|
||||
|
||||
(def 'get-resolver (node)
|
||||
(sload node))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Stores new resolver in node record.
|
||||
;; @param node Set resolver of this node.
|
||||
;; @param new-resolver New resolver for this node.
|
||||
|
||||
(def 'set-resolver (node new-resolver)
|
||||
(sstore node new-resolver))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Retrieves TTL From node record.
|
||||
;; @param node Get TTL of this node.
|
||||
|
||||
(def 'get-ttl (node)
|
||||
(sload (+ node ttl)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Stores new TTL in node record.
|
||||
;; @param node Set TTL of this node.
|
||||
;; @param new-resolver New TTL for this node.
|
||||
|
||||
(def 'set-ttl (node new-ttl)
|
||||
(sstore (+ node ttl) new-ttl))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; @notice Checks that the caller is the node owner.
|
||||
;; @param node Check owner of this node.
|
||||
|
||||
(def 'only-node-owner (node)
|
||||
(when (!= (caller) (get-owner node))
|
||||
(jump invalid-location)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; INIT
|
||||
|
||||
;; Set the owner of the root node (0x00) to the deploying account.
|
||||
(set-owner 0x00 (caller))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; CODE
|
||||
|
||||
(returnlll
|
||||
(seq
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Returns the address of the resolver for the specified node.
|
||||
;; @dev Signature: resolver(bytes32)
|
||||
;; @param node Return this node's resolver.
|
||||
;; @return The associated resolver.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
|
||||
(function get-node-resolver
|
||||
(seq
|
||||
|
||||
;; Get the node's resolver and save it.
|
||||
(mstore call-result (get-resolver node))
|
||||
|
||||
;; Return result.
|
||||
(return call-result 32)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Returns the address that owns the specified node.
|
||||
;; @dev Signature: owner(bytes32)
|
||||
;; @param node Return this node's owner.
|
||||
;; @return The associated address.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
|
||||
(function get-node-owner
|
||||
(seq
|
||||
|
||||
;; Get the node's owner and save it.
|
||||
(mstore call-result (get-owner node))
|
||||
|
||||
;; Return result.
|
||||
(return call-result 32)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Returns the TTL of a node and any records associated with it.
|
||||
;; @dev Signature: ttl(bytes32)
|
||||
;; @param node Return this node's TTL.
|
||||
;; @return The node's TTL.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
|
||||
(function get-node-ttl
|
||||
(seq
|
||||
|
||||
;; Get the node's TTL and save it.
|
||||
(mstore call-result (get-ttl node))
|
||||
|
||||
;; Return result.
|
||||
(return call-result 32)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Transfers ownership of a node to a new address. May only be
|
||||
;; called by the current owner of the node.
|
||||
;; @dev Signature: setOwner(bytes32,address)
|
||||
;; @param node The node to transfer ownership of.
|
||||
;; @param new-owner The address of the new owner.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
(def 'new-owner (calldataload 0x24))
|
||||
|
||||
(function set-node-owner
|
||||
(seq (only-node-owner node)
|
||||
|
||||
;; Transfer ownership by storing passed-in address.
|
||||
(set-owner node new-owner)
|
||||
|
||||
;; Emit an event about the transfer.
|
||||
;; Transfer(bytes32 indexed node, address owner);
|
||||
(mstore call-result new-owner)
|
||||
(log2 call-result 32
|
||||
(sha3 0x00 (lit 0x00 "Transfer(bytes32,address)")) node)
|
||||
|
||||
;; Nothing to return.
|
||||
(stop)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Transfers ownership of a subnode to a new address. May only be
|
||||
;; called by the owner of the parent node.
|
||||
;; @dev Signature: setSubnodeOwner(bytes32,bytes32,address)
|
||||
;; @param node The parent node.
|
||||
;; @param label The hash of the label specifying the subnode.
|
||||
;; @param new-owner The address of the new owner.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
(def 'label (calldataload 0x24))
|
||||
(def 'new-owner (calldataload 0x44))
|
||||
|
||||
(function set-subnode-owner
|
||||
(seq (only-node-owner node)
|
||||
|
||||
;; Transfer ownership by storing passed-in address.
|
||||
(set-subowner node label new-owner)
|
||||
|
||||
;; Emit an event about the transfer.
|
||||
;; NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
|
||||
(mstore call-result new-owner)
|
||||
(log3 call-result 32
|
||||
(sha3 0x00 (lit 0x00 "NewOwner(bytes32,bytes32,address)"))
|
||||
node label)
|
||||
|
||||
;; Nothing to return.
|
||||
(stop)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Sets the resolver address for the specified node.
|
||||
;; @dev Signature: setResolver(bytes32,address)
|
||||
;; @param node The node to update.
|
||||
;; @param new-resolver The address of the resolver.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
(def 'new-resolver (calldataload 0x24))
|
||||
|
||||
(function set-node-resolver
|
||||
(seq (only-node-owner node)
|
||||
|
||||
;; Transfer ownership by storing passed-in address.
|
||||
(set-resolver node new-resolver)
|
||||
|
||||
;; Emit an event about the change of resolver.
|
||||
;; NewResolver(bytes32 indexed node, address resolver);
|
||||
(mstore call-result new-resolver)
|
||||
(log2 call-result 32
|
||||
(sha3 0x00 (lit 0x00 "NewResolver(bytes32,address)")) node)
|
||||
|
||||
;; Nothing to return.
|
||||
(stop)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Sets the TTL for the specified node.
|
||||
;; @dev Signature: setTTL(bytes32,uint64)
|
||||
;; @param node The node to update.
|
||||
;; @param ttl The TTL in seconds.
|
||||
|
||||
(def 'node (calldataload 0x04))
|
||||
(def 'new-ttl (calldataload 0x24))
|
||||
|
||||
(function set-node-ttl
|
||||
(seq (only-node-owner node)
|
||||
|
||||
;; Set new TTL by storing passed-in time.
|
||||
(set-ttl node new-ttl)
|
||||
|
||||
;; Emit an event about the change of TTL.
|
||||
;; NewTTL(bytes32 indexed node, uint64 ttl);
|
||||
(mstore call-result new-ttl)
|
||||
(log2 call-result 32
|
||||
(sha3 0x00 (lit 0x00 "NewTTL(bytes32,uint64)")) node)
|
||||
|
||||
;; Nothing to return.
|
||||
(stop)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; @notice Fallback: No functions matched the function ID provided.
|
||||
|
||||
(jump invalid-location)))
|
||||
|
||||
)
|
||||
)DELIMITER";
|
||||
|
||||
static unique_ptr<bytes> s_compiledEns;
|
||||
|
||||
class LLLENSTestFramework: public LLLExecutionFramework
|
||||
{
|
||||
protected:
|
||||
void deployEns()
|
||||
{
|
||||
if (!s_compiledEns)
|
||||
{
|
||||
vector<string> errors;
|
||||
s_compiledEns.reset(new bytes(compileLLL(ensCode, solidity::test::Options::get().evmVersion(), solidity::test::Options::get().optimize, &errors)));
|
||||
BOOST_REQUIRE(errors.empty());
|
||||
}
|
||||
sendMessage(*s_compiledEns, true);
|
||||
BOOST_REQUIRE(m_transactionSuccessful);
|
||||
BOOST_REQUIRE(!m_output.empty());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Test suite for the deployed ENS Registry implementation written in LLL
|
||||
BOOST_FIXTURE_TEST_SUITE(LLLENS, LLLENSTestFramework)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(creation)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Root node 0x00 should initially be owned by the deploying account, account(0).
|
||||
BOOST_CHECK(callContractFunction("owner(bytes32)", 0x00) == encodeArgs(ACCOUNT(0)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(transfer_ownership)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Transfer ownership of root node from account(0) to account(1).
|
||||
BOOST_REQUIRE(callContractFunction("setOwner(bytes32,address)", 0x00, ACCOUNT(1)) == encodeArgs());
|
||||
|
||||
// Check that an event was raised and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(ACCOUNT(1)));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 2);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("Transfer(bytes32,address)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == u256(0x00));
|
||||
|
||||
// Verify that owner of 0x00 is now account(1).
|
||||
BOOST_CHECK(callContractFunction("owner(bytes32)", 0x00) == encodeArgs(ACCOUNT(1)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(transfer_ownership_fail)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Try to steal ownership of node 0x01
|
||||
BOOST_REQUIRE(callContractFunction("setOwner(bytes32,address)", 0x01, ACCOUNT(0)) == encodeArgs());
|
||||
|
||||
// Verify that owner of 0x01 remains the default zero address
|
||||
BOOST_CHECK(callContractFunction("owner(bytes32)", 0x01) == encodeArgs(0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(set_resolver)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Set resolver of root node to account(1).
|
||||
BOOST_REQUIRE(callContractFunction("setResolver(bytes32,address)", 0x00, ACCOUNT(1)) == encodeArgs());
|
||||
|
||||
// Check that an event was raised and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(ACCOUNT(1)));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 2);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("NewResolver(bytes32,address)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == u256(0x00));
|
||||
|
||||
// Verify that the resolver is changed to account(1).
|
||||
BOOST_CHECK(callContractFunction("resolver(bytes32)", 0x00) == encodeArgs(ACCOUNT(1)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(set_resolver_fail)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Try to set resolver of node 0x01, which is not owned by account(0).
|
||||
BOOST_REQUIRE(callContractFunction("setResolver(bytes32,address)", 0x01, ACCOUNT(0)) == encodeArgs());
|
||||
|
||||
// Verify that the resolver of 0x01 remains default zero address.
|
||||
BOOST_CHECK(callContractFunction("resolver(bytes32)", 0x01) == encodeArgs(0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(set_ttl)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Set ttl of root node to 3600.
|
||||
BOOST_REQUIRE(callContractFunction("setTTL(bytes32,uint64)", 0x00, 3600) == encodeArgs());
|
||||
|
||||
// Check that an event was raised and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(3600));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 2);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("NewTTL(bytes32,uint64)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == u256(0x00));
|
||||
|
||||
// Verify that the TTL has been set.
|
||||
BOOST_CHECK(callContractFunction("ttl(bytes32)", 0x00) == encodeArgs(3600));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(set_ttl_fail)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Try to set TTL of node 0x01, which is not owned by account(0).
|
||||
BOOST_REQUIRE(callContractFunction("setTTL(bytes32,uint64)", 0x01, 3600) == encodeArgs());
|
||||
|
||||
// Verify that the TTL of node 0x01 has not changed from the default.
|
||||
BOOST_CHECK(callContractFunction("ttl(bytes32)", 0x01) == encodeArgs(0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_subnode)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Set ownership of "eth" sub-node to account(1)
|
||||
BOOST_REQUIRE(callContractFunction("setSubnodeOwner(bytes32,bytes32,address)", 0x00, keccak256(string("eth")), ACCOUNT(1)) == encodeArgs());
|
||||
|
||||
// Check that an event was raised and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(ACCOUNT(1)));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 3);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("NewOwner(bytes32,bytes32,address)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == u256(0x00));
|
||||
BOOST_CHECK(logTopic(0, 2) == keccak256(string("eth")));
|
||||
|
||||
// Verify that the sub-node owner is now account(1).
|
||||
u256 namehash = keccak256(h256(0x00).asBytes() + keccak256("eth").asBytes());
|
||||
BOOST_CHECK(callContractFunction("owner(bytes32)", namehash) == encodeArgs(ACCOUNT(1)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_subnode_fail)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Send account(1) some ether for gas.
|
||||
sendEther(account(1), 1000 * ether);
|
||||
BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
|
||||
|
||||
// account(1) tries to set ownership of the "eth" sub-node.
|
||||
m_sender = account(1);
|
||||
BOOST_REQUIRE(callContractFunction("setSubnodeOwner(bytes32,bytes32,address)", 0x00, keccak256(string("eth")), ACCOUNT(1)) == encodeArgs());
|
||||
|
||||
// Verify that the sub-node owner remains at default zero address.
|
||||
u256 namehash = keccak256(h256(0x00).asBytes() + keccak256("eth").asBytes());
|
||||
BOOST_CHECK(callContractFunction("owner(bytes32)", namehash) == encodeArgs(0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fallback)
|
||||
{
|
||||
deployEns();
|
||||
|
||||
// Call fallback - should just abort via jump to invalid location.
|
||||
BOOST_CHECK(callFallback() == encodeArgs());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // end namespaces
|
@ -1,652 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Ben Edgington <ben@benjaminion.xyz>
|
||||
* @date 2017
|
||||
* Tests for an ERC20 token implementation written in LLL
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <test/liblll/ExecutionFramework.h>
|
||||
#include <liblll/Compiler.h>
|
||||
|
||||
#define TOKENSUPPLY 100000
|
||||
#define TOKENDECIMALS 2
|
||||
#define TOKENSYMBOL "BEN"
|
||||
#define TOKENNAME "Ben Token"
|
||||
#define ACCOUNT(n) h256(account(n), h256::AlignRight)
|
||||
#define SUCCESS encodeArgs(1)
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::lll;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::test;
|
||||
|
||||
namespace solidity::lll::test
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static char const* erc20Code = R"DELIMITER(
|
||||
(seq
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; CONSTANTS
|
||||
|
||||
;; Token parameters.
|
||||
;; 0x40 is a "magic number" - the text of the string is placed here
|
||||
;; when returning the string to the caller. See return-string below.
|
||||
(def 'token-name-string (lit 0x40 "Ben Token"))
|
||||
(def 'token-symbol-string (lit 0x40 "BEN"))
|
||||
(def 'token-decimals 2)
|
||||
(def 'token-supply 100000) ; 1000.00 total tokens
|
||||
|
||||
;; Booleans
|
||||
(def 'false 0)
|
||||
(def 'true 1)
|
||||
|
||||
;; Memory layout.
|
||||
(def 'mem-ret 0x00) ; Fixed due to compiler macro for return.
|
||||
(def 'mem-func 0x00) ; No conflict with mem-ret, so re-use.
|
||||
(def 'mem-keccak 0x00) ; No conflict with mem-func or mem-ret, so re-use.
|
||||
(def 'scratch0 0x20)
|
||||
(def 'scratch1 0x40)
|
||||
|
||||
;; Precomputed function IDs.
|
||||
(def 'get-name 0x06fdde03) ; name()
|
||||
(def 'get-symbol 0x95d89b41) ; symbol()
|
||||
(def 'get-decimals 0x313ce567) ; decimals()
|
||||
(def 'get-total-supply 0x18160ddd) ; totalSupply()
|
||||
(def 'get-balance-of 0x70a08231) ; balanceOf(address)
|
||||
(def 'transfer 0xa9059cbb) ; transfer(address,uint256)
|
||||
(def 'transfer-from 0x23b872dd) ; transferFrom(address,address,uint256)
|
||||
(def 'approve 0x095ea7b3) ; approve(address,uint256)
|
||||
(def 'get-allowance 0xdd62ed3e) ; allowance(address,address)
|
||||
|
||||
;; Event IDs
|
||||
(def 'transfer-event-id ; Transfer(address,address,uint256)
|
||||
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef)
|
||||
|
||||
(def 'approval-event-id ; Approval(address,address,uint256)
|
||||
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925)
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; UTILITIES
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; The following define the key data-structures:
|
||||
;; - balance(addr) => value
|
||||
;; - allowance(addr,addr) => value
|
||||
|
||||
;; Balances are stored at s[owner_addr].
|
||||
(def 'balance (address) address)
|
||||
|
||||
;; Allowances are stored at s[owner_addr + keccak256(spender_addr)]
|
||||
;; We use a crypto function here to avoid any situation where
|
||||
;; approve(me, spender) can be abused to do approve(target, me).
|
||||
(def 'allowance (owner spender)
|
||||
(seq
|
||||
(mstore mem-keccak spender)
|
||||
(keccak256 mem-keccak 0x20)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; For convenience we have macros to refer to function arguments
|
||||
|
||||
(def 'arg1 (calldataload 0x04))
|
||||
(def 'arg2 (calldataload 0x24))
|
||||
(def 'arg3 (calldataload 0x44))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Revert is a soft return that does not consume the remaining gas.
|
||||
;; We use it when rejecting invalid user input.
|
||||
;;
|
||||
;; Note: The REVERT opcode will be implemented in Metropolis (EIP 140).
|
||||
;; Meanwhile it just causes an invalid instruction exception (similar
|
||||
;; to a "throw" in Solidity). When fully implemented, Revert could be
|
||||
;; use to return error codes, or even messages.
|
||||
|
||||
(def 'revert () (revert 0 0))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Macro for returning string names.
|
||||
;; Compliant with the ABI format for strings.
|
||||
|
||||
(def 'return-string (string-literal)
|
||||
(seq
|
||||
(mstore 0x00 0x20) ; Points to our string's memory location
|
||||
(mstore 0x20 string-literal) ; Length. String itself is copied to 0x40.
|
||||
(return 0x00 (& (+ (mload 0x20) 0x5f) (~ 0x1f)))))
|
||||
; Round return up to 32 byte boundary
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Convenience macro for raising Events
|
||||
|
||||
(def 'event3 (id addr1 addr2 value)
|
||||
(seq
|
||||
(mstore scratch0 value)
|
||||
(log3 scratch0 0x20 id addr1 addr2)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Determines whether the stored function ID matches a known
|
||||
;; function hash and executes <code-body> if so.
|
||||
;; @param function-hash The four-byte hash of a known function signature.
|
||||
;; @param code-body The code to run in the case of a match.
|
||||
|
||||
(def 'function (function-hash code-body)
|
||||
(when (= (mload mem-func) function-hash)
|
||||
code-body))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Gets the function ID and stores it in memory for reference.
|
||||
;; The function ID is in the leftmost four bytes of the call data.
|
||||
|
||||
(def 'uses-functions
|
||||
(mstore
|
||||
mem-func
|
||||
(shr (calldataload 0x00) 224)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; GUARDS
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Checks that ensure that each function is called with the right
|
||||
;; number of arguments. For one thing this addresses the "ERC20
|
||||
;; short address attack". For another, it stops me making
|
||||
;; mistakes while testing. We use these only on the non-constant functions.
|
||||
|
||||
(def 'has-one-arg (unless (= 0x24 (calldatasize)) (revert)))
|
||||
(def 'has-two-args (unless (= 0x44 (calldatasize)) (revert)))
|
||||
(def 'has-three-args (unless (= 0x64 (calldatasize)) (revert)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Check that addresses have only 160 bits and revert if not.
|
||||
;; We use these input type-checks on the non-constant functions.
|
||||
|
||||
(def 'is-address (addr)
|
||||
(when
|
||||
(shr addr 160)
|
||||
(revert)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Check that transfer values are smaller than total supply and
|
||||
;; revert if not. This should effectively exclude negative values.
|
||||
|
||||
(def 'is-value (value)
|
||||
(when (> value token-supply) (revert)))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Will revert if sent any Ether. We use the macro immediately so as
|
||||
;; to abort if sent any Ether during contract deployment.
|
||||
|
||||
(def 'not-payable
|
||||
(when (callvalue) (revert)))
|
||||
|
||||
not-payable
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; INITIALISATION
|
||||
;;
|
||||
;; Assign all tokens initially to the owner of the contract.
|
||||
|
||||
(sstore (balance (caller)) token-supply)
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; CONTRACT CODE
|
||||
|
||||
(returnlll
|
||||
(seq not-payable uses-functions
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Getter for the name of the token.
|
||||
;; @abi name() constant returns (string)
|
||||
;; @return The token name as a string.
|
||||
|
||||
(function get-name
|
||||
(return-string token-name-string))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Getter for the symbol of the token.
|
||||
;; @abi symbol() constant returns (string)
|
||||
;; @return The token symbol as a string.
|
||||
|
||||
(function get-symbol
|
||||
(return-string token-symbol-string))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Getter for the number of decimals assigned to the token.
|
||||
;; @abi decimals() constant returns (uint256)
|
||||
;; @return The token decimals.
|
||||
|
||||
(function get-decimals
|
||||
(return token-decimals))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Getter for the total token supply.
|
||||
;; @abi totalSupply() constant returns (uint256)
|
||||
;; @return The token supply.
|
||||
|
||||
(function get-total-supply
|
||||
(return token-supply))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Returns the account balance of another account.
|
||||
;; @abi balanceOf(address) constant returns (uint256)
|
||||
;; @param owner The address of the account's owner.
|
||||
;; @return The account balance.
|
||||
|
||||
(function get-balance-of
|
||||
(seq
|
||||
|
||||
(def 'owner arg1)
|
||||
|
||||
(return (sload (balance owner)))))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Transfers _value amount of tokens to address _to. The command
|
||||
;; should throw if the _from account balance has not enough
|
||||
;; tokens to spend.
|
||||
;; @abi transfer(address, uint256) returns (bool)
|
||||
;; @param to The account to receive the tokens.
|
||||
;; @param value The quantity of tokens to transfer.
|
||||
;; @return Success (true). Other outcomes result in a Revert.
|
||||
|
||||
(function transfer
|
||||
(seq has-two-args (is-address arg1) (is-value arg2)
|
||||
|
||||
(def 'to arg1)
|
||||
(def 'value arg2)
|
||||
|
||||
(when value ; value == 0 is a no-op
|
||||
(seq
|
||||
|
||||
;; The caller's balance. Save in memory for efficiency.
|
||||
(mstore scratch0 (sload (balance (caller))))
|
||||
|
||||
;; Revert if the caller's balance is not sufficient.
|
||||
(when (> value (mload scratch0))
|
||||
(revert))
|
||||
|
||||
;; Make the transfer
|
||||
;; It would be good to check invariants (sum of balances).
|
||||
(sstore (balance (caller)) (- (mload scratch0) value))
|
||||
(sstore (balance to) (+ (sload (balance to)) value))
|
||||
|
||||
;; Event - Transfer(address,address,uint256)
|
||||
(event3 transfer-event-id (caller) to value)))
|
||||
|
||||
(return true)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Send _value amount of tokens from address _from to address _to
|
||||
;; @abi transferFrom(address,address,uint256) returns (bool)
|
||||
;; @param from The account to send the tokens from.
|
||||
;; @param to The account to receive the tokens.
|
||||
;; @param value The quantity of tokens to transfer.
|
||||
;; @return Success (true). Other outcomes result in a Revert.
|
||||
|
||||
(function transfer-from
|
||||
(seq has-three-args (is-address arg1) (is-address arg2) (is-value arg3)
|
||||
|
||||
(def 'from arg1)
|
||||
(def 'to arg2)
|
||||
(def 'value arg3)
|
||||
|
||||
(when value ; value == 0 is a no-op
|
||||
|
||||
(seq
|
||||
|
||||
;; Save data to memory for efficiency.
|
||||
(mstore scratch0 (sload (balance from)))
|
||||
(mstore scratch1 (sload (allowance from (caller))))
|
||||
|
||||
;; Revert if not enough funds, or not enough approved.
|
||||
(when
|
||||
(||
|
||||
(> value (mload scratch0))
|
||||
(> value (mload scratch1)))
|
||||
(revert))
|
||||
|
||||
;; Make the transfer and update allowance.
|
||||
(sstore (balance from) (- (mload scratch0) value))
|
||||
(sstore (balance to) (+ (sload (balance to)) value))
|
||||
(sstore (allowance from (caller)) (- (mload scratch1) value))
|
||||
|
||||
;; Event - Transfer(address,address,uint256)
|
||||
(event3 transfer-event-id from to value)))
|
||||
|
||||
(return true)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Allows _spender to withdraw from your account multiple times,
|
||||
;; up to the _value amount. If this function is called again it
|
||||
;; overwrites the current allowance with _value.
|
||||
;; @abi approve(address,uint256) returns (bool)
|
||||
;; @param spender The withdrawing account having its limit set.
|
||||
;; @param value The maximum allowed amount.
|
||||
;; @return Success (true). Other outcomes result in a Revert.
|
||||
|
||||
(function approve
|
||||
(seq has-two-args (is-address arg1) (is-value arg2)
|
||||
|
||||
(def 'spender arg1)
|
||||
(def 'value arg2)
|
||||
|
||||
;; Force users set the allowance to 0 before setting it to
|
||||
;; another value for the same spender. Prevents this attack:
|
||||
;; https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM
|
||||
(when
|
||||
(&& value (sload (allowance (caller) spender)))
|
||||
(revert))
|
||||
|
||||
(sstore (allowance (caller) spender) value)
|
||||
|
||||
;; Event - Approval(address,address,uint256)
|
||||
(event3 approval-event-id (caller) spender value)
|
||||
|
||||
(return true)))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Returns the amount which _spender is still allowed to withdraw
|
||||
;; from _owner.
|
||||
;; @abi allowance(address,address) constant returns (uint256)
|
||||
;; @param owner The owning account.
|
||||
;; @param spender The withdrawing account.
|
||||
;; @return The allowed amount remaining.
|
||||
|
||||
(function get-allowance
|
||||
(seq
|
||||
|
||||
(def 'owner arg1)
|
||||
(def 'spender arg2)
|
||||
|
||||
(return (sload (allowance owner spender)))))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; Fallback: No functions matched the function ID provided.
|
||||
|
||||
(revert)))
|
||||
)
|
||||
)DELIMITER";
|
||||
|
||||
static unique_ptr<bytes> s_compiledErc20;
|
||||
|
||||
class LLLERC20TestFramework: public LLLExecutionFramework
|
||||
{
|
||||
protected:
|
||||
void deployErc20()
|
||||
{
|
||||
if (!s_compiledErc20)
|
||||
{
|
||||
vector<string> errors;
|
||||
s_compiledErc20.reset(new bytes(compileLLL(erc20Code, solidity::test::Options::get().evmVersion(), solidity::test::Options::get().optimize, &errors)));
|
||||
BOOST_REQUIRE(errors.empty());
|
||||
}
|
||||
sendMessage(*s_compiledErc20, true);
|
||||
BOOST_REQUIRE(m_transactionSuccessful);
|
||||
BOOST_REQUIRE(!m_output.empty());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Test suite for an ERC20 contract written in LLL.
|
||||
BOOST_FIXTURE_TEST_SUITE(LLLERC20, LLLERC20TestFramework)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(creation)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// All tokens are initially assigned to the contract creator.
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(constants)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
BOOST_CHECK(callContractFunction("totalSupply()") == encodeArgs(TOKENSUPPLY));
|
||||
BOOST_CHECK(callContractFunction("decimals()") == encodeArgs(TOKENDECIMALS));
|
||||
BOOST_CHECK(callContractFunction("symbol()") == encodeDyn(string(TOKENSYMBOL)));
|
||||
BOOST_CHECK(callContractFunction("name()") == encodeDyn(string(TOKENNAME)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(send_value)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Send value to the contract. Should always fail.
|
||||
m_sender = account(0);
|
||||
auto contractBalance = balanceAt(m_contractAddress);
|
||||
|
||||
// Fallback: check value is not transferred.
|
||||
BOOST_CHECK(callFallbackWithValue(42) != SUCCESS);
|
||||
BOOST_CHECK(balanceAt(m_contractAddress) == contractBalance);
|
||||
|
||||
// Transfer: check nothing happened.
|
||||
BOOST_CHECK(callContractFunctionWithValue("transfer(address,uint256)", ACCOUNT(1), 100, 42) != SUCCESS);
|
||||
BOOST_CHECK(balanceAt(m_contractAddress) == contractBalance);
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(0));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(transfer)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Transfer 100 tokens from account(0) to account(1).
|
||||
int transfer = 100;
|
||||
m_sender = account(0);
|
||||
BOOST_CHECK(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(transfer_from)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Approve account(1) to transfer up to 1000 tokens from account(0).
|
||||
int allow = 1000;
|
||||
m_sender = account(0);
|
||||
BOOST_REQUIRE(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow)) == SUCCESS);
|
||||
BOOST_REQUIRE(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow));
|
||||
|
||||
// Send account(1) some ether for gas.
|
||||
sendEther(account(1), 1000 * ether);
|
||||
BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
|
||||
|
||||
// Transfer 300 tokens from account(0) to account(2); check that the allowance decreases.
|
||||
int transfer = 300;
|
||||
m_sender = account(1);
|
||||
BOOST_REQUIRE(callContractFunction("transferFrom(address,address,uint256)", ACCOUNT(0), ACCOUNT(2), u256(transfer)) == SUCCESS);
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(2)) == encodeArgs(transfer));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
|
||||
BOOST_CHECK(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow - transfer));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(transfer_event)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Transfer 1000 tokens from account(0) to account(1).
|
||||
int transfer = 1000;
|
||||
m_sender = account(0);
|
||||
BOOST_REQUIRE(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
|
||||
|
||||
// Check that a Transfer event was recorded and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(transfer));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 3);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("Transfer(address,address,uint256)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == ACCOUNT(0));
|
||||
BOOST_CHECK(logTopic(0, 2) == ACCOUNT(1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(transfer_zero_no_event)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Transfer 0 tokens from account(0) to account(1). This is a no-op.
|
||||
int transfer = 0;
|
||||
m_sender = account(0);
|
||||
BOOST_REQUIRE(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
|
||||
|
||||
// Check that no Event was recorded.
|
||||
BOOST_CHECK(numLogs() == 0);
|
||||
|
||||
// Check that balances have not changed.
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(approval_and_transfer_events)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Approve account(1) to transfer up to 10000 tokens from account(0).
|
||||
int allow = 10000;
|
||||
m_sender = account(0);
|
||||
BOOST_REQUIRE(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow)) == SUCCESS);
|
||||
|
||||
// Check that an Approval event was recorded and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(allow));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 3);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("Approval(address,address,uint256)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == ACCOUNT(0));
|
||||
BOOST_CHECK(logTopic(0, 2) == ACCOUNT(1));
|
||||
|
||||
// Send account(1) some ether for gas.
|
||||
sendEther(account(1), 1000 * ether);
|
||||
BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
|
||||
|
||||
// Transfer 3000 tokens from account(0) to account(2); check that the allowance decreases.
|
||||
int transfer = 3000;
|
||||
m_sender = account(1);
|
||||
BOOST_REQUIRE(callContractFunction("transferFrom(address,address,uint256)", ACCOUNT(0), ACCOUNT(2), u256(transfer)) == SUCCESS);
|
||||
|
||||
// Check that a Transfer event was recorded and contents are correct.
|
||||
BOOST_REQUIRE(numLogs() == 1);
|
||||
BOOST_CHECK(logData(0) == encodeArgs(transfer));
|
||||
BOOST_REQUIRE(numLogTopics(0) == 3);
|
||||
BOOST_CHECK(logTopic(0, 0) == keccak256(string("Transfer(address,address,uint256)")));
|
||||
BOOST_CHECK(logTopic(0, 1) == ACCOUNT(0));
|
||||
BOOST_CHECK(logTopic(0, 2) == ACCOUNT(2));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_transfer_1)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Transfer more than the total supply; ensure nothing changes.
|
||||
int transfer = TOKENSUPPLY + 1;
|
||||
m_sender = account(0);
|
||||
BOOST_CHECK(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) != SUCCESS);
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_transfer_2)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// Separate transfers that together exceed initial balance.
|
||||
int transfer = 1 + TOKENSUPPLY / 2;
|
||||
m_sender = account(0);
|
||||
|
||||
// First transfer should succeed.
|
||||
BOOST_REQUIRE(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
|
||||
BOOST_REQUIRE(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
|
||||
BOOST_REQUIRE(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
|
||||
|
||||
// Second transfer should fail.
|
||||
BOOST_CHECK(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) != SUCCESS);
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_transfer_from)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
// TransferFrom without approval.
|
||||
int transfer = 300;
|
||||
|
||||
// Send account(1) some ether for gas.
|
||||
m_sender = account(0);
|
||||
sendEther(account(1), 1000 * ether);
|
||||
BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
|
||||
|
||||
// Try the transfer; ensure nothing changes.
|
||||
m_sender = account(1);
|
||||
BOOST_CHECK(callContractFunction("transferFrom(address,address,uint256)", ACCOUNT(0), ACCOUNT(2), u256(transfer)) != SUCCESS);
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(2)) == encodeArgs(0));
|
||||
BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
|
||||
BOOST_CHECK(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_reapprove)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
m_sender = account(0);
|
||||
|
||||
// Approve account(1) to transfer up to 1000 tokens from account(0).
|
||||
int allow1 = 1000;
|
||||
BOOST_REQUIRE(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow1)) == SUCCESS);
|
||||
BOOST_REQUIRE(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow1));
|
||||
|
||||
// Now approve account(1) to transfer up to 500 tokens from account(0).
|
||||
// Should fail (we need to reset allowance to 0 first).
|
||||
int allow2 = 500;
|
||||
BOOST_CHECK(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow2)) != SUCCESS);
|
||||
BOOST_CHECK(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bad_data)
|
||||
{
|
||||
deployErc20();
|
||||
|
||||
m_sender = account(0);
|
||||
|
||||
// Correct data: transfer(address _to, 1).
|
||||
sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a123456789a") + encodeArgs(1), false, 0);
|
||||
BOOST_CHECK(m_transactionSuccessful);
|
||||
BOOST_CHECK(m_output == SUCCESS);
|
||||
|
||||
// Too little data (address is truncated by one byte).
|
||||
sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a12345678") + encodeArgs(1), false, 0);
|
||||
BOOST_CHECK(!m_transactionSuccessful);
|
||||
BOOST_CHECK(m_output != SUCCESS);
|
||||
|
||||
// Too much data (address is extended with a zero byte).
|
||||
sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a123456789a00") + encodeArgs(1), false, 0);
|
||||
BOOST_CHECK(!m_transactionSuccessful);
|
||||
BOOST_CHECK(m_output != SUCCESS);
|
||||
|
||||
// Invalid address (a bit above the 160th is set).
|
||||
sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000100123456789a123456789a123456789a123456789a") + encodeArgs(1), false, 0);
|
||||
BOOST_CHECK(!m_transactionSuccessful);
|
||||
BOOST_CHECK(m_output != SUCCESS);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // end namespaces
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Alex Beregszaszi
|
||||
* @date 2016
|
||||
* Unit tests for the LLL parser.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <liblll/Compiler.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace solidity::lll::test
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool successParse(std::string const& _source)
|
||||
{
|
||||
std::string ret = lll::parseLLL(_source);
|
||||
return ret.size() != 0;
|
||||
}
|
||||
|
||||
std::string parse(std::string const& _source)
|
||||
{
|
||||
return lll::parseLLL(_source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(LLLParser)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(smoke_test)
|
||||
{
|
||||
char const* text = "1";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(string)
|
||||
{
|
||||
char const* text = "\"string\"";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"("string")");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(symbol)
|
||||
{
|
||||
char const* text = "symbol";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(symbol)");
|
||||
|
||||
BOOST_CHECK(successParse("'symbol"));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(symbol)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(decimals)
|
||||
{
|
||||
char const* text = "1234";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(1234)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(hexadecimals)
|
||||
{
|
||||
char const* text = "0x1234";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(4660)");
|
||||
|
||||
BOOST_CHECK(!successParse("0x"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sequence)
|
||||
{
|
||||
char const* text = "{ 1234 }";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"({ 1234 })");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(empty_sequence)
|
||||
{
|
||||
char const* text = "{}";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"({ })");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(mload)
|
||||
{
|
||||
char const* text = "@0";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(@ 0)");
|
||||
|
||||
BOOST_CHECK(successParse("@0x0"));
|
||||
BOOST_CHECK(successParse("@symbol"));
|
||||
BOOST_CHECK(!successParse("@"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sload)
|
||||
{
|
||||
char const* text = "@@0";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(@@ 0)");
|
||||
|
||||
BOOST_CHECK(successParse("@@0x0"));
|
||||
BOOST_CHECK(successParse("@@symbol"));
|
||||
BOOST_CHECK(!successParse("@@"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(mstore)
|
||||
{
|
||||
char const* text = "[0]:0";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"([ 0 ] 0)");
|
||||
|
||||
BOOST_CHECK(successParse("[0] 0"));
|
||||
BOOST_CHECK(successParse("[0x0]:0x0"));
|
||||
BOOST_CHECK(successParse("[symbol]:symbol"));
|
||||
BOOST_CHECK(!successParse("[]"));
|
||||
BOOST_CHECK(!successParse("[0]"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sstore)
|
||||
{
|
||||
char const* text = "[[0]]:0";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"([[ 0 ]] 0)");
|
||||
|
||||
BOOST_CHECK(successParse("[[0]] 0"));
|
||||
BOOST_CHECK(successParse("[[0x0]]:0x0"));
|
||||
BOOST_CHECK(successParse("[[symbol]]:symbol"));
|
||||
BOOST_CHECK(!successParse("[[]]"));
|
||||
BOOST_CHECK(!successParse("[[0x0]]"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldataload)
|
||||
{
|
||||
char const* text = "$0";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"($ 0)");
|
||||
|
||||
BOOST_CHECK(successParse("$0x0"));
|
||||
BOOST_CHECK(successParse("$symbol"));
|
||||
BOOST_CHECK(!successParse("$"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(list)
|
||||
{
|
||||
char const* text = "( 1234 )";
|
||||
BOOST_CHECK(successParse(text));
|
||||
BOOST_CHECK_EQUAL(parse(text), R"(( 1234 ))");
|
||||
|
||||
BOOST_CHECK(successParse("( 1234 5467 )"));
|
||||
BOOST_CHECK(successParse("()"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(macro_with_zero_args)
|
||||
{
|
||||
char const* text = "(def 'zeroargs () (asm INVALID))";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // end namespaces
|
Loading…
Reference in New Issue
Block a user