Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
8cbdfb4e77
  1. 4
      CMakeLists.txt
  2. 1
      alethzero/CMakeLists.txt
  3. BIN
      alethzero/alethzero.ico
  4. 1
      alethzero/alethzero.rc
  5. 28
      cmake/EthExecutableHelper.cmake
  6. 2
      libdevcrypto/Common.h
  7. 7
      libethereum/Transaction.cpp
  8. 9
      libevm/VM.h
  9. 21
      libevmcore/Assembly.cpp
  10. 3
      libserpent/bignum.h
  11. 279
      libserpent/compiler.cpp
  12. 203
      libserpent/functions.cpp
  13. 39
      libserpent/functions.h
  14. 154
      libserpent/opcodes.cpp
  15. 123
      libserpent/opcodes.h
  16. 98
      libserpent/optimize.cpp
  17. 19
      libserpent/optimize.h
  18. 86
      libserpent/parser.cpp
  19. 327
      libserpent/preprocess.cpp
  20. 50
      libserpent/preprocess.h
  21. 1206
      libserpent/rewriter.cpp
  22. 211
      libserpent/rewriteutils.cpp
  23. 76
      libserpent/rewriteutils.h
  24. 4
      libserpent/tokenize.cpp
  25. 57
      libserpent/util.cpp
  26. 36
      libserpent/util.h
  27. 20
      libsolidity/CompilerStack.cpp
  28. 9
      libsolidity/CompilerStack.h
  29. 13
      sc/cmdline.cpp
  30. 0
      test/SolidityABIJSON.cpp
  31. 0
      test/SolidityCompiler.cpp
  32. 2
      test/SolidityEndToEndTest.cpp
  33. 0
      test/SolidityExpressionCompiler.cpp
  34. 0
      test/SolidityNameAndTypeResolution.cpp
  35. 0
      test/SolidityNatspecJSON.cpp
  36. 2
      test/SolidityOptimizer.cpp
  37. 0
      test/SolidityParser.cpp
  38. 0
      test/SolidityScanner.cpp
  39. 145
      test/stRefundTestFiller.json
  40. 10
      test/state.cpp

4
CMakeLists.txt

@ -165,6 +165,8 @@ if (WIN32)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION "0.7")
set(CPACK_GENERATOR "NSIS")
# seems to be not working
# set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.bmp")
# our stuff
set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications")
@ -178,6 +180,8 @@ if (WIN32)
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum")
set(CPACK_NSIS_CONTACT "ethereum.org")
set(CPACK_NSIS_MODIFY_PATH ON)
set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.ico")
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.ico")
include(CPack)
endif (WIN32)

1
alethzero/CMakeLists.txt

@ -27,6 +27,7 @@ endif ()
eth_add_executable(${EXECUTABLE}
ICON alethzero
UI_RESOURCES alethzero.icns Main.ui
WIN_RESOURCES alethzero.rc
)
add_dependencies(${EXECUTABLE} BuildInfo.h)

BIN
alethzero/alethzero.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

1
alethzero/alethzero.rc

@ -0,0 +1 @@
APP_ICON ICON DISCARDABLE "alethzero.ico"

28
cmake/EthExecutableHelper.cmake

@ -18,7 +18,7 @@ macro(eth_add_executable EXECUTABLE)
set (extra_macro_args ${ARGN})
set (options)
set (one_value_args ICON)
set (multi_value_args UI_RESOURCES)
set (multi_value_args UI_RESOURCES WIN_RESOURCES)
cmake_parse_arguments (ETH_ADD_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (APPLE)
@ -38,7 +38,7 @@ macro(eth_add_executable EXECUTABLE)
set_source_files_properties(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
else ()
add_executable(${EXECUTABLE} ${ETH_ADD_EXECUTABLE_UI_RESOURCES} ${SRC_LIST} ${HEADERS})
add_executable(${EXECUTABLE} ${ETH_ADD_EXECUTABLE_UI_RESOURCES} ${ETH_ADD_EXECUTABLE_WIN_RESOURCES} ${SRC_LIST} ${HEADERS})
endif()
endmacro()
@ -60,7 +60,11 @@ macro(eth_install_executable EXECUTABLE)
cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (ETH_INSTALL_EXECUTABLE_QMLDIR)
set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}")
if (APPLE)
set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}")
elseif (WIN32)
set(eth_qml_dir --qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR})
endif()
message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}")
endif()
@ -68,7 +72,8 @@ macro(eth_install_executable EXECUTABLE)
# First have qt5 install plugins and frameworks
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app ${eth_qml_dir}
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
# This tool and next will inspect linked libraries in order to determine which dependencies are required
if (${CMAKE_CFG_INTDIR} STREQUAL ".")
@ -108,6 +113,13 @@ macro(eth_install_executable EXECUTABLE)
$<TARGET_FILE_DIR:${EXECUTABLE}>/platforms
)
# ugly way, improve that
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory
"${ETH_DEPENDENCY_INSTALL_DIR}/qml"
$<TARGET_FILE_DIR:${EXECUTABLE}>
)
install( FILES ${DLLS}
DESTINATION bin
COMPONENT ${EXECUTABLE}
@ -118,6 +130,14 @@ macro(eth_install_executable EXECUTABLE)
COMPONENT ${EXECUTABLE}
)
file (GLOB QMLS ${ETH_DEPENDENCY_INSTALL_DIR}/qml/*)
foreach(QML ${QMLS})
install( DIRECTORY ${QML}
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
endforeach()
install( TARGETS ${EXECUTABLE} RUNTIME
DESTINATION bin
COMPONENT ${EXECUTABLE}

2
libdevcrypto/Common.h

@ -47,7 +47,7 @@ struct SignatureStruct
{
SignatureStruct() {}
SignatureStruct(Signature const& _s) { *(h520*)this = _s; }
SignatureStruct(h256 _r, h256 _s, byte _v): r(_r), s(_s), v(_v) {}
SignatureStruct(h256 const& _r, h256 const& _s, byte _v): r(_r), s(_s), v(_v) {}
operator Signature() const { return *(h520 const*)this; }
/// @returns true if r,s,v values are valid, otherwise false

7
libethereum/Transaction.cpp

@ -43,7 +43,10 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
m_receiveAddress = rlp[field = 3].toHash<Address>();
m_value = rlp[field = 4].toInt<u256>();
m_data = rlp[field = 5].toBytes();
m_vrs = SignatureStruct{ rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>(), byte(rlp[field = 6].toInt<byte>() - 27) };
byte v = rlp[field = 6].toInt<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].toInt<u256>();
m_vrs = SignatureStruct{ r, s, v };
if (_checkSender)
m_sender = sender();
}
@ -71,7 +74,7 @@ Address Transaction::sender() const
{
if (!m_sender)
{
auto p = recover(*(Signature const*)&m_vrs, sha3(WithoutSignature));
auto p = recover(m_vrs, sha3(WithoutSignature));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));

9
libevm/VM.h

@ -217,6 +217,8 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st
require(7);
runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1];
newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]));
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
break;
case Instruction::CREATE:
@ -226,6 +228,8 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st
u256 inSize = m_stack[m_stack.size() - 3];
newTempSize = (bigint)inOff + inSize;
runGas = c_createGas;
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
break;
}
case Instruction::EXP:
@ -566,6 +570,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st
break;
default:
// this is unreachable, but if someone introduces a bug in the future, he may get here.
assert(false);
BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested."));
break;
}
@ -795,8 +800,6 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st
if (_ext.balance(_ext.myAddress) >= endowment)
{
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
_ext.subBalance(endowment);
m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
}
@ -825,8 +828,6 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st
if (_ext.balance(_ext.myAddress) >= value)
{
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
_ext.subBalance(value);
m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress));
}

21
libevmcore/Assembly.cpp

@ -478,7 +478,6 @@ bytes Assembly::assemble() const
bytes ret;
unsigned totalBytes = bytesRequired();
ret.reserve(totalBytes);
vector<unsigned> tagPos(m_usedTags);
map<unsigned, unsigned> tagRef;
multimap<h256, unsigned> dataRef;
@ -489,6 +488,12 @@ bytes Assembly::assemble() const
for (auto const& i: m_subs)
m_data[i.first] = i.second.assemble();
unsigned bytesRequiredIncludingData = bytesRequired();
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);
byte dataRefPush = (byte)Instruction::PUSH1 - 1 + bytesPerDataRef;
ret.reserve(bytesRequiredIncludingData);
// m_data must not change from here on
for (AssemblyItem const& i: m_items)
switch (i.m_type)
{
@ -526,9 +531,9 @@ bytes Assembly::assemble() const
}
case PushData: case PushSub:
{
ret.push_back(tagPush);
ret.push_back(dataRefPush);
dataRef.insert(make_pair((h256)i.m_data, ret.size()));
ret.resize(ret.size() + bytesPerTag);
ret.resize(ret.size() + bytesPerDataRef);
break;
}
case PushSubSize:
@ -542,10 +547,12 @@ bytes Assembly::assemble() const
break;
}
case PushProgramSize:
ret.push_back(tagPush);
{
ret.push_back(dataRefPush);
sizeRef.push_back(ret.size());
ret.resize(ret.size() + bytesPerTag);
ret.resize(ret.size() + bytesPerDataRef);
break;
}
case Tag:
tagPos[(unsigned)i.m_data] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST);
@ -573,7 +580,7 @@ bytes Assembly::assemble() const
{
for (auto it = its.first; it != its.second; ++it)
{
bytesRef r(ret.data() + it->second, bytesPerTag);
bytesRef r(ret.data() + it->second, bytesPerDataRef);
toBigEndian(ret.size(), r);
}
for (auto b: i.second)
@ -583,7 +590,7 @@ bytes Assembly::assemble() const
}
for (unsigned pos: sizeRef)
{
bytesRef r(ret.data() + pos, bytesPerTag);
bytesRef r(ret.data() + pos, bytesPerDataRef);
toBigEndian(ret.size(), r);
}
return ret;

3
libserpent/bignum.h

@ -35,4 +35,7 @@ bool decimalGt(std::string a, std::string b, bool eqAllowed=false);
unsigned decimalToUnsigned(std::string a);
#define utd unsignedToDecimal
#define dtu decimalToUnsigned
#endif

279
libserpent/compiler.cpp

@ -6,6 +6,7 @@
#include "bignum.h"
#include "opcodes.h"
// Auxiliary data that is gathered while compiling
struct programAux {
std::map<std::string, std::string> vars;
int nextVarMem;
@ -13,15 +14,19 @@ struct programAux {
bool calldataUsed;
int step;
int labelLength;
int functionCount;
};
// Auxiliary data that gets passed down vertically
// but not back up
struct programVerticalAux {
int height;
std::string innerScopeName;
std::map<std::string, int> dupvars;
std::map<std::string, int> funvars;
std::vector<mss> scopes;
};
// Compilation result
struct programData {
programAux aux;
Node code;
@ -34,7 +39,6 @@ programAux Aux() {
o.calldataUsed = false;
o.step = 0;
o.nextVarMem = 32;
o.functionCount = 0;
return o;
}
@ -43,6 +47,7 @@ programVerticalAux verticalAux() {
o.height = 0;
o.dupvars = std::map<std::string, int>();
o.funvars = std::map<std::string, int>();
o.scopes = std::vector<mss>();
return o;
}
@ -72,29 +77,58 @@ Node popwrap(Node node) {
return multiToken(nodelist, 2, node.metadata);
}
// Grabs variables
mss getVariables(Node node, mss cur=mss()) {
Metadata m = node.metadata;
// Tokens don't contain any variables
if (node.type == TOKEN)
return cur;
// Don't descend into call fragments
else if (node.val == "lll")
return getVariables(node.args[1], cur);
// At global scope get/set/ref also declare
else if (node.val == "get" || node.val == "set" || node.val == "ref") {
if (node.args[0].type != TOKEN)
err("Variable name must be simple token,"
" not complex expression! " + printSimple(node.args[0]), m);
if (!cur.count(node.args[0].val)) {
cur[node.args[0].val] = utd(cur.size() * 32 + 32);
//std::cerr << node.args[0].val << " " << cur[node.args[0].val] << "\n";
}
}
// Recursively process children
for (unsigned i = 0; i < node.args.size(); i++) {
cur = getVariables(node.args[i], cur);
}
return cur;
}
// Turns LLL tree into tree of code fragments
programData opcodeify(Node node,
programAux aux=Aux(),
programVerticalAux vaux=verticalAux()) {
std::string symb = "_"+mkUniqueToken();
Metadata m = node.metadata;
// Get variables
if (!aux.vars.size()) {
aux.vars = getVariables(node);
aux.nextVarMem = aux.vars.size() * 32 + 32;
}
// Numbers
if (node.type == TOKEN) {
return pd(aux, nodeToNumeric(node), 1);
}
else if (node.val == "ref" || node.val == "get" ||
node.val == "set" || node.val == "declare") {
else if (node.val == "ref" || node.val == "get" || node.val == "set") {
std::string varname = node.args[0].val;
if (!aux.vars.count(varname)) {
aux.vars[varname] = unsignedToDecimal(aux.nextVarMem);
aux.nextVarMem += 32;
}
if (varname == "'msg.data") aux.calldataUsed = true;
// Determine reference to variable
Node varNode = tkn(aux.vars[varname], m);
//std::cerr << varname << " " << printSimple(varNode) << "\n";
// Set variable
if (node.val == "set") {
programData sub = opcodeify(node.args[1], aux, vaux);
if (!sub.outs)
err("Value to set variable must have nonzero arity!", m);
// What if we are setting a stack variable?
if (vaux.dupvars.count(node.args[0].val)) {
int h = vaux.height - vaux.dupvars[node.args[0].val];
if (h > 16) err("Too deep for stack variable (max 16)", m);
@ -105,149 +139,65 @@ programData opcodeify(Node node,
};
return pd(sub.aux, multiToken(nodelist, 3, m), 0);
}
Node nodelist[] = {
sub.code,
token(sub.aux.vars[varname], m),
token("MSTORE", m),
};
return pd(sub.aux, multiToken(nodelist, 3, m), 0);
// Setting a memory variable
else {
Node nodelist[] = {
sub.code,
varNode,
token("MSTORE", m),
};
return pd(sub.aux, multiToken(nodelist, 3, m), 0);
}
}
// Get variable
else if (node.val == "get") {
if (vaux.dupvars.count(node.args[0].val)) {
// Getting a stack variable
if (vaux.dupvars.count(node.args[0].val)) {
int h = vaux.height - vaux.dupvars[node.args[0].val];
if (h > 16) err("Too deep for stack variable (max 16)", m);
return pd(aux, token("DUP"+unsignedToDecimal(h)), 1);
}
Node nodelist[] =
{ token(aux.vars[varname], m), token("MLOAD", m) };
return pd(aux, multiToken(nodelist, 2, m), 1);
// Getting a memory variable
else {
Node nodelist[] =
{ varNode, token("MLOAD", m) };
return pd(aux, multiToken(nodelist, 2, m), 1);
}
}
// Refer variable
else if (node.val == "ref") {
if (vaux.dupvars.count(node.args[0].val))
err("Cannot ref stack variable!", m);
return pd(aux, token(aux.vars[varname], m), 1);
}
// Declare variable
else {
return pd(aux, multiToken(nullptr, 0, m), 0);
return pd(aux, varNode, 1);
}
}
// Define functions (TODO: eventually move to rewriter.cpp, keep
// compiler pure LLL)
if (node.val == "def") {
std::vector<std::string> varNames;
std::vector<int> varSizes;
bool useLt32 = false;
int totalSz = 0;
if (node.args.size() != 2)
err("Malformed def!", m);
// Collect the list of variable names and variable byte counts
for (unsigned i = 0; i < node.args[0].args.size(); i++) {
if (node.args[0].args[i].val == "kv") {
if (node.args[0].args[i].args.size() != 2)
err("Malformed def!", m);
varNames.push_back(node.args[0].args[i].args[0].val);
varSizes.push_back(
decimalToUnsigned(node.args[0].args[i].args[1].val));
if (varSizes.back() > 32)
err("Max argument width: 32 bytes", m);
useLt32 = true;
// Comments do nothing
else if (node.val == "comment") {
Node nodelist[] = { };
return pd(aux, multiToken(nodelist, 0, m), 0);
}
// Custom operation sequence
// eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc
if (node.val == "ops") {
std::vector<Node> subs2;
int depth = 0;
for (unsigned i = 0; i < node.args.size(); i++) {
std::string op = upperCase(node.args[i].val);
if (node.args[i].type == ASTNODE || opinputs(op) == -1) {
programVerticalAux vaux2 = vaux;
vaux2.height = vaux.height - i - 1 + node.args.size();
programData sub = opcodeify(node.args[i], aux, vaux2);
aux = sub.aux;
depth += sub.outs;
subs2.push_back(sub.code);
}
else {
varNames.push_back(node.args[0].args[i].val);
varSizes.push_back(32);
subs2.push_back(token(op, m));
depth += opoutputs(op) - opinputs(op);
}
aux.vars[varNames.back()] = unsignedToDecimal(aux.nextVarMem + 32 * i);
totalSz += varSizes.back();
}
int functionCount = aux.functionCount;
int nextVarMem = aux.nextVarMem;
aux.nextVarMem += 32 * varNames.size();
aux.functionCount += 1;
programData inner;
// If we're only using 32-byte variables, then great, just copy
// over the calldata!
if (!useLt32) {
programData sub = opcodeify(node.args[1], aux, vaux);
Node nodelist[] = {
token(unsignedToDecimal(totalSz), m),
token("1", m),
token(unsignedToDecimal(nextVarMem), m),
token("CALLDATACOPY", m),
sub.code
};
inner = pd(sub.aux, multiToken(nodelist, 5, m), 0);
}
else {
std::vector<Node> innerList;
int cum = 1;
for (unsigned i = 0; i < varNames.size();) {
// If we get a series of 32-byte values, we calldatacopy them
if (varSizes[i] == 32) {
unsigned until = i+1;
while (until < varNames.size() && varSizes[until] == 32)
until += 1;
innerList.push_back(token(unsignedToDecimal((until - i) * 32), m));
innerList.push_back(token(unsignedToDecimal(cum), m));
innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m));
innerList.push_back(token("CALLDATACOPY", m));
cum += (until - i) * 32;
i = until;
}
// Otherwise, we do a clever trick to extract the value
else {
innerList.push_back(token(unsignedToDecimal(32 - varSizes[i]), m));
innerList.push_back(token("256", m));
innerList.push_back(token("EXP", m));
innerList.push_back(token(unsignedToDecimal(cum), m));
innerList.push_back(token("CALLDATALOAD", m));
innerList.push_back(token("DIV", m));
innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m));
innerList.push_back(token("MSTORE", m));
cum += varSizes[i];
i += 1;
}
}
// If caller == origin, then it's from a tx, so unpack, otherwise
// plain copy
programData sub = opcodeify(node.args[1], aux, vaux);
Node ilnode = astnode("", innerList, m);
Node nodelist[] = {
token(unsignedToDecimal(32 * varNames.size()), m),
token("1", m),
token(unsignedToDecimal(nextVarMem), m),
token("CALLDATACOPY", m),
token("CALLER", m),
token("ORIGIN", m),
token("EQ", m),
token("ISZERO", m),
token("$maincode"+symb, m),
token("JUMPI", m),
ilnode,
token("~maincode"+symb, m),
token("JUMPDEST", m),
sub.code
};
inner = pd(sub.aux, multiToken(nodelist, 14, m), 0);
}
// Check if the function call byte is the same
Node nodelist2[] = {
token("0", m),
token("CALLDATALOAD", m),
token("0", m),
token("BYTE", m),
token(unsignedToDecimal(functionCount), m),
token("EQ", m),
token("ISZERO", m),
token("$endcode"+symb, m),
token("JUMPI", m),
inner.code,
token("~endcode"+symb, m),
token("JUMPDEST", m),
};
return pd(inner.aux, multiToken(nodelist2, 12, m), 0);
if (depth < 0 || depth > 1) err("Stack depth mismatch", m);
return pd(aux, astnode("_", subs2, m), 0);
}
// Code blocks
if (node.val == "lll" && node.args.size() == 2) {
@ -372,49 +322,14 @@ programData opcodeify(Node node,
};
return pd(aux, multiToken(nodelist, 8, m), 1);
}
// Array literals
else if (node.val == "array_lit") {
aux.allocUsed = true;
std::vector<Node> nodes;
if (!node.args.size()) {
nodes.push_back(token("MSIZE", m));
return pd(aux, astnode("_", nodes, m));
}
nodes.push_back(token("MSIZE", m));
nodes.push_back(token("0", m));
nodes.push_back(token("MSIZE", m));
nodes.push_back(token(unsignedToDecimal(node.args.size() * 32 - 1), m));
nodes.push_back(token("ADD", m));
nodes.push_back(token("MSTORE8", m));
for (unsigned i = 0; i < node.args.size(); i++) {
Metadata m2 = node.args[i].metadata;
nodes.push_back(token("DUP1", m2));
programVerticalAux vaux2 = vaux;
vaux2.height += 2;
programData sub = opcodeify(node.args[i], aux, vaux2);
if (!sub.outs)
err("Array_lit item " + unsignedToDecimal(i) + " has zero arity", m2);
aux = sub.aux;
nodes.push_back(sub.code);
nodes.push_back(token("SWAP1", m2));
if (i > 0) {
nodes.push_back(token(unsignedToDecimal(i * 32), m2));
nodes.push_back(token("ADD", m2));
}
nodes.push_back(token("MSTORE", m2));
}
return pd(aux, astnode("_", nodes, m), 1);
}
// All other functions/operators
else {
std::vector<Node> subs2;
int depth = opinputs(upperCase(node.val));
if (node.val != "debug") {
if (depth == -1)
err("Not a function or opcode: "+node.val, m);
if ((int)node.args.size() != depth)
err("Invalid arity for "+node.val, m);
}
if (depth == -1)
err("Not a function or opcode: "+node.val, m);
if ((int)node.args.size() != depth)
err("Invalid arity for "+node.val, m);
for (int i = node.args.size() - 1; i >= 0; i--) {
programVerticalAux vaux2 = vaux;
vaux2.height = vaux.height - i - 1 + node.args.size();
@ -424,13 +339,8 @@ programData opcodeify(Node node,
err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata);
subs2.push_back(sub.code);
}
if (node.val == "debug") {
subs2.push_back(token("DUP"+unsignedToDecimal(node.args.size()), m));
for (int i = 0; i <= (int)node.args.size(); i++)
subs2.push_back(token("POP", m));
}
else subs2.push_back(token(upperCase(node.val), m));
int outdepth = node.val == "debug" ? 0 : opoutputs(upperCase(node.val));
subs2.push_back(token(upperCase(node.val), m));
int outdepth = opoutputs(upperCase(node.val));
return pd(aux, astnode("_", subs2, m), outdepth);
}
}
@ -449,15 +359,6 @@ Node finalize(programData c) {
};
bottom.push_back(multiToken(nodelist, 3, m));
}
// If msg.data is being used as an array, then we need to copy it
if (c.aux.calldataUsed) {
Node nodelist[] = {
token("MSIZE", m), token("CALLDATASIZE", m), token("0", m),
token("MSIZE", m), token("CALLDATACOPY", m),
token(c.aux.vars["'msg.data"], m), token("MSTORE", m)
};
bottom.push_back(multiToken(nodelist, 7, m));
}
// The actual code
bottom.push_back(c.code);
return astnode("_", bottom, m);

203
libserpent/functions.cpp

@ -0,0 +1,203 @@
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
#include "lllparser.h"
#include "bignum.h"
#include "optimize.h"
#include "rewriteutils.h"
#include "preprocess.h"
#include "functions.h"
std::string getSignature(std::vector<Node> args) {
std::string o;
for (unsigned i = 0; i < args.size(); i++) {
if (args[i].val == ":" && args[i].args[1].val == "s")
o += "s";
else if (args[i].val == ":" && args[i].args[1].val == "a")
o += "a";
else
o += "i";
}
return o;
}
// Convert a list of arguments into a node containing a
// < datastart, datasz > pair
Node packArguments(std::vector<Node> args, std::string sig,
int funId, Metadata m) {
// Plain old 32 byte arguments
std::vector<Node> nargs;
// Variable-sized arguments
std::vector<Node> vargs;
// Variable sizes
std::vector<Node> sizes;
// Is a variable an array?
std::vector<bool> isArray;
// Fill up above three argument lists
int argCount = 0;
for (unsigned i = 0; i < args.size(); i++) {
Metadata m = args[i].metadata;
if (args[i].val == "=") {
// do nothing
}
else {
// Determine the correct argument type
char argType;
if (sig.size() > 0) {
if (argCount >= (signed)sig.size())
err("Too many args", m);
argType = sig[argCount];
}
else argType = 'i';
// Integer (also usable for short strings)
if (argType == 'i') {
if (args[i].val == ":")
err("Function asks for int, provided string or array", m);
nargs.push_back(args[i]);
}
// Long string
else if (argType == 's') {
if (args[i].val != ":")
err("Must specify string length", m);
vargs.push_back(args[i].args[0]);
sizes.push_back(args[i].args[1]);
isArray.push_back(false);
}
// Array
else if (argType == 'a') {
if (args[i].val != ":")
err("Must specify array length", m);
vargs.push_back(args[i].args[0]);
sizes.push_back(args[i].args[1]);
isArray.push_back(true);
}
else err("Invalid arg type in signature", m);
argCount++;
}
}
int static_arg_size = 1 + (vargs.size() + nargs.size()) * 32;
// Start off by saving the size variables and calculating the total
msn kwargs;
kwargs["funid"] = tkn(utd(funId), m);
std::string pattern =
"(with _sztot "+utd(static_arg_size)+" "
" (with _sizes (alloc "+utd(sizes.size() * 32)+") "
" (seq ";
for (unsigned i = 0; i < sizes.size(); i++) {
std::string sizeIncrement =
isArray[i] ? "(mul 32 _x)" : "_x";
pattern +=
"(with _x $sz"+utd(i)+"(seq "
" (mstore (add _sizes "+utd(i * 32)+") _x) "
" (set _sztot (add _sztot "+sizeIncrement+" )))) ";
kwargs["sz"+utd(i)] = sizes[i];
}
// Allocate memory, and set first data byte
pattern +=
"(with _datastart (alloc (add _sztot 32)) (seq "
" (mstore8 _datastart $funid) ";
// Copy over size variables
for (unsigned i = 0; i < sizes.size(); i++) {
int v = 1 + i * 32;
pattern +=
" (mstore "
" (add _datastart "+utd(v)+") "
" (mload (add _sizes "+utd(v-1)+"))) ";
}
// Store normal arguments
for (unsigned i = 0; i < nargs.size(); i++) {
int v = 1 + (i + sizes.size()) * 32;
pattern +=
" (mstore (add _datastart "+utd(v)+") $"+utd(i)+") ";
kwargs[utd(i)] = nargs[i];
}
// Loop through variable-sized arguments, store them
pattern +=
" (with _pos (add _datastart "+utd(static_arg_size)+") (seq";
for (unsigned i = 0; i < vargs.size(); i++) {
std::string copySize =
isArray[i] ? "(mul 32 (mload (add _sizes "+utd(i * 32)+")))"
: "(mload (add _sizes "+utd(i * 32)+"))";
pattern +=
" (unsafe_mcopy _pos $vl"+utd(i)+" "+copySize+") "
" (set _pos (add _pos "+copySize+")) ";
kwargs["vl"+utd(i)] = vargs[i];
}
// Return a 2-item array containing the start and size
pattern += " (array_lit _datastart _sztot))))))))";
std::string prefix = "_temp_"+mkUniqueToken();
// Fill in pattern, return triple
return subst(parseLLL(pattern), kwargs, prefix, m);
}
// Create a node for argument unpacking
Node unpackArguments(std::vector<Node> vars, Metadata m) {
std::vector<std::string> varNames;
std::vector<std::string> longVarNames;
std::vector<bool> longVarIsArray;
// Fill in variable and long variable names, as well as which
// long variables are arrays and which are strings
for (unsigned i = 0; i < vars.size(); i++) {
if (vars[i].val == ":") {
if (vars[i].args.size() != 2)
err("Malformed def!", m);
longVarNames.push_back(vars[i].args[0].val);
std::string tag = vars[i].args[1].val;
if (tag == "s")
longVarIsArray.push_back(false);
else if (tag == "a")
longVarIsArray.push_back(true);
else
err("Function value can only be string or array", m);
}
else {
varNames.push_back(vars[i].val);
}
}
std::vector<Node> sub;
if (!varNames.size() && !longVarNames.size()) {
// do nothing if we have no arguments
}
else {
std::vector<Node> varNodes;
for (unsigned i = 0; i < longVarNames.size(); i++)
varNodes.push_back(token(longVarNames[i], m));
for (unsigned i = 0; i < varNames.size(); i++)
varNodes.push_back(token(varNames[i], m));
// Copy over variable lengths and short variables
for (unsigned i = 0; i < varNodes.size(); i++) {
int pos = 1 + i * 32;
std::string prefix = (i < longVarNames.size()) ? "_len_" : "";
sub.push_back(asn("untyped", asn("set",
token(prefix+varNodes[i].val, m),
asn("calldataload", tkn(utd(pos), m), m),
m)));
}
// Copy over long variables
if (longVarNames.size() > 0) {
std::vector<Node> sub2;
int pos = varNodes.size() * 32 + 1;
Node tot = tkn("_tot", m);
for (unsigned i = 0; i < longVarNames.size(); i++) {
Node var = tkn(longVarNames[i], m);
Node varlen = longVarIsArray[i]
? asn("mul", tkn("32", m), tkn("_len_"+longVarNames[i], m))
: tkn("_len_"+longVarNames[i], m);
sub2.push_back(asn("untyped",
asn("set", var, asn("alloc", varlen))));
sub2.push_back(asn("calldatacopy", var, tot, varlen));
sub2.push_back(asn("set", tot, asn("add", tot, varlen)));
}
std::string prefix = "_temp_"+mkUniqueToken();
sub.push_back(subst(
astnode("with", tot, tkn(utd(pos), m), asn("seq", sub2)),
msn(),
prefix,
m));
}
}
return asn("seq", sub, m);
}

39
libserpent/functions.h

@ -0,0 +1,39 @@
#ifndef ETHSERP_FUNCTIONS
#define ETHSERP_FUNCTIONS
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
#include "lllparser.h"
#include "bignum.h"
#include "optimize.h"
#include "rewriteutils.h"
#include "preprocess.h"
class argPack {
public:
argPack(Node a, Node b, Node c) {
pre = a;
datastart = b;
datasz = c;
}
Node pre;
Node datastart;
Node datasz;
};
// Get a signature from a function
std::string getSignature(std::vector<Node> args);
// Convert a list of arguments into a <pre, mstart, msize> node
// triple, given the signature of a function
Node packArguments(std::vector<Node> args, std::string sig,
int funId, Metadata m);
// Create a node for argument unpacking
Node unpackArguments(std::vector<Node> vars, Metadata m);
#endif

154
libserpent/opcodes.cpp

@ -0,0 +1,154 @@
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "opcodes.h"
#include "util.h"
#include "bignum.h"
Mapping mapping[] = {
Mapping("STOP", 0x00, 0, 0),
Mapping("ADD", 0x01, 2, 1),
Mapping("MUL", 0x02, 2, 1),
Mapping("SUB", 0x03, 2, 1),
Mapping("DIV", 0x04, 2, 1),
Mapping("SDIV", 0x05, 2, 1),
Mapping("MOD", 0x06, 2, 1),
Mapping("SMOD", 0x07, 2, 1),
Mapping("ADDMOD", 0x08, 3, 1),
Mapping("MULMOD", 0x09, 3, 1),
Mapping("EXP", 0x0a, 2, 1),
Mapping("SIGNEXTEND", 0x0b, 2, 1),
Mapping("LT", 0x10, 2, 1),
Mapping("GT", 0x11, 2, 1),
Mapping("SLT", 0x12, 2, 1),
Mapping("SGT", 0x13, 2, 1),
Mapping("EQ", 0x14, 2, 1),
Mapping("ISZERO", 0x15, 1, 1),
Mapping("AND", 0x16, 2, 1),
Mapping("OR", 0x17, 2, 1),
Mapping("XOR", 0x18, 2, 1),
Mapping("NOT", 0x19, 1, 1),
Mapping("BYTE", 0x1a, 2, 1),
Mapping("SHA3", 0x20, 2, 1),
Mapping("ADDRESS", 0x30, 0, 1),
Mapping("BALANCE", 0x31, 1, 1),
Mapping("ORIGIN", 0x32, 0, 1),
Mapping("CALLER", 0x33, 0, 1),
Mapping("CALLVALUE", 0x34, 0, 1),
Mapping("CALLDATALOAD", 0x35, 1, 1),
Mapping("CALLDATASIZE", 0x36, 0, 1),
Mapping("CALLDATACOPY", 0x37, 3, 0),
Mapping("CODESIZE", 0x38, 0, 1),
Mapping("CODECOPY", 0x39, 3, 0),
Mapping("GASPRICE", 0x3a, 0, 1),
Mapping("EXTCODESIZE", 0x3b, 1, 1),
Mapping("EXTCODECOPY", 0x3c, 4, 0),
Mapping("PREVHASH", 0x40, 0, 1),
Mapping("COINBASE", 0x41, 0, 1),
Mapping("TIMESTAMP", 0x42, 0, 1),
Mapping("NUMBER", 0x43, 0, 1),
Mapping("DIFFICULTY", 0x44, 0, 1),
Mapping("GASLIMIT", 0x45, 0, 1),
Mapping("POP", 0x50, 1, 0),
Mapping("MLOAD", 0x51, 1, 1),
Mapping("MSTORE", 0x52, 2, 0),
Mapping("MSTORE8", 0x53, 2, 0),
Mapping("SLOAD", 0x54, 1, 1),
Mapping("SSTORE", 0x55, 2, 0),
Mapping("JUMP", 0x56, 1, 0),
Mapping("JUMPI", 0x57, 2, 0),
Mapping("PC", 0x58, 0, 1),
Mapping("MSIZE", 0x59, 0, 1),
Mapping("GAS", 0x5a, 0, 1),
Mapping("JUMPDEST", 0x5b, 0, 0),
Mapping("LOG0", 0xa0, 2, 0),
Mapping("LOG1", 0xa1, 3, 0),
Mapping("LOG2", 0xa2, 4, 0),
Mapping("LOG3", 0xa3, 5, 0),
Mapping("LOG4", 0xa4, 6, 0),
Mapping("CREATE", 0xf0, 3, 1),
Mapping("CALL", 0xf1, 7, 1),
Mapping("CALLCODE", 0xf2, 7, 1),
Mapping("RETURN", 0xf3, 2, 0),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
};
std::map<std::string, std::vector<int> > opcodes;
std::map<int, std::string> reverseOpcodes;
// Fetches everything EXCEPT PUSH1..32
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi) {
if (!opcodes.size()) {
int i = 0;
while (mapping[i].op != "---END---") {
Mapping mi = mapping[i];
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out);
i++;
}
for (i = 1; i <= 16; i++) {
opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1);
opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1);
}
for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin();
it != opcodes.end();
it++) {
reverseOpcodes[(*it).second[0]] = (*it).first;
}
}
ops = upperCase(ops);
std::string op;
std::vector<int> opdata;
op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : "";
opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1);
return std::pair<std::string, std::vector<int> >(op, opdata);
}
int opcode(std::string op) {
return _opdata(op, -1).second[0];
}
int opinputs(std::string op) {
return _opdata(op, -1).second[1];
}
int opoutputs(std::string op) {
return _opdata(op, -1).second[2];
}
std::string op(int opcode) {
return _opdata("", opcode).first;
}
std::string lllSpecials[][3] = {
{ "ref", "1", "1" },
{ "get", "1", "1" },
{ "set", "2", "2" },
{ "with", "3", "3" },
{ "comment", "0", "2147483647" },
{ "ops", "0", "2147483647" },
{ "lll", "2", "2" },
{ "seq", "0", "2147483647" },
{ "if", "3", "3" },
{ "unless", "2", "2" },
{ "until", "2", "2" },
{ "alloc", "1", "1" },
{ "---END---", "0", "0" },
};
std::map<std::string, std::pair<int, int> > lllMap;
// Is a function name one of the valid functions above?
bool isValidLLLFunc(std::string f, int argc) {
if (lllMap.size() == 0) {
for (int i = 0; ; i++) {
if (lllSpecials[i][0] == "---END---") break;
lllMap[lllSpecials[i][0]] = std::pair<int, int>(
dtu(lllSpecials[i][1]), dtu(lllSpecials[i][2]));
}
}
return lllMap.count(f)
&& argc >= lllMap[f].first
&& argc <= lllMap[f].second;
}

123
libserpent/opcodes.h

@ -5,6 +5,7 @@
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
class Mapping {
public:
@ -20,119 +21,25 @@ class Mapping {
int out;
};
Mapping mapping[] = {
Mapping("STOP", 0x00, 0, 0),
Mapping("ADD", 0x01, 2, 1),
Mapping("MUL", 0x02, 2, 1),
Mapping("SUB", 0x03, 2, 1),
Mapping("DIV", 0x04, 2, 1),
Mapping("SDIV", 0x05, 2, 1),
Mapping("MOD", 0x06, 2, 1),
Mapping("SMOD", 0x07, 2, 1),
Mapping("ADDMOD", 0x08, 3, 1),
Mapping("MULMOD", 0x09, 3, 1),
Mapping("EXP", 0x0a, 2, 1),
Mapping("SIGNEXTEND", 0x0b, 2, 1),
Mapping("LT", 0x10, 2, 1),
Mapping("GT", 0x11, 2, 1),
Mapping("SLT", 0x12, 2, 1),
Mapping("SGT", 0x13, 2, 1),
Mapping("EQ", 0x14, 2, 1),
Mapping("ISZERO", 0x15, 1, 1),
Mapping("AND", 0x16, 2, 1),
Mapping("OR", 0x17, 2, 1),
Mapping("XOR", 0x18, 2, 1),
Mapping("NOT", 0x19, 1, 1),
Mapping("BYTE", 0x1a, 2, 1),
Mapping("ADDMOD", 0x14, 3, 1),
Mapping("MULMOD", 0x15, 3, 1),
Mapping("SIGNEXTEND", 0x16, 2, 1),
Mapping("SHA3", 0x20, 2, 1),
Mapping("ADDRESS", 0x30, 0, 1),
Mapping("BALANCE", 0x31, 1, 1),
Mapping("ORIGIN", 0x32, 0, 1),
Mapping("CALLER", 0x33, 0, 1),
Mapping("CALLVALUE", 0x34, 0, 1),
Mapping("CALLDATALOAD", 0x35, 1, 1),
Mapping("CALLDATASIZE", 0x36, 0, 1),
Mapping("CALLDATACOPY", 0x37, 3, 1),
Mapping("CODESIZE", 0x38, 0, 1),
Mapping("CODECOPY", 0x39, 3, 1),
Mapping("GASPRICE", 0x3a, 0, 1),
Mapping("PREVHASH", 0x40, 0, 1),
Mapping("COINBASE", 0x41, 0, 1),
Mapping("TIMESTAMP", 0x42, 0, 1),
Mapping("NUMBER", 0x43, 0, 1),
Mapping("DIFFICULTY", 0x44, 0, 1),
Mapping("GASLIMIT", 0x45, 0, 1),
Mapping("POP", 0x50, 1, 0),
Mapping("MLOAD", 0x51, 1, 1),
Mapping("MSTORE", 0x52, 2, 0),
Mapping("MSTORE8", 0x53, 2, 0),
Mapping("SLOAD", 0x54, 1, 1),
Mapping("SSTORE", 0x55, 2, 0),
Mapping("JUMP", 0x56, 1, 0),
Mapping("JUMPI", 0x57, 2, 0),
Mapping("PC", 0x58, 0, 1),
Mapping("MSIZE", 0x59, 0, 1),
Mapping("GAS", 0x5a, 0, 1),
Mapping("JUMPDEST", 0x5b, 0, 0),
Mapping("LOG0", 0xa0, 2, 0),
Mapping("LOG1", 0xa1, 3, 0),
Mapping("LOG2", 0xa2, 4, 0),
Mapping("LOG3", 0xa3, 5, 0),
Mapping("LOG4", 0xa4, 6, 0),
Mapping("CREATE", 0xf0, 3, 1),
Mapping("CALL", 0xf1, 7, 1),
Mapping("RETURN", 0xf2, 2, 0),
Mapping("CALL_CODE", 0xf3, 7, 1),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
};
extern Mapping mapping[];
std::map<std::string, std::vector<int> > opcodes;
std::map<int, std::string> reverseOpcodes;
extern std::map<std::string, std::vector<int> > opcodes;
extern std::map<int, std::string> reverseOpcodes;
// Fetches everything EXCEPT PUSH1..32
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi) {
if (!opcodes.size()) {
int i = 0;
while (mapping[i].op != "---END---") {
Mapping mi = mapping[i];
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out);
i++;
}
for (i = 1; i <= 16; i++) {
opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1);
opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1);
}
for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin();
it != opcodes.end();
it++) {
reverseOpcodes[(*it).second[0]] = (*it).first;
}
}
std::string op;
std::vector<int> opdata;
op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : "";
opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1);
return std::pair<std::string, std::vector<int> >(op, opdata);
}
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi);
int opcode(std::string op);
int opinputs(std::string op);
int opoutputs(std::string op);
int opcode(std::string op) {
return _opdata(op, -1).second[0];
}
std::string op(int opcode);
int opinputs(std::string op) {
return _opdata(op, -1).second[1];
}
extern std::string lllSpecials[][3];
int opoutputs(std::string op) {
return _opdata(op, -1).second[2];
}
extern std::map<std::string, std::pair<int, int> > lllMap;
std::string op(int opcode) {
return _opdata("", opcode).first;
}
bool isValidLLLFunc(std::string f, int argc);
#endif

98
libserpent/optimize.cpp

@ -0,0 +1,98 @@
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
#include "lllparser.h"
#include "bignum.h"
// Compile-time arithmetic calculations
Node optimize(Node inp) {
if (inp.type == TOKEN) {
Node o = tryNumberize(inp);
if (decimalGt(o.val, tt256, true))
err("Value too large (exceeds 32 bytes or 2^256)", inp.metadata);
return o;
}
for (unsigned i = 0; i < inp.args.size(); i++) {
inp.args[i] = optimize(inp.args[i]);
}
// Arithmetic-specific transform
if (inp.val == "+") inp.val = "add";
if (inp.val == "*") inp.val = "mul";
if (inp.val == "-") inp.val = "sub";
if (inp.val == "/") inp.val = "sdiv";
if (inp.val == "^") inp.val = "exp";
if (inp.val == "**") inp.val = "exp";
if (inp.val == "%") inp.val = "smod";
// Degenerate cases for add and mul
if (inp.args.size() == 2) {
if (inp.val == "add" && inp.args[0].type == TOKEN &&
inp.args[0].val == "0") {
Node x = inp.args[1];
inp = x;
}
if (inp.val == "add" && inp.args[1].type == TOKEN &&
inp.args[1].val == "0") {
Node x = inp.args[0];
inp = x;
}
if (inp.val == "mul" && inp.args[0].type == TOKEN &&
inp.args[0].val == "1") {
Node x = inp.args[1];
inp = x;
}
if (inp.val == "mul" && inp.args[1].type == TOKEN &&
inp.args[1].val == "1") {
Node x = inp.args[0];
inp = x;
}
}
// Arithmetic computation
if (inp.args.size() == 2
&& inp.args[0].type == TOKEN
&& inp.args[1].type == TOKEN) {
std::string o;
if (inp.val == "add") {
o = decimalMod(decimalAdd(inp.args[0].val, inp.args[1].val), tt256);
}
else if (inp.val == "sub") {
if (decimalGt(inp.args[0].val, inp.args[1].val, true))
o = decimalSub(inp.args[0].val, inp.args[1].val);
}
else if (inp.val == "mul") {
o = decimalMod(decimalMul(inp.args[0].val, inp.args[1].val), tt256);
}
else if (inp.val == "div" && inp.args[1].val != "0") {
o = decimalDiv(inp.args[0].val, inp.args[1].val);
}
else if (inp.val == "sdiv" && inp.args[1].val != "0"
&& decimalGt(tt255, inp.args[0].val)
&& decimalGt(tt255, inp.args[1].val)) {
o = decimalDiv(inp.args[0].val, inp.args[1].val);
}
else if (inp.val == "mod" && inp.args[1].val != "0") {
o = decimalMod(inp.args[0].val, inp.args[1].val);
}
else if (inp.val == "smod" && inp.args[1].val != "0"
&& decimalGt(tt255, inp.args[0].val)
&& decimalGt(tt255, inp.args[1].val)) {
o = decimalMod(inp.args[0].val, inp.args[1].val);
}
else if (inp.val == "exp") {
o = decimalModExp(inp.args[0].val, inp.args[1].val, tt256);
}
if (o.length()) return token(o, inp.metadata);
}
return inp;
}
// Is a node degenerate (ie. trivial to calculate) ?
bool isDegenerate(Node n) {
return optimize(n).type == TOKEN;
}
// Is a node purely arithmetic?
bool isPureArithmetic(Node n) {
return isNumberLike(optimize(n));
}

19
libserpent/optimize.h

@ -0,0 +1,19 @@
#ifndef ETHSERP_OPTIMIZER
#define ETHSERP_OPTIMIZER
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
// Compile-time arithmetic calculations
Node optimize(Node inp);
// Is a node degenerate (ie. trivial to calculate) ?
bool isDegenerate(Node n);
// Is a node purely arithmetic?
bool isPureArithmetic(Node n);
#endif

86
libserpent/parser.cpp

@ -12,17 +12,15 @@ int precedence(Node tok) {
if (v == ".") return -1;
else if (v == "!" || v == "not") return 1;
else if (v=="^" || v == "**") return 2;
else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 3;
else if (v=="*" || v=="/" || v=="%") return 3;
else if (v=="+" || v=="-") return 4;
else if (v=="<" || v==">" || v=="<=" || v==">=") return 5;
else if (v=="@<" || v=="@>" || v=="@<=" || v=="@>=") return 5;
else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 6;
else if (v=="&&" || v=="and") return 7;
else if (v=="||" || v=="or") return 8;
else if (v==":") return 9;
else if (v=="=") return 10;
else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10;
else if (v=="@/=" || v=="@%=") return 10;
else if (v==":" || v == "::") return 11;
else return 0;
}
@ -223,8 +221,15 @@ Node treefy(std::vector<Node> stream) {
filename = filename.substr(1, filename.length() - 2);
if (!exists(root + filename))
err("File does not exist: "+root + filename, tok.metadata);
oq.back().args.pop_back();
oq.back().args.push_back(parseSerpent(root + filename));
if (v == "inset") {
oq.pop_back();
oq.push_back(parseSerpent(root + filename));
}
else {
oq.back().args.pop_back();
oq.back().args.push_back(
asn("outer", parseSerpent(root + filename), tok.metadata));
}
}
//Useful for debugging
//for (int i = 0; i < oq.size(); i++) {
@ -237,7 +242,7 @@ Node treefy(std::vector<Node> stream) {
err("Output blank", Metadata());
}
else if (oq.size() > 1) {
err("Multiple expressions or unclosed bracket", oq[1].metadata);
return asn("multi", oq, oq[0].metadata);
}
return oq[0];
@ -262,15 +267,9 @@ int spaceCount(std::string s) {
bool bodied(std::string tok) {
return tok == "if" || tok == "elif" || tok == "while"
|| tok == "with" || tok == "def" || tok == "extern"
|| tok == "data";
}
// Is this a command that takes an argument as a child block?
bool childBlocked(std::string tok) {
return tok == "if" || tok == "elif" || tok == "else"
|| tok == "code" || tok == "shared" || tok == "init"
|| tok == "while" || tok == "repeat" || tok == "for"
|| tok == "with" || tok == "def";
|| tok == "data" || tok == "assert" || tok == "return"
|| tok == "fun" || tok == "scope" || tok == "macro"
|| tok == "type";
}
// Are the two commands meant to continue each other?
@ -278,10 +277,7 @@ bool bodiedContinued(std::string prev, std::string tok) {
return (prev == "if" && tok == "elif")
|| (prev == "elif" && tok == "else")
|| (prev == "elif" && tok == "elif")
|| (prev == "if" && tok == "else")
|| (prev == "init" && tok == "code")
|| (prev == "shared" && tok == "code")
|| (prev == "shared" && tok == "init");
|| (prev == "if" && tok == "else");
}
// Is a line of code empty?
@ -310,16 +306,17 @@ Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) {
}
// Tokenize current line
std::vector<Node> tokens = tokenize(main.substr(sp), metadata);
// Remove extraneous tokens, including if / elif
// Remove comments
std::vector<Node> tokens2;
for (unsigned j = 0; j < tokens.size(); j++) {
if (tokens[j].val == "#" || tokens[j].val == "//") break;
if (j >= 1 || !bodied(tokens[j].val)) {
tokens2.push_back(tokens[j]);
}
tokens2.push_back(tokens[j]);
}
if (tokens2.size() > 0 && tokens2.back().val == ":")
bool expectingChildBlock = false;
if (tokens2.size() > 0 && tokens2.back().val == ":") {
tokens2.pop_back();
expectingChildBlock = true;
}
// Parse current line
Node out = parseSerpentTokenStream(tokens2);
// Parse child block
@ -343,14 +340,8 @@ Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) {
for (unsigned i = 0; i < childBlock.size(); i++) {
if (childBlock[i].length() > 0) { cbe = false; break; }
}
// Bring back if / elif into AST
if (bodied(tokens[0].val)) {
std::vector<Node> args;
args.push_back(out);
out = astnode(tokens[0].val, args, out.metadata);
}
// Add child block to AST
if (childBlocked(tokens[0].val)) {
if (expectingChildBlock) {
if (cbe)
err("Expected indented child block!", out.metadata);
out.type = ASTNODE;
@ -360,6 +351,37 @@ Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) {
}
else if (!cbe)
err("Did not expect indented child block!", out.metadata);
else if (out.args.size() && out.args[out.args.size() - 1].val == ":") {
Node n = out.args[out.args.size() - 1];
out.args.pop_back();
out.args.push_back(n.args[0]);
out.args.push_back(n.args[1]);
}
// Bring back if / elif into AST
if (bodied(tokens[0].val)) {
if (out.val != "multi") {
// token not being used in bodied form
}
else if (out.args[0].val == "id")
out = astnode(tokens[0].val, out.args[1].args, out.metadata);
else if (out.args[0].type == TOKEN) {
std::vector<Node> out2;
for (unsigned i = 1; i < out.args.size(); i++)
out2.push_back(out.args[i]);
out = astnode(tokens[0].val, out2, out.metadata);
}
else
out = astnode("fun", out.args, out.metadata);
}
// Multi not supported
if (out.val == "multi")
err("Multiple expressions or unclosed bracket", out.metadata);
// Convert top-level colon expressions into non-colon expressions;
// makes if statements and the like equivalent indented or not
//if (out.val == ":" && out.args[0].type == TOKEN)
// out = asn(out.args[0].val, out.args[1], out.metadata);
//if (bodied(tokens[0].val) && out.args[0].val == ":")
// out = asn(tokens[0].val, out.args[0].args);
if (o.size() == 0 || o.back().type == TOKEN) {
o.push_back(out);
continue;

327
libserpent/preprocess.cpp

@ -0,0 +1,327 @@
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
#include "lllparser.h"
#include "bignum.h"
#include "rewriteutils.h"
#include "optimize.h"
#include "preprocess.h"
#include "functions.h"
#include "opcodes.h"
// Convert a function of the form (def (f x y z) (do stuff)) into
// (if (first byte of ABI is correct) (seq (setup x y z) (do stuff)))
Node convFunction(Node node, int functionCount) {
std::string prefix = "_temp"+mkUniqueToken()+"_";
Metadata m = node.metadata;
if (node.args.size() != 2)
err("Malformed def!", m);
// Collect the list of variable names and variable byte counts
Node unpack = unpackArguments(node.args[0].args, m);
// And the actual code
Node body = node.args[1];
// Main LLL-based function body
return astnode("if",
astnode("eq",
astnode("get", token("__funid", m), m),
token(unsignedToDecimal(functionCount), m),
m),
astnode("seq", unpack, body, m));
}
// Populate an svObj with the arguments needed to determine
// the storage position of a node
svObj getStorageVars(svObj pre, Node node, std::string prefix,
int index) {
Metadata m = node.metadata;
if (!pre.globalOffset.size()) pre.globalOffset = "0";
std::vector<Node> h;
std::vector<std::string> coefficients;
// Array accesses or atoms
if (node.val == "access" || node.type == TOKEN) {
std::string tot = "1";
h = listfyStorageAccess(node);
coefficients.push_back("1");
for (unsigned i = h.size() - 1; i >= 1; i--) {
// Array sizes must be constant or at least arithmetically
// evaluable at compile time
if (!isPureArithmetic(h[i]))
err("Array size must be fixed value", m);
// Create a list of the coefficient associated with each
// array index
coefficients.push_back(decimalMul(coefficients.back(), h[i].val));
}
}
// Tuples
else {
int startc;
// Handle the (fun <fun_astnode> args...) case
if (node.val == "fun") {
startc = 1;
h = listfyStorageAccess(node.args[0]);
}
// Handle the (<fun_name> args...) case, which
// the serpent parser produces when the function
// is a simple name and not a complex astnode
else {
startc = 0;
h = listfyStorageAccess(token(node.val, m));
}
svObj sub = pre;
sub.globalOffset = "0";
// Evaluate tuple elements recursively
for (unsigned i = startc; i < node.args.size(); i++) {
sub = getStorageVars(sub,
node.args[i],
prefix+h[0].val.substr(2)+".",
i-startc);
}
coefficients.push_back(sub.globalOffset);
for (unsigned i = h.size() - 1; i >= 1; i--) {
// Array sizes must be constant or at least arithmetically
// evaluable at compile time
if (!isPureArithmetic(h[i]))
err("Array size must be fixed value", m);
// Create a list of the coefficient associated with each
// array index
coefficients.push_back(decimalMul(coefficients.back(), h[i].val));
}
pre.offsets = sub.offsets;
pre.coefficients = sub.coefficients;
pre.nonfinal = sub.nonfinal;
pre.nonfinal[prefix+h[0].val.substr(2)] = true;
}
pre.coefficients[prefix+h[0].val.substr(2)] = coefficients;
pre.offsets[prefix+h[0].val.substr(2)] = pre.globalOffset;
pre.indices[prefix+h[0].val.substr(2)] = index;
if (decimalGt(tt176, coefficients.back()))
pre.globalOffset = decimalAdd(pre.globalOffset, coefficients.back());
return pre;
}
// Preprocess input containing functions
//
// localExterns is a map of the form, eg,
//
// { x: { foo: 0, bar: 1, baz: 2 }, y: { qux: 0, foo: 1 } ... }
//
// localExternSigs is a map of the form, eg,
//
// { x : { foo: iii, bar: iis, baz: ia }, y: { qux: i, foo: as } ... }
//
// Signifying that x.foo = 0, x.baz = 2, y.foo = 1, etc
// and that x.foo has three integers as arguments, x.bar has two
// integers and a variable-length string, and baz has an integer
// and an array
//
// globalExterns is a one-level map, eg from above
//
// { foo: 1, bar: 1, baz: 2, qux: 0 }
//
// globalExternSigs is a one-level map, eg from above
//
// { foo: as, bar: iis, baz: ia, qux: i}
//
// Note that globalExterns and globalExternSigs may be ambiguous
// Also, a null signature implies an infinite tail of integers
preprocessResult preprocessInit(Node inp) {
Metadata m = inp.metadata;
if (inp.val != "seq")
inp = astnode("seq", inp, m);
std::vector<Node> empty = std::vector<Node>();
Node init = astnode("seq", empty, m);
Node shared = astnode("seq", empty, m);
std::vector<Node> any;
std::vector<Node> functions;
preprocessAux out = preprocessAux();
out.localExterns["self"] = std::map<std::string, int>();
int functionCount = 0;
int storageDataCount = 0;
for (unsigned i = 0; i < inp.args.size(); i++) {
Node obj = inp.args[i];
// Functions
if (obj.val == "def") {
if (obj.args.size() == 0)
err("Empty def", m);
std::string funName = obj.args[0].val;
// Init, shared and any are special functions
if (funName == "init" || funName == "shared" || funName == "any") {
if (obj.args[0].args.size())
err(funName+" cannot have arguments", m);
}
if (funName == "init") init = obj.args[1];
else if (funName == "shared") shared = obj.args[1];
else if (funName == "any") any.push_back(obj.args[1]);
else {
// Other functions
functions.push_back(convFunction(obj, functionCount));
out.localExterns["self"][obj.args[0].val] = functionCount;
out.localExternSigs["self"][obj.args[0].val]
= getSignature(obj.args[0].args);
functionCount++;
}
}
// Extern declarations
else if (obj.val == "extern") {
std::string externName = obj.args[0].val;
Node al = obj.args[1];
if (!out.localExterns.count(externName))
out.localExterns[externName] = std::map<std::string, int>();
for (unsigned i = 0; i < al.args.size(); i++) {
if (al.args[i].val == ":") {
std::string v = al.args[i].args[0].val;
std::string sig = al.args[i].args[1].val;
out.globalExterns[v] = i;
out.globalExternSigs[v] = sig;
out.localExterns[externName][v] = i;
out.localExternSigs[externName][v] = sig;
}
else {
std::string v = al.args[i].val;
out.globalExterns[v] = i;
out.globalExternSigs[v] = "";
out.localExterns[externName][v] = i;
out.localExternSigs[externName][v] = "";
}
}
}
// Custom macros
else if (obj.val == "macro" || (obj.val == "fun" && obj.args[0].val == "macro")) {
// Rules for valid macros:
//
// There are only four categories of valid macros:
//
// 1. a macro where the outer function is something
// which is NOT an existing valid function/extern/datum
// 2. a macro of the form set(c(x), d) where c must NOT
// be an existing valid function/extern/datum
// 3. something of the form access(c(x)), where c must NOT
// be an existing valid function/extern/datum
// 4. something of the form set(access(c(x)), d) where c must
// NOT be an existing valid function/extern/datum
// 5. something of the form with(c(x), d, e) where c must
// NOT be an existing valid function/extern/datum
bool valid = false;
Node pattern;
Node substitution;
int priority;
// Priority not set: default zero
if (obj.val == "macro") {
pattern = obj.args[0];
substitution = obj.args[1];
priority = 0;
}
// Specified priority
else {
pattern = obj.args[1];
substitution = obj.args[2];
if (obj.args[0].args.size())
priority = dtu(obj.args[0].args[0].val);
else
priority = 0;
}
if (opcode(pattern.val) < 0 && !isValidFunctionName(pattern.val))
valid = true;
if (pattern.val == "set" &&
opcode(pattern.args[0].val) < 0 &&
!isValidFunctionName(pattern.args[0].val))
valid = true;
if (pattern.val == "access" &&
opcode(pattern.args[0].val) < 0 &&
!isValidFunctionName(pattern.args[0].val))
if (pattern.val == "set" &&
pattern.args[0].val == "access" &&
opcode(pattern.args[0].args[0].val) < 0 &&
!isValidFunctionName(pattern.args[0].args[0].val))
valid = true;
if (pattern.val == "with" &&
opcode(pattern.args[0].val) < 0 &&
!isValidFunctionName(pattern.args[0].val))
valid = true;
if (valid) {
if (!out.customMacros.count(priority))
out.customMacros[priority] = rewriteRuleSet();
out.customMacros[priority].addRule
(rewriteRule(pattern, substitution));
}
else warn("Macro does not fit valid template: "+printSimple(pattern), m);
}
// Variable types
else if (obj.val == "type") {
std::string typeName = obj.args[0].val;
std::vector<Node> vars = obj.args[1].args;
for (unsigned i = 0; i < vars.size(); i++)
out.types[vars[i].val] = typeName;
}
// Storage variables/structures
else if (obj.val == "data") {
out.storageVars = getStorageVars(out.storageVars,
obj.args[0],
"",
storageDataCount);
storageDataCount += 1;
}
else any.push_back(obj);
}
// Set up top-level AST structure
std::vector<Node> main;
if (shared.args.size()) main.push_back(shared);
if (init.args.size()) main.push_back(init);
std::vector<Node> code;
if (shared.args.size()) code.push_back(shared);
for (unsigned i = 0; i < any.size(); i++)
code.push_back(any[i]);
for (unsigned i = 0; i < functions.size(); i++)
code.push_back(functions[i]);
Node codeNode;
if (functions.size() > 0) {
codeNode = astnode("with",
token("__funid", m),
astnode("byte",
token("0", m),
astnode("calldataload", token("0", m), m),
m),
astnode("seq", code, m),
m);
}
else codeNode = astnode("seq", code, m);
main.push_back(astnode("~return",
token("0", m),
astnode("lll",
codeNode,
token("0", m),
m),
m));
Node result;
if (main.size() == 1) result = main[0];
else result = astnode("seq", main, inp.metadata);
return preprocessResult(result, out);
}
preprocessResult processTypes (preprocessResult pr) {
preprocessAux aux = pr.second;
Node node = pr.first;
if (node.type == TOKEN && aux.types.count(node.val))
node = asn(aux.types[node.val], node, node.metadata);
else if (node.val == "untyped")
return preprocessResult(node.args[0], aux);
else if (node.val == "outer")
return preprocessResult(node, aux);
else {
for (unsigned i = 0; i < node.args.size(); i++) {
node.args[i] =
processTypes(preprocessResult(node.args[i], aux)).first;
}
}
return preprocessResult(node, aux);
}
preprocessResult preprocess(Node n) {
return processTypes(preprocessInit(n));
}

50
libserpent/preprocess.h

@ -0,0 +1,50 @@
#ifndef ETHSERP_PREPROCESSOR
#define ETHSERP_PREPROCESSOR
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
#include "rewriteutils.h"
// Storage variable index storing object
struct svObj {
std::map<std::string, std::string> offsets;
std::map<std::string, int> indices;
std::map<std::string, std::vector<std::string> > coefficients;
std::map<std::string, bool> nonfinal;
std::string globalOffset;
};
// Preprocessing result storing object
class preprocessAux {
public:
preprocessAux() {
globalExterns = std::map<std::string, int>();
localExterns = std::map<std::string, std::map<std::string, int> >();
localExterns["self"] = std::map<std::string, int>();
}
std::map<std::string, int> globalExterns;
std::map<std::string, std::string> globalExternSigs;
std::map<std::string, std::map<std::string, int> > localExterns;
std::map<std::string, std::map<std::string, std::string> > localExternSigs;
std::map<int, rewriteRuleSet > customMacros;
std::map<std::string, std::string> types;
svObj storageVars;
};
#define preprocessResult std::pair<Node, preprocessAux>
// Populate an svObj with the arguments needed to determine
// the storage position of a node
svObj getStorageVars(svObj pre, Node node, std::string prefix="",
int index=0);
// Preprocess a function (see cpp for details)
preprocessResult preprocess(Node inp);
#endif

1206
libserpent/rewriter.cpp

File diff suppressed because it is too large

211
libserpent/rewriteutils.cpp

@ -0,0 +1,211 @@
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
#include "lllparser.h"
#include "bignum.h"
#include "rewriteutils.h"
#include "optimize.h"
// Valid functions and their min and max argument counts
std::string validFunctions[][3] = {
{ "if", "2", "3" },
{ "unless", "2", "2" },
{ "while", "2", "2" },
{ "until", "2", "2" },
{ "alloc", "1", "1" },
{ "array", "1", "1" },
{ "call", "2", tt256 },
{ "callcode", "2", tt256 },
{ "create", "1", "4" },
{ "getch", "2", "2" },
{ "setch", "3", "3" },
{ "sha3", "1", "2" },
{ "return", "1", "2" },
{ "inset", "1", "1" },
{ "min", "2", "2" },
{ "max", "2", "2" },
{ "array_lit", "0", tt256 },
{ "seq", "0", tt256 },
{ "log", "1", "6" },
{ "outer", "1", "1" },
{ "set", "2", "2" },
{ "get", "1", "1" },
{ "ref", "1", "1" },
{ "declare", "1", tt256 },
{ "with", "3", "3" },
{ "outer", "1", "1" },
{ "mcopy", "3", "3" },
{ "unsafe_mcopy", "3", "3" },
{ "save", "3", "3" },
{ "load", "2", "2" },
{ "---END---", "", "" } //Keep this line at the end of the list
};
std::map<std::string, bool> vfMap;
// Is a function name one of the valid functions above?
bool isValidFunctionName(std::string f) {
if (vfMap.size() == 0) {
for (int i = 0; ; i++) {
if (validFunctions[i][0] == "---END---") break;
vfMap[validFunctions[i][0]] = true;
}
}
return vfMap.count(f);
}
// Cool function for debug purposes (named cerrStringList to make
// all prints searchable via 'cerr')
void cerrStringList(std::vector<std::string> s, std::string suffix) {
for (unsigned i = 0; i < s.size(); i++) std::cerr << s[i] << " ";
std::cerr << suffix << "\n";
}
// Convert:
// self.cow -> ["cow"]
// self.horse[0] -> ["horse", "0"]
// self.a[6][7][self.storage[3]].chicken[9] ->
// ["6", "7", (sload 3), "chicken", "9"]
std::vector<Node> listfyStorageAccess(Node node) {
std::vector<Node> out;
std::vector<Node> nodez;
nodez.push_back(node);
while (1) {
if (nodez.back().type == TOKEN) {
out.push_back(token("--" + nodez.back().val, node.metadata));
std::vector<Node> outrev;
for (int i = (signed)out.size() - 1; i >= 0; i--) {
outrev.push_back(out[i]);
}
return outrev;
}
if (nodez.back().val == ".")
nodez.back().args[1].val = "--" + nodez.back().args[1].val;
if (nodez.back().args.size() == 0)
err("Error parsing storage variable statement", node.metadata);
if (nodez.back().args.size() == 1)
out.push_back(token(tt256m1, node.metadata));
else
out.push_back(nodez.back().args[1]);
nodez.push_back(nodez.back().args[0]);
}
}
// Is the given node something of the form
// self.cow
// self.horse[0]
// self.a[6][7][self.storage[3]].chicken[9]
bool isNodeStorageVariable(Node node) {
std::vector<Node> nodez;
nodez.push_back(node);
while (1) {
if (nodez.back().type == TOKEN) return false;
if (nodez.back().args.size() == 0) return false;
if (nodez.back().val != "." && nodez.back().val != "access")
return false;
if (nodez.back().args[0].val == "self") return true;
nodez.push_back(nodez.back().args[0]);
}
}
// Main pattern matching routine, for those patterns that can be expressed
// using our standard mini-language above
//
// Returns two values. First, a boolean to determine whether the node matches
// the pattern, second, if the node does match then a map mapping variables
// in the pattern to nodes
matchResult match(Node p, Node n) {
matchResult o;
o.success = false;
if (p.type == TOKEN) {
if (p.val == n.val && n.type == TOKEN) o.success = true;
else if (p.val[0] == '$' || p.val[0] == '@') {
o.success = true;
o.map[p.val.substr(1)] = n;
}
}
else if (n.type==TOKEN || p.val!=n.val || p.args.size()!=n.args.size()) {
// do nothing
}
else {
for (unsigned i = 0; i < p.args.size(); i++) {
matchResult oPrime = match(p.args[i], n.args[i]);
if (!oPrime.success) {
o.success = false;
return o;
}
for (std::map<std::string, Node>::iterator it = oPrime.map.begin();
it != oPrime.map.end();
it++) {
o.map[(*it).first] = (*it).second;
}
}
o.success = true;
}
return o;
}
// Fills in the pattern with a dictionary mapping variable names to
// nodes (these dicts are generated by match). Match and subst together
// create a full pattern-matching engine.
Node subst(Node pattern,
std::map<std::string, Node> dict,
std::string varflag,
Metadata m) {
// Swap out patterns at the token level
if (pattern.metadata.ln == -1)
pattern.metadata = m;
if (pattern.type == TOKEN &&
pattern.val[0] == '$') {
if (dict.count(pattern.val.substr(1))) {
return dict[pattern.val.substr(1)];
}
else {
return token(varflag + pattern.val.substr(1), m);
}
}
// Other tokens are untouched
else if (pattern.type == TOKEN) {
return pattern;
}
// Substitute recursively for ASTs
else {
std::vector<Node> args;
for (unsigned i = 0; i < pattern.args.size(); i++) {
args.push_back(subst(pattern.args[i], dict, varflag, m));
}
return asn(pattern.val, args, m);
}
}
// Transforms a sequence containing two-argument with statements
// into a statement containing those statements in nested form
Node withTransform (Node source) {
Node o = token("--");
Metadata m = source.metadata;
std::vector<Node> args;
for (int i = source.args.size() - 1; i >= 0; i--) {
Node a = source.args[i];
if (a.val == "with" && a.args.size() == 2) {
std::vector<Node> flipargs;
for (int j = args.size() - 1; j >= 0; j--)
flipargs.push_back(args[i]);
if (o.val != "--")
flipargs.push_back(o);
o = asn("with", a.args[0], a.args[1], asn("seq", flipargs, m), m);
args = std::vector<Node>();
}
else {
args.push_back(a);
}
}
std::vector<Node> flipargs;
for (int j = args.size() - 1; j >= 0; j--)
flipargs.push_back(args[j]);
if (o.val != "--")
flipargs.push_back(o);
return asn("seq", flipargs, m);
}

76
libserpent/rewriteutils.h

@ -0,0 +1,76 @@
#ifndef ETHSERP_REWRITEUTILS
#define ETHSERP_REWRITEUTILS
#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include "util.h"
// Valid functions and their min and max argument counts
extern std::string validFunctions[][3];
extern std::map<std::string, bool> vfMap;
bool isValidFunctionName(std::string f);
// Converts deep array access into ordered list of the arguments
// along the descent
std::vector<Node> listfyStorageAccess(Node node);
// Cool function for debug purposes (named cerrStringList to make
// all prints searchable via 'cerr')
void cerrStringList(std::vector<std::string> s, std::string suffix="");
// Is the given node something of the form
// self.cow
// self.horse[0]
// self.a[6][7][self.storage[3]].chicken[9]
bool isNodeStorageVariable(Node node);
// Applies rewrite rules adding without wrapper
Node rewriteChunk(Node inp);
// Match result storing object
struct matchResult {
bool success;
std::map<std::string, Node> map;
};
// Match node to pattern
matchResult match(Node p, Node n);
// Substitute node using pattern
Node subst(Node pattern,
std::map<std::string, Node> dict,
std::string varflag,
Metadata m);
Node withTransform(Node source);
class rewriteRule {
public:
rewriteRule(Node p, Node s) {
pattern = p;
substitution = s;
}
Node pattern;
Node substitution;
};
class rewriteRuleSet {
public:
rewriteRuleSet() {
ruleLists = std::map<std::string, std::vector<rewriteRule> >();
}
void addRule(rewriteRule r) {
if (!ruleLists.count(r.pattern.val))
ruleLists[r.pattern.val] = std::vector<rewriteRule>();
ruleLists[r.pattern.val].push_back(r);
}
std::map<std::string, std::vector<rewriteRule> > ruleLists;
};
#endif

4
libserpent/tokenize.cpp

@ -13,8 +13,8 @@ int chartype(char c) {
if (c >= '0' && c <= '9') return ALPHANUM;
else if (c >= 'a' && c <= 'z') return ALPHANUM;
else if (c >= 'A' && c <= 'Z') return ALPHANUM;
else if (std::string("~_$").find(c) != std::string::npos) return ALPHANUM;
else if (c == '\t' || c == ' ' || c == '\n') return SPACE;
else if (std::string("~_$@").find(c) != std::string::npos) return ALPHANUM;
else if (c == '\t' || c == ' ' || c == '\n' || c == '\r') return SPACE;
else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK;
else if (c == '"') return DQUOTE;
else if (c == '\'') return SQUOTE;

57
libserpent/util.cpp

@ -2,7 +2,6 @@
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include "util.h"
#include "bignum.h"
#include <fstream>
@ -28,6 +27,11 @@ Node astnode(std::string val, std::vector<Node> args, Metadata met) {
}
//AST node constructors for a specific number of children
Node astnode(std::string val, Metadata met) {
std::vector<Node> args;
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Metadata met) {
std::vector<Node> args;
args.push_back(a);
@ -49,6 +53,16 @@ Node astnode(std::string val, Node a, Node b, Node c, Metadata met) {
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Node b, Node c, Node d, Metadata met) {
std::vector<Node> args;
args.push_back(a);
args.push_back(b);
args.push_back(c);
args.push_back(d);
return astnode(val, args, met);
}
// Print token list
std::string printTokens(std::vector<Node> tokens) {
std::string s = "";
@ -146,6 +160,15 @@ std::string indentLines(std::string inp) {
return joinLines(lines);
}
// Binary to hexadecimal
std::string binToNumeric(std::string inp) {
std::string o = "0";
for (unsigned i = 0; i < inp.length(); i++) {
o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i]));
}
return o;
}
// Converts string to simple numeric format
std::string strToNumeric(std::string inp) {
std::string o = "0";
@ -154,7 +177,7 @@ std::string strToNumeric(std::string inp) {
}
else if ((inp[0] == '"' && inp[inp.length()-1] == '"')
|| (inp[0] == '\'' && inp[inp.length()-1] == '\'')) {
for (unsigned i = 1; i < inp.length() - 1; i++) {
for (unsigned i = 1; i < inp.length() - 1; i++) {
o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i]));
}
}
@ -181,6 +204,14 @@ bool isNumberLike(Node node) {
return strToNumeric(node.val) != "";
}
// Is the number decimal?
bool isDecimal(std::string inp) {
for (unsigned i = 0; i < inp.length(); i++) {
if (inp[i] < '0' || inp[i] > '9') return false;
}
return true;
}
//Normalizes number representations
Node nodeToNumeric(Node node) {
std::string o = strToNumeric(node.val);
@ -246,6 +277,14 @@ void err(std::string errtext, Metadata met) {
throw(err);
}
//Report warning
void warn(std::string errtext, Metadata met) {
std::string err = "Warning (file \"" + met.file + "\", line " +
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) +
"): " + errtext;
std::cerr << err << "\n";
}
//Bin to hex
std::string binToHex(std::string inp) {
std::string o = "";
@ -280,7 +319,15 @@ std::string upperCase(std::string inp) {
//Three-int vector
std::vector<int> triple(int a, int b, int c) {
std::vector<int> o;
o.push_back(a); o.push_back(b); o.push_back(c);
return o;
std::vector<int> v;
v.push_back(a);
v.push_back(b);
v.push_back(c);
return v;
}
//Extend node vector
std::vector<Node> extend(std::vector<Node> a, std::vector<Node> b) {
for (unsigned i = 0; i < b.size(); i++) a.push_back(b[i]);
return a;
}

36
libserpent/util.h

@ -28,30 +28,36 @@ const int TOKEN = 0,
// Stores metadata about each token
class Metadata {
public:
Metadata(std::string File="main", int Ln=0, int Ch=0) {
Metadata(std::string File="main", int Ln=-1, int Ch=-1) {
file = File;
ln = Ln;
ch = Ch;
fixed = false;
}
std::string file;
int ln;
int ch;
bool fixed;
};
std::string mkUniqueToken();
// type can be TOKEN or ASTNODE
struct Node {
int type;
std::string val;
std::vector<Node> args;
Metadata metadata;
class Node {
public:
int type;
std::string val;
std::vector<Node> args;
Metadata metadata;
};
Node token(std::string val, Metadata met=Metadata());
Node astnode(std::string val, std::vector<Node> args, Metadata met=Metadata());
Node astnode(std::string val, Metadata met=Metadata());
Node astnode(std::string val, Node a, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b, Node c, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b,
Node c, Node d, Metadata met=Metadata());
// Number of tokens in a tree
int treeSize(Node prog);
@ -74,6 +80,9 @@ std::string joinLines(std::vector<std::string> lines);
// Indent all lines by 4 spaces
std::string indentLines(std::string inp);
// Converts binary to simple numeric format
std::string binToNumeric(std::string inp);
// Converts string to simple numeric format
std::string strToNumeric(std::string inp);
@ -98,6 +107,9 @@ bool exists(std::string fileName);
//Report error
void err(std::string errtext, Metadata met);
//Report warning
void warn(std::string errtext, Metadata met);
//Bin to hex
std::string binToHex(std::string inp);
@ -110,4 +122,16 @@ std::string upperCase(std::string inp);
//Three-int vector
std::vector<int> triple(int a, int b, int c);
//Extend node vector
std::vector<Node> extend(std::vector<Node> a, std::vector<Node> b);
// Is the number decimal?
bool isDecimal(std::string inp);
#define asn astnode
#define tkn token
#define msi std::map<std::string, int>
#define msn std::map<std::string, Node>
#define mss std::map<std::string, std::string>
#endif

20
libsolidity/CompilerStack.cpp

@ -36,13 +36,12 @@ namespace dev
namespace solidity
{
void CompilerStack::addSource(string const& _name, string const& _content)
bool CompilerStack::addSource(string const& _name, string const& _content)
{
if (m_sources.count(_name))
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists."));
bool existed = m_sources.count(_name);
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
return existed;
}
void CompilerStack::setSource(string const& _sourceCode)
@ -181,6 +180,11 @@ SourceUnit const& CompilerStack::getAST(string const& _sourceName) const
return *getSource(_sourceName).ast;
}
ContractDefinition const& CompilerStack::getContractDefinition(string const& _contractName) const
{
return *getContract(_contractName).contract;
}
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
{
CompilerStack stack;
@ -234,9 +238,13 @@ CompilerStack::Contract const& CompilerStack::getContract(string const& _contrac
{
if (m_contracts.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
string contractName = _contractName;
if (_contractName.empty())
return m_contracts.begin()->second;
auto it = m_contracts.find(_contractName);
// try to find the "last contract"
for (ASTPointer<ASTNode> const& node: m_sourceOrder.back()->ast->getNodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contractName = contract->getName();
auto it = m_contracts.find(contractName);
if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));
return it->second;

9
libsolidity/CompilerStack.h

@ -57,7 +57,8 @@ public:
CompilerStack(): m_parseSuccessful(false) {}
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
void addSource(std::string const& _name, std::string const& _content);
/// @returns true if a source object by the name already existed and was replaced.
bool addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse();
@ -86,9 +87,13 @@ public:
/// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Returns the previously used scanner, useful for counting lines during error reporting.
/// @returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = "") const;
/// @returns the parsed source unit with the supplied name.
SourceUnit const& getAST(std::string const& _sourceName = "") const;
/// @returns the parsed contract with the supplied name. Throws an exception if the contract
/// does not exist.
ContractDefinition const& getContractDefinition(std::string const& _contractName) const;
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information.

13
sc/cmdline.cpp

@ -10,6 +10,19 @@ int main(int argv, char** argc) {
std::cerr << "Must provide a command and arguments! Try parse, rewrite, compile, assemble\n";
return 0;
}
if (argv == 2 && (std::string(argc[1]) == "--help" || std::string(argc[1]) == "-h" )) {
std::cout << argc[1] << "\n";
std::cout << "serpent command input\n";
std::cout << "where input -s for from stdin, a file, or interpreted as serpent code if does not exist as file.";
std::cout << "where command: \n";
std::cout << " parse: Just parses and returns s-expression code.\n";
std::cout << " rewrite: Parse, use rewrite rules print s-expressions of result.\n";
std::cout << " compile: Return resulting compiled EVM code in hex.\n";
std::cout << " assemble: Return result from step before compilation.\n";
return 0;
}
std::string flag = "";
std::string command = argc[1];
std::string input;

0
test/solidityJSONInterfaceTest.cpp → test/SolidityABIJSON.cpp

0
test/solidityCompiler.cpp → test/SolidityCompiler.cpp

2
test/solidityEndToEndTest.cpp → test/SolidityEndToEndTest.cpp

@ -36,7 +36,7 @@ namespace solidity
namespace test
{
BOOST_FIXTURE_TEST_SUITE(SolidityCompilerEndToEndTest, ExecutionFramework)
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, ExecutionFramework)
BOOST_AUTO_TEST_CASE(smoke_test)
{

0
test/solidityExpressionCompiler.cpp → test/SolidityExpressionCompiler.cpp

0
test/solidityNameAndTypeResolution.cpp → test/SolidityNameAndTypeResolution.cpp

0
test/solidityNatspecJSON.cpp → test/SolidityNatspecJSON.cpp

2
test/solidityOptimizerTest.cpp → test/SolidityOptimizer.cpp

@ -71,7 +71,7 @@ protected:
Address m_nonOptimizedContract;
};
BOOST_FIXTURE_TEST_SUITE(SolidityOptimizerTest, OptimizerTestFramework)
BOOST_FIXTURE_TEST_SUITE(SolidityOptimizer, OptimizerTestFramework)
BOOST_AUTO_TEST_CASE(smoke_test)
{

0
test/solidityParser.cpp → test/SolidityParser.cpp

0
test/solidityScanner.cpp → test/SolidityScanner.cpp

145
test/stRefundTestFiller.json

@ -0,0 +1,145 @@
{
"refund_getEtherBack" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 1 ]] 0 }",
"storage" : {
"0x01" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "850",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"refund_changeNonZeroStorage" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 1 ]] 23 }",
"storage" : {
"0x01" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "850",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"refund_OOG" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 1 ]] 0 }",
"storage" : {
"0x01" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "500",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "500",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"refund_NoOOG_1" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 1 ]] 0 }",
"storage" : {
"0x01" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "502",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "502",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
}
}

10
test/state.cpp

@ -140,11 +140,21 @@ BOOST_AUTO_TEST_CASE(stInitCodeTest)
dev::test::executeTests("stInitCodeTest", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stTransactionTest)
{
dev::test::executeTests("stTransactionTest", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stSpecialTest)
{
dev::test::executeTests("stSpecialTest", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stRefundTest)
{
dev::test::executeTests("stRefundTest", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stCreateTest)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)

Loading…
Cancel
Save