Browse Source

Merge branch 'develop' into p2p

Conflicts:
	eth/main.cpp
	libwebthree/WebThree.cpp
	libwebthree/WebThree.h
	neth/main.cpp
cl-refactor
subtly 10 years ago
parent
commit
2455966c11
  1. 8
      CMakeLists.txt
  2. 2
      CodingStandards.txt
  3. 1
      alethzero/CMakeLists.txt
  4. 34
      alethzero/MainWin.cpp
  5. 2
      alethzero/NatspecHandler.h
  6. 3
      cmake/FindJsoncpp.cmake
  7. 17
      cmake/scripts/jsonrpcstub.cmake
  8. 98
      eth/main.cpp
  9. 6
      libdevcore/CommonData.cpp
  10. 9
      libdevcore/CommonData.h
  11. 7
      libethereum/Client.cpp
  12. 2
      libethereum/Client.h
  13. 3
      libsolidity/AST.cpp
  14. 2
      libsolidity/ASTJsonConverter.h
  15. 7
      libsolidity/Compiler.cpp
  16. 250
      libsolidity/CompilerUtils.cpp
  17. 33
      libsolidity/CompilerUtils.h
  18. 203
      libsolidity/ExpressionCompiler.cpp
  19. 34
      libsolidity/ExpressionCompiler.h
  20. 2
      libsolidity/InterfaceHandler.h
  21. 6
      libsolidity/Token.h
  22. 70
      libsolidity/Types.cpp
  23. 40
      libsolidity/Types.h
  24. 6
      libwebthree/WebThree.cpp
  25. 2
      libwebthree/WebThree.h
  26. 1
      lllc/CMakeLists.txt
  27. 1
      mix/CMakeLists.txt
  28. 81
      neth/main.cpp
  29. 1
      solc/CMakeLists.txt
  30. 2
      test/SolidityABIJSON.cpp
  31. 139
      test/SolidityEndToEndTest.cpp
  32. 2
      test/SolidityNatspecJSON.cpp
  33. 2
      test/solidityExecutionFramework.h

8
CMakeLists.txt

@ -109,6 +109,7 @@ cmake_policy(SET CMP0015 NEW)
createDefaultCacheConfig() createDefaultCacheConfig()
configureProject() configureProject()
message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}")
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}") message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}")
@ -140,15 +141,15 @@ endif()
add_subdirectory(libdevcore) add_subdirectory(libdevcore)
add_subdirectory(libevmcore) add_subdirectory(libevmcore)
add_subdirectory(liblll) add_subdirectory(liblll)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(libserpent) add_subdirectory(libserpent)
add_subdirectory(sc)
endif () endif ()
add_subdirectory(libsolidity) add_subdirectory(libsolidity)
add_subdirectory(lllc) add_subdirectory(lllc)
add_subdirectory(solc) add_subdirectory(solc)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(sc)
endif()
if (JSONRPC) if (JSONRPC)
add_subdirectory(libweb3jsonrpc) add_subdirectory(libweb3jsonrpc)
@ -166,6 +167,7 @@ add_subdirectory(libethereum)
add_subdirectory(libwebthree) add_subdirectory(libwebthree)
add_subdirectory(test) add_subdirectory(test)
add_subdirectory(eth) add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
add_subdirectory(exp) add_subdirectory(exp)
endif () endif ()

2
CodingStandards.txt

@ -93,7 +93,7 @@ b. Only one per line.
c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)).
d. Favour declarations close to use; don't habitually declare at top of scope ala C. d. Favour declarations close to use; don't habitually declare at top of scope ala C.
e. Always pass non-trivial parameters with a const& suffix. e. Always pass non-trivial parameters with a const& suffix.
f. If a func tion returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires.
g. Never use a macro where adequate non-preprocessor C++ can be written. g. Never use a macro where adequate non-preprocessor C++ can be written.
h. Prefer "using NewType = OldType" to "typedef OldType NewType". h. Prefer "using NewType = OldType" to "typedef OldType NewType".

1
alethzero/CMakeLists.txt

@ -11,6 +11,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..) include_directories(..)
qt5_wrap_ui(ui_Main.h Main.ui) qt5_wrap_ui(ui_Main.h Main.ui)

34
alethzero/MainWin.cpp

@ -572,7 +572,23 @@ Address Main::fromString(QString const& _n) const
}*/ }*/
if (_n.size() == 40) if (_n.size() == 40)
return Address(fromHex(_n.toStdString())); {
try
{
return Address(fromHex(_n.toStdString(), ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, address rejected";
cwarn << boost::diagnostic_information(_e);
return Address();
}
catch (...)
{
cwarn << "address rejected";
return Address();
}
}
else else
return Address(); return Address();
} }
@ -1325,8 +1341,20 @@ void Main::ourAccountsRowsMoved()
void Main::on_inject_triggered() void Main::on_inject_triggered()
{ {
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex"); QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex");
bytes b = fromHex(s.toStdString()); try
ethereum()->inject(&b); {
bytes b = fromHex(s.toStdString(), ThrowType::Throw);
ethereum()->inject(&b);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
} }
void Main::on_blocks_currentItemChanged() void Main::on_blocks_currentItemChanged()

2
alethzero/NatspecHandler.h

@ -26,7 +26,7 @@
#pragma warning(disable: 4100 4267) #pragma warning(disable: 4100 4267)
#include <leveldb/db.h> #include <leveldb/db.h>
#pragma warning(pop) #pragma warning(pop)
#include <jsoncpp/json/json.h> #include <json/json.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
namespace ldb = leveldb; namespace ldb = leveldb;

3
cmake/FindJsoncpp.cmake

@ -12,7 +12,8 @@
# only look in default directories # only look in default directories
find_path( find_path(
JSONCPP_INCLUDE_DIR JSONCPP_INCLUDE_DIR
NAMES jsoncpp/json/json.h NAMES json/json.h
PATH_SUFFIXES jsoncpp
DOC "jsoncpp include dir" DOC "jsoncpp include dir"
) )

17
cmake/scripts/jsonrpcstub.cmake

@ -27,10 +27,19 @@ set(CLIENT_OUTFILE "${ETH_CLIENT_DIR}/${ETH_CLIENT_NAME_LOWER}.h")
execute_process( execute_process(
COMMAND ${ETH_JSON_RPC_STUB} ${ETH_SPEC_PATH} COMMAND ${ETH_JSON_RPC_STUB} ${ETH_SPEC_PATH}
--cpp-server=${ETH_SERVER_NAME} --cpp-server-file=${SERVER_TMPFILE} --cpp-server=${ETH_SERVER_NAME} --cpp-server-file=${SERVER_TMPFILE}
--cpp-client=${ETH_CLIENT_NAME} --cpp-client-file=${CLIENT_TMPFILE} --cpp-client=${ETH_CLIENT_NAME} --cpp-client-file=${CLIENT_TMPFILE}
OUTPUT_VARIABLE ERR ERROR_QUIET
) )
include("${ETH_SOURCE_DIR}/cmake/EthUtils.cmake") # don't throw fatal error on jsonrpcstub error, someone might have old version of jsonrpcstub,
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}") # he does not need to upgrade it if he is not working on JSON RPC
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}") # show him warning instead
if (ERR)
message(WARNING "Your version of jsonrcpstub tool is not supported. Please upgrade it.")
message(WARNING "${ERR}")
else()
include("${ETH_SOURCE_DIR}/cmake/EthUtils.cmake")
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}")
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}")
endif()

98
eth/main.cpp

@ -119,6 +119,7 @@ void help()
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl << " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl << " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl << " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl << " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl
@ -195,6 +196,7 @@ int main(int argc, char** argv)
unsigned mining = ~(unsigned)0; unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full; NodeMode mode = NodeMode::Full;
unsigned peers = 5; unsigned peers = 5;
int miners = -1;
bool interactive = false; bool interactive = false;
#if ETH_JSONRPC #if ETH_JSONRPC
int jsonrpc = -1; int jsonrpc = -1;
@ -258,7 +260,23 @@ int main(int argc, char** argv)
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i]; clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
coinbase = h160(fromHex(argv[++i])); {
try
{
coinbase = h160(fromHex(argv[++i], ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
break;
}
catch (...)
{
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i]))); us = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
@ -295,6 +313,8 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]); g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]); peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) else if ((arg == "-o" || arg == "--mode") && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
@ -339,7 +359,9 @@ int main(int argc, char** argv)
false, false,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(), mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs, netPrefs,
&nodesState); &nodesState,
miners
);
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -530,9 +552,21 @@ int main(int argc, char** argv)
} }
else else
{ {
Secret secret = h256(fromHex(sechex)); try
Address dest = h160(fromHex(hexAddr)); {
c->transact(secret, amount, dest, data, gas, gasPrice); Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->transact(secret, amount, dest, data, gas, gasPrice);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
} }
} }
else else
@ -581,8 +615,20 @@ int main(int argc, char** argv)
auto blockData = bc.block(h); auto blockData = bc.block(h);
BlockInfo info(blockData); BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0); u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(hexAddr)); try
c->transact(us.secret(), amount, dest, bytes(), minGas); {
Address dest = h160(fromHex(hexAddr, ThrowType::Throw));
c->transact(us.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
} }
} }
else else
@ -613,14 +659,30 @@ int main(int argc, char** argv)
{ {
cnote << "Assembled:"; cnote << "Assembled:";
stringstream ssc; stringstream ssc;
init = fromHex(sinit); try
{
init = fromHex(sinit, ThrowType::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string()); ssc.str(string());
ssc << disassemble(init); ssc << disassemble(init);
cnote << "Init:"; cnote << "Init:";
cnote << ssc.str(); cnote << ssc.str();
} }
u256 minGas = (u256)Client::txGas(init, 0); u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0) if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment"; cwarn << "Invalid endowment";
else if (gas < minGas) else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas; cwarn << "Minimum gas amount is" << minGas;
@ -757,8 +819,22 @@ int main(int argc, char** argv)
if (hexAddr.length() != 40) if (hexAddr.length() != 40)
cwarn << "Invalid address length: " << hexAddr.length(); cwarn << "Invalid address length: " << hexAddr.length();
else else
coinbase = h160(fromHex(hexAddr)); {
} try
{
coinbase = h160(fromHex(hexAddr, ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "coinbase rejected";
}
}
}
else else
cwarn << "Require parameter: setAddress HEXADDRESS"; cwarn << "Require parameter: setAddress HEXADDRESS";
} }

6
libdevcore/CommonData.cpp

@ -78,7 +78,7 @@ int dev::fromHex(char _i)
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i));
} }
bytes dev::fromHex(std::string const& _s) bytes dev::fromHex(std::string const& _s, ThrowType _throw)
{ {
unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0;
std::vector<uint8_t> ret; std::vector<uint8_t> ret;
@ -96,6 +96,8 @@ bytes dev::fromHex(std::string const& _s)
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information(); cwarn << boost::current_exception_diagnostic_information();
#endif #endif
if (_throw == ThrowType::Throw)
throw;
} }
for (unsigned i = s; i < _s.size(); i += 2) for (unsigned i = s; i < _s.size(); i += 2)
try try
@ -107,6 +109,8 @@ bytes dev::fromHex(std::string const& _s)
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information(); cwarn << boost::current_exception_diagnostic_information();
#endif #endif
if (_throw == ThrowType::Throw)
throw;
} }
return ret; return ret;
} }

9
libdevcore/CommonData.h

@ -35,6 +35,12 @@ namespace dev
// String conversion functions, mainly to/from hex/nibble/byte representations. // String conversion functions, mainly to/from hex/nibble/byte representations.
enum class ThrowType
{
NoThrow = 0,
Throw = 1,
};
/// Convert a series of bytes to the corresponding string of hex duplets. /// Convert a series of bytes to the corresponding string of hex duplets.
/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte. /// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169" /// @example toHex("A\x69") == "4169"
@ -53,7 +59,8 @@ int fromHex(char _i);
/// Converts a (printable) ASCII hex string into the corresponding byte stream. /// Converts a (printable) ASCII hex string into the corresponding byte stream.
/// @example fromHex("41626261") == asBytes("Abba") /// @example fromHex("41626261") == asBytes("Abba")
bytes fromHex(std::string const& _s); /// If _throw = ThrowType::NoThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception.
bytes fromHex(std::string const& _s, ThrowType _throw = ThrowType::NoThrow);
#if 0 #if 0
std::string toBase58(bytesConstRef _data); std::string toBase58(bytesConstRef _data);

7
libethereum/Client.cpp

@ -59,7 +59,7 @@ void VersionChecker::setOk()
} }
} }
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId): Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId, int miners):
Worker("eth"), Worker("eth"),
m_vc(_dbPath), m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean), m_bc(_dbPath, !m_vc.ok() || _forceClean),
@ -69,7 +69,10 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean,
{ {
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
setMiningThreads(); if (miners > -1)
setMiningThreads(miners);
else
setMiningThreads();
if (_dbPath.size()) if (_dbPath.size())
Defaults::setDBPath(_dbPath); Defaults::setDBPath(_dbPath);
m_vc.setOk(); m_vc.setOk();

2
libethereum/Client.h

@ -166,7 +166,7 @@ class Client: public MinerHost, public Interface, Worker
public: public:
/// New-style Constructor. /// New-style Constructor.
explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0); explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0, int miners = -1);
/// Destructor. /// Destructor.
virtual ~Client(); virtual ~Client();

3
libsolidity/AST.cpp

@ -403,7 +403,8 @@ void Assignment::checkTypeRequirements()
m_leftHandSide->checkTypeRequirements(); m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue(); m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings //@todo later, assignments to structs might be possible, but not to mappings
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue()) if (m_leftHandSide->getType()->getCategory() != Type::Category::ByteArray &&
!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue.")); BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_type = m_leftHandSide->getType(); m_type = m_leftHandSide->getType();
if (m_assigmentOperator == Token::Assign) if (m_assigmentOperator == Token::Assign)

2
libsolidity/ASTJsonConverter.h

@ -27,7 +27,7 @@
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/Utils.h> #include <libsolidity/Utils.h>
#include <jsoncpp/json/json.h> #include <json/json.h>
namespace dev namespace dev
{ {

7
libsolidity/Compiler.cpp

@ -207,15 +207,10 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
for (TypePointer const& type: _typeParameters) for (TypePointer const& type: _typeParameters)
{ {
unsigned numBytes = type->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, *type); CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true); ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_leftAligned = type->getCategory() == Type::Category::String;
bool const c_padToWords = true; bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords); dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
stackDepth -= type->getSizeOnStack(); stackDepth -= type->getSizeOnStack();
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here

250
libsolidity/CompilerUtils.cpp

@ -62,20 +62,59 @@ unsigned CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _
} }
} }
unsigned CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
bool _padToWordBoundaries)
{ {
if (_bytes == 0) solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type.");
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
return numBytes;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
if (_type.getCategory() == Type::Category::ByteArray)
{ {
m_context << eth::Instruction::POP; auto const& type = dynamic_cast<ByteArrayType const&>(_type);
return 0; solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Non-storage byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart
// load and store
<< eth::Instruction::DUP2 << eth::Instruction::SLOAD
<< eth::Instruction::DUP2 << eth::Instruction::MSTORE
// increment storage_data_offset by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment memory offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
{
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
m_context << u256(numBytes) << eth::Instruction::ADD;
}
} }
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
if (_bytes != 32 && !_leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
return _padToWordBoundaries ? 32 : _bytes;
} }
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@ -114,5 +153,194 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size; return size;
} }
void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries)
{
unsigned length = storeInMemory(0, _type, _padToWordBoundaries);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
}
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
ByteArrayType const& _sourceType) const
{
// stack layout: [source_ref] target_ref (top)
// need to leave target_ref on the stack at the end
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, "");
switch (_sourceType.getLocation())
{
case ByteArrayType::Location::CallData:
{
// @todo this does not take length into account. It also assumes that after "CALLDATALENGTH" we only have zeros.
// fetch old length and convert to words
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
// stack here: target_ref target_length_words
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::DUP2;
CompilerUtils(m_context).computeHashStatic();
// compute target_data_end
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1;
// stack here: target_ref target_data_end target_data_ref
// store length (in bytes)
if (_sourceType.getOffset() == 0)
m_context << eth::Instruction::CALLDATASIZE;
else
m_context << _sourceType.getOffset() << eth::Instruction::CALLDATASIZE << eth::Instruction::SUB;
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP5 << eth::Instruction::SSTORE;
// jump to end if length is zero
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
m_context << _sourceType.getOffset();
// stack now: target_ref target_data_end target_data_ref calldata_offset
eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart
// copy from calldata and store
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE
// increment target_data_ref by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment calldata_offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATASIZE << eth::Instruction::GT;
m_context.appendConditionalJumpTo(copyLoopStart);
m_context << eth::Instruction::POP;
m_context << copyLoopEnd;
// now clear leftover bytes of the old value
// stack now: target_ref target_data_end target_data_ref
clearStorageLoop();
m_context << eth::Instruction::POP;
break;
}
case ByteArrayType::Location::Storage:
{
// this copies source to target and also clears target if it was larger
// stack: source_ref target_ref
// store target_ref
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
// fetch lengthes
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP2
<< eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack: target_ref target_len_bytes target_ref source_ref source_len_bytes
// store new target length
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
// compute hashes (data positions)
m_context << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos
// convert lengthes from bytes to storage slots
m_context << u256(31) << u256(32) << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::DUP8 << eth::Instruction::ADD << eth::Instruction::DIV
<< eth::Instruction::SWAP2
<< eth::Instruction::DUP6 << eth::Instruction::ADD << eth::Instruction::DIV;
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len
// @todo we might be able to go without a third counter
m_context << u256(0);
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
// copy
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SLOAD
<< eth::Instruction::DUP6 << eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SSTORE;
// increment
m_context << u256(1) << eth::Instruction::ADD;
m_context.appendJumpTo(copyLoopStart);
m_context << copyLoopEnd;
// zero-out leftovers in target
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
// add counter to target_data_pos
m_context << eth::Instruction::DUP5 << eth::Instruction::ADD
<< eth::Instruction::SWAP5 << eth::Instruction::POP;
// stack: target_ref target_len_bytes target_data_pos_updated target_data_pos source_data_pos target_len source_len
// add length to target_data_pos to get target_data_end
m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SWAP4
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
// stack: target_ref target_data_end target_data_pos_updated
clearStorageLoop();
m_context << eth::Instruction::POP;
break;
}
default:
solAssert(false, "Given byte array location not implemented.");
}
}
void CompilerUtils::clearByteArray(ByteArrayType const& _type) const
{
solAssert(_type.getLocation() == ByteArrayType::Location::Storage, "");
// fetch length
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// set length to zero
m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
// convert length from bytes to storage slots
m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
// compute data positions
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
// stack: len data_pos
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1;
clearStorageLoop();
// cleanup
m_context << eth::Instruction::POP;
}
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
{
unsigned _encodedSize = _type.getCalldataEncodedSize();
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
bool leftAligned = _type.getCategory() == Type::Category::String;
if (numBytes == 0)
m_context << eth::Instruction::POP;
else
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
}
return numBytes;
}
void CompilerUtils::clearStorageLoop() const
{
// stack: end_pos pos
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(zeroLoopEnd);
// zero out
m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE;
// increment
m_context << u256(1) << eth::Instruction::ADD;
m_context.appendJumpTo(loopStart);
// cleanup
m_context << zeroLoopEnd;
m_context << eth::Instruction::POP;
}
} }
} }

33
libsolidity/CompilerUtils.h

@ -47,13 +47,16 @@ public:
bool _fromCalldata = false, bool _padToWordBoundaries = false); bool _fromCalldata = false, bool _padToWordBoundaries = false);
/// Stores data from stack in memory. /// Stores data from stack in memory.
/// @param _offset offset in memory /// @param _offset offset in memory
/// @param _bytes number of bytes to store /// @param _type type of the data on the stack
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries /// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
/// @returns the number of bytes written to memory (can be different from _bytes if /// @returns the number of bytes written to memory (can be different from _bytes if
/// _padToWordBoundaries is true) /// _padToWordBoundaries is true)
unsigned storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
bool _padToWordBoundaries = false); /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that.
/// Stack pre: memory_offset value...
/// Stack post: (memory_offset+length)
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the /// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
/// padded calldata) /// padded calldata)
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; } static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
@ -69,10 +72,32 @@ public:
static unsigned getSizeOnStack(std::vector<T> const& _variables); static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes); static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
/// Appends code that computes tha SHA3 hash of the topmost stack element of type @a _type.
/// If @a _pad is set, padds the type to muliples of 32 bytes.
/// @note Only works for types of fixed size.
void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Copies a byte array to a byte array in storage.
/// Stack pre: [source_reference] target_reference
/// Stack post: target_reference
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
/// Clears the length and data elements of the byte array referenced on the stack.
/// Stack pre: reference
/// Stack post:
void clearByteArray(ByteArrayType const& _type) const;
/// Bytes we need to the start of call data. /// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier. /// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset; static const unsigned int dataStartOffset;
private: private:
/// Prepares the given type for storing in memory by shifting it if necessary.
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const;
/// Appends a loop that clears a sequence of storage slots (excluding end).
/// Stack pre: end_ref start_ref
/// Stack post: end_ref
void clearStorageLoop() const;
CompilerContext& m_context; CompilerContext& m_context;
}; };

203
libsolidity/ExpressionCompiler.cpp

@ -59,13 +59,15 @@ void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context,
bool ExpressionCompiler::visit(Assignment const& _assignment) bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
_assignment.getRightHandSide().accept(*this); _assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); if (_assignment.getType()->isValueType())
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
_assignment.getLeftHandSide().accept(*this); _assignment.getLeftHandSide().accept(*this);
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
Token::Value op = _assignment.getAssignmentOperator(); Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::Assign) // compound assignment if (op != Token::Assign) // compound assignment
{ {
solAssert(_assignment.getType()->isValueType(), "Compound operators not implemented for non-value types.");
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true); m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true);
@ -73,7 +75,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
} }
m_currentLValue.storeValue(_assignment); m_currentLValue.storeValue(_assignment, *_assignment.getRightHandSide().getType());
m_currentLValue.reset(); m_currentLValue.reset();
return false; return false;
@ -103,7 +105,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
break; break;
case Token::Delete: // delete case Token::Delete: // delete
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation); m_currentLValue.setToZero(_unaryOperation, *_unaryOperation.getSubExpression().getType());
m_currentLValue.reset(); m_currentLValue.reset();
break; break;
case Token::Inc: // ++ (pre- or postfix) case Token::Inc: // ++ (pre- or postfix)
@ -126,7 +128,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// Stack for postfix: *ref [ref] (*ref)+-1 // Stack for postfix: *ref [ref] (*ref)+-1
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation, !_unaryOperation.isPrefixOperation()); m_currentLValue.storeValue(_unaryOperation, *_unaryOperation.getType(), !_unaryOperation.isPrefixOperation());
m_currentLValue.reset(); m_currentLValue.reset();
break; break;
case Token::Add: // + case Token::Add: // +
@ -274,10 +276,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
//@todo copy to memory position 0, shift as soon as we use memory //@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY; m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned length = bytecode.size(); m_context << u256(bytecode.size());
length += appendArgumentsCopyToMemory(arguments, function.getParameterTypes(), length); appendArgumentsCopyToMemory(arguments, function.getParameterTypes());
// size, offset, endowment // size, offset, endowment
m_context << u256(length) << u256(0); m_context << u256(0);
if (function.valueSet()) if (function.valueSet())
m_context << eth::dupInstruction(3); m_context << eth::dupInstruction(3);
else else
@ -327,8 +329,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break; break;
case Location::SHA3: case Location::SHA3:
{ {
unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments()); m_context << u256(0);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments());
m_context << u256(0) << eth::Instruction::SHA3;
break; break;
} }
case Location::Log0: case Location::Log0:
@ -343,23 +346,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[arg]->accept(*this); arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
} }
unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(), m_context << u256(0);
*arguments.front()); appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
solAssert(length == 32, "Log data should be 32 bytes long (for now)."); m_context << u256(0) << eth::logInstruction(logNumber);
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
break; break;
} }
case Location::Event: case Location::Event:
{ {
_functionCall.getExpression().accept(*this); _functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration()); auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
// Copy all non-indexed arguments to memory (data)
unsigned numIndexed = 0; unsigned numIndexed = 0;
unsigned memLength = 0;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg],
*arguments[arg], memLength);
// All indexed arguments go to the stack // All indexed arguments go to the stack
for (unsigned arg = arguments.size(); arg > 0; --arg) for (unsigned arg = arguments.size(); arg > 0; --arg)
if (event.getParameters()[arg - 1]->isIndexed()) if (event.getParameters()[arg - 1]->isIndexed())
@ -372,7 +368,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
++numIndexed; ++numIndexed;
solAssert(numIndexed <= 4, "Too many indexed arguments."); solAssert(numIndexed <= 4, "Too many indexed arguments.");
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed); // Copy all non-indexed arguments to memory (data)
m_context << u256(0);
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]);
m_context << u256(0) << eth::logInstruction(numIndexed);
break; break;
} }
case Location::BlockHash: case Location::BlockHash:
@ -472,6 +473,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::GAS; m_context << eth::Instruction::GAS;
else if (member == "gasprice") else if (member == "gasprice")
m_context << eth::Instruction::GASPRICE; m_context << eth::Instruction::GASPRICE;
else if (member == "data")
{
// nothing to store on the stack
}
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
break; break;
@ -508,12 +513,16 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
_indexAccess.getBaseExpression().accept(*this); _indexAccess.getBaseExpression().accept(*this);
TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(); Type const& baseType = *_indexAccess.getBaseExpression().getType();
unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression()); solAssert(baseType.getCategory() == Type::Category::Mapping, "");
solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
// @todo move this once we actually use memory m_context << u256(0);
length += CompilerUtils(m_context).storeInMemory(length); appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; solAssert(baseType.getSizeOnStack() == 1,
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
m_context << eth::Instruction::SWAP1;
appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType()); m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -804,26 +813,30 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize); unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1); unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
if (!bare) //@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
m_context << u256(retSize) << u256(0);
if (bare)
m_context << u256(0);
else
{ {
// copy function identifier // copy function identifier
m_context << eth::dupInstruction(gasValueSize + 1); m_context << eth::dupInstruction(gasValueSize + 3);
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset); CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8));
m_context << u256(CompilerUtils::dataStartOffset);
} }
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes, // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes. // do not pad it to 32 bytes.
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset, appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(),
_functionType.padArguments(), bare); _functionType.padArguments(), bare);
//@todo only return the first return value for now // CALL arguments: outSize, outOff, inSize, (already present up to here)
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : // inOff, value, addr, gas (stack top)
_functionType.getReturnParameterTypes().front().get(); m_context << u256(0);
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
if (_functionType.valueSet()) if (_functionType.valueSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else else
@ -852,14 +865,12 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
} }
} }
unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments, void ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types, TypePointers const& _types,
unsigned _memoryOffset, bool _padToWordBoundaries,
bool _padToWordBoundaries, bool _padExceptionIfFourBytes)
bool _padExceptionIfFourBytes)
{ {
solAssert(_types.empty() || _types.size() == _arguments.size(), ""); solAssert(_types.empty() || _types.size() == _arguments.size(), "");
unsigned length = 0;
for (size_t i = 0; i < _arguments.size(); ++i) for (size_t i = 0; i < _arguments.size(); ++i)
{ {
_arguments[i]->accept(*this); _arguments[i]->accept(*this);
@ -869,31 +880,20 @@ unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expre
// Do not pad if the first argument has exactly four bytes // Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4) if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
pad = false; pad = false;
length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(), appendTypeMoveToMemory(*expectedType, pad);
_memoryOffset + length, pad);
} }
return length;
} }
unsigned ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, bool _padToWordBoundaries) void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries)
{ {
unsigned const c_encodedSize = _type.getCalldataEncodedSize(); CompilerUtils(m_context).storeInMemoryDynamic(_type, _padToWordBoundaries);
unsigned const c_numBytes = _padToWordBoundaries ? CompilerUtils::getPaddedSize(c_encodedSize) : c_encodedSize;
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_location)
<< errinfo_comment("Type " + _type.toString() + " not yet supported."));
bool const c_leftAligned = _type.getCategory() == Type::Category::String;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, _padToWordBoundaries);
} }
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
Expression const& _expression,
unsigned _memoryOffset)
{ {
_expression.accept(*this); _expression.accept(*this);
appendTypeConversion(*_expression.getType(), _expectedType, true); appendTypeConversion(*_expression.getType(), _expectedType, true);
return appendTypeMoveToMemory(_expectedType, _expression.getLocation(), _memoryOffset); appendTypeMoveToMemory(_expectedType);
} }
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
@ -904,7 +904,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
TypePointers const& paramTypes = accessorType.getParameterTypes(); TypePointers const& paramTypes = accessorType.getParameterTypes();
// move arguments to memory // move arguments to memory
for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes)) for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
length += appendTypeMoveToMemory(*paramType, Location(), length); length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
// retrieve the position of the variable // retrieve the position of the variable
m_context << m_context.getStorageLocationOfVariable(_varDecl); m_context << m_context.getStorageLocationOfVariable(_varDecl);
@ -1014,7 +1014,7 @@ void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _ty
} }
} }
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type const& _sourceType, bool _move) const
{ {
switch (m_type) switch (m_type)
{ {
@ -1032,28 +1032,46 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
if (!_expression.getType()->isValueType()) // stack layout: value value ... value target_ref
break; // no distinction between value and reference for non-value types if (_expression.getType()->isValueType())
// stack layout: value value ... value ref
if (!_move) // copy values
{ {
if (m_size + 1 > 16) if (!_move) // copy values
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) {
<< errinfo_comment("Stack too deep.")); if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
}
if (m_size > 0) // store high index value first
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1; {
if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE;
else
// stack here: value value ... value value (target_ref+offset)
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
} }
if (m_size > 0) // store high index value first else
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i)
{ {
if (i + 1 >= m_size) solAssert(!_move, "Move assign for non-value types not implemented.");
*m_context << eth::Instruction::SSTORE; solAssert(_sourceType.getCategory() == _expression.getType()->getCategory(), "");
if (_expression.getType()->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*_expression.getType()),
dynamic_cast<ByteArrayType const&>(_sourceType));
else if (_expression.getType()->getCategory() == Type::Category::Struct)
{
//@todo
solAssert(false, "Struct copy not yet implemented.");
}
else else
// v v ... v v r+x BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << errinfo_comment("Invalid non-value type for assignment."));
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
} }
break; break;
case LValueType::Memory: case LValueType::Memory:
@ -1069,7 +1087,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
} }
} }
void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type const& _type) const
{ {
switch (m_type) switch (m_type)
{ {
@ -1086,20 +1104,21 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
if (m_size == 0) if (_type.getCategory() == Type::Category::ByteArray)
*m_context << eth::Instruction::POP; CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(_type));
for (unsigned i = 0; i < m_size; ++i) else
{ {
if (i + 1 >= m_size) if (m_size == 0)
*m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; *m_context << eth::Instruction::POP;
else for (unsigned i = 0; i < m_size; ++i)
*m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE if (i + 1 >= m_size)
<< u256(1) << eth::Instruction::ADD; *m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
else
*m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::ADD;
} }
break; break;
case LValueType::Memory: case LValueType::Memory:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented.")); << errinfo_comment("Location type not yet implemented."));
break; break;
@ -1108,7 +1127,6 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
<< errinfo_comment("Unsupported location type.")); << errinfo_comment("Unsupported location type."));
break; break;
} }
} }
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression) void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
@ -1145,5 +1163,6 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
<< errinfo_comment("Identifier type not supported or identifier not found.")); << errinfo_comment("Identifier type not supported or identifier not found."));
} }
} }
} }

34
libsolidity/ExpressionCompiler.h

@ -92,21 +92,18 @@ private:
/// Appends code to call a function of the given type with the given arguments. /// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments, void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
bool bare = false); bool bare = false);
/// Appends code that evaluates the given arguments and moves the result to memory (with optional offset). /// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
/// @returns the number of bytes moved to memory /// expected to be on the stack and is updated by this call.
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments, void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {}, TypePointers const& _types = {},
unsigned _memoryOffset = 0, bool _padToWordBoundaries = true,
bool _padToWordBoundaries = true, bool _padExceptionIfFourBytes = false);
bool _padExceptionIfFourBytes = false); /// Appends code that moves a stack element of the given type to memory. The memory offset is
/// Appends code that moves a stack element of the given type to memory /// expected below the stack element and is updated by this call.
/// @returns the number of bytes moved to memory void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, /// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
bool _padToWordBoundaries = true); /// expected to be on the stack and is updated by this call.
/// Appends code that evaluates a single expression and moves the result to memory (with optional offset). void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
/// @returns the number of bytes moved to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
/// Appends code for a State Variable accessor function /// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl); void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
@ -148,10 +145,11 @@ private:
/// be on the top of the stack, if any) in the lvalue and removes the reference. /// be on the top of the stack, if any) in the lvalue and removes the reference.
/// Also removes the stored value from the stack if @a _move is /// Also removes the stored value from the stack if @a _move is
/// true. @a _expression is the current expression, used for error reporting. /// true. @a _expression is the current expression, used for error reporting.
void storeValue(Expression const& _expression, bool _move = false) const; /// @a _sourceType is the type of the expression that is assigned.
void storeValue(Expression const& _expression, Type const& _sourceType, bool _move = false) const;
/// Stores zero in the lvalue. /// Stores zero in the lvalue.
/// @a _expression is the current expression, used for error reporting. /// @a _expression is the current expression, used for error reporting.
void setToZero(Expression const& _expression) const; void setToZero(Expression const& _expression, Type const& _type) const;
/// Convenience function to convert the stored reference to a value and reset type to NONE if /// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression. /// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression); void retrieveValueIfLValueNotRequested(Expression const& _expression);
@ -159,6 +157,8 @@ private:
private: private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
/// Copies from a byte array to a byte array in storage, both references on the stack.
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
CompilerContext* m_context; CompilerContext* m_context;
LValueType m_type = LValueType::None; LValueType m_type = LValueType::None;

2
libsolidity/InterfaceHandler.h

@ -28,7 +28,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <jsoncpp/json/json.h> #include <json/json.h>
namespace dev namespace dev
{ {

6
libsolidity/Token.h

@ -176,8 +176,7 @@ namespace solidity
K(SubFinney, "finney", 0) \ K(SubFinney, "finney", 0) \
K(SubEther, "ether", 0) \ K(SubEther, "ether", 0) \
/* type keywords, keep them in this order, keep int as first keyword /* type keywords, keep them in this order, keep int as first keyword
* the implementation in Types.cpp has to be synced to this here * the implementation in Types.cpp has to be synced to this here */\
* TODO more to be added */ \
K(Int, "int", 0) \ K(Int, "int", 0) \
K(Int8, "int8", 0) \ K(Int8, "int8", 0) \
K(Int16, "int16", 0) \ K(Int16, "int16", 0) \
@ -279,7 +278,8 @@ namespace solidity
K(Hash256, "hash256", 0) \ K(Hash256, "hash256", 0) \
K(Address, "address", 0) \ K(Address, "address", 0) \
K(Bool, "bool", 0) \ K(Bool, "bool", 0) \
K(StringType, "string", 0) \ K(Bytes, "bytes", 0) \
K(StringType, "string", 0) \
K(String0, "string0", 0) \ K(String0, "string0", 0) \
K(String1, "string1", 0) \ K(String1, "string1", 0) \
K(String2, "string2", 0) \ K(String2, "string2", 0) \

70
libsolidity/Types.cpp

@ -35,7 +35,7 @@ namespace dev
namespace solidity namespace solidity
{ {
shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken) TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{ {
solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
@ -57,12 +57,19 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
return make_shared<BoolType>(); return make_shared<BoolType>();
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0)); return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes)
return make_shared<ByteArrayType>(ByteArrayType::Location::Storage, 0, 0, true);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
} }
shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) TypePointer Type::fromElementaryTypeName(string const& _name)
{
return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}
TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{ {
Declaration const* declaration = _typeName.getReferencedDeclaration(); Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
@ -71,21 +78,21 @@ shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const&
return make_shared<FunctionType>(*function); return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract); return make_shared<ContractType>(*contract);
return shared_ptr<Type const>(); return TypePointer();
} }
shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName) TypePointer Type::fromMapping(Mapping const& _typeName)
{ {
shared_ptr<Type const> keyType = _typeName.getKeyType().toType(); TypePointer keyType = _typeName.getKeyType().toType();
if (!keyType) if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
shared_ptr<Type const> valueType = _typeName.getValueType().toType(); TypePointer valueType = _typeName.getValueType().toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
shared_ptr<Type const> Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
{ {
switch (_literal.getToken()) switch (_literal.getToken())
{ {
@ -506,6 +513,40 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
} }
bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (*this == _convertTo)
return true;
if (_convertTo.getCategory() != Category::ByteArray)
return false;
auto const& other = dynamic_cast<ByteArrayType const&>(_convertTo);
return (m_dynamicLength == other.m_dynamicLength || m_length == other.m_length);
}
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Delete)
return make_shared<VoidType>();
return TypePointer();
}
bool ByteArrayType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other);
return other.m_location == m_location && other.m_dynamicLength == m_dynamicLength
&& other.m_length == m_length && other.m_offset == m_offset;
}
unsigned ByteArrayType::getSizeOnStack() const
{
if (m_location == Location::CallData)
return 0;
else
return 1;
}
bool ContractType::operator==(Type const& _other) const bool ContractType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
@ -525,8 +566,8 @@ MemberList const& ContractType::getMembers() const
if (!m_members) if (!m_members)
{ {
// All address members and all interface functions // All address members and all interface functions
map<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(), map<string, TypePointer> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end()); IntegerType::AddressMemberList.end());
if (m_super) if (m_super)
{ {
for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
@ -581,14 +622,14 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const u256 StructType::getStorageSize() const
{ {
u256 size = 0; u256 size = 0;
for (pair<string, shared_ptr<Type const>> const& member: getMembers()) for (pair<string, TypePointer> const& member: getMembers())
size += member.second->getStorageSize(); size += member.second->getStorageSize();
return max<u256>(1, size); return max<u256>(1, size);
} }
bool StructType::canLiveOutsideStorage() const bool StructType::canLiveOutsideStorage() const
{ {
for (pair<string, shared_ptr<Type const>> const& member: getMembers()) for (pair<string, TypePointer> const& member: getMembers())
if (!member.second->canLiveOutsideStorage()) if (!member.second->canLiveOutsideStorage())
return false; return false;
return true; return true;
@ -604,7 +645,7 @@ MemberList const& StructType::getMembers() const
// We need to lazy-initialize it because of recursive references. // We need to lazy-initialize it because of recursive references.
if (!m_members) if (!m_members)
{ {
map<string, shared_ptr<Type const>> members; map<string, TypePointer> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members[variable->getName()] = variable->getType(); members[variable->getName()] = variable->getType();
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));
@ -811,7 +852,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
TypePointers pointers; TypePointers pointers;
pointers.reserve(_types.size()); pointers.reserve(_types.size());
for (string const& type: _types) for (string const& type: _types)
pointers.push_back(Type::fromElementaryTypeName(Token::fromIdentifierOrKeyword(type))); pointers.push_back(Type::fromElementaryTypeName(type));
return pointers; return pointers;
} }
@ -941,7 +982,8 @@ MagicType::MagicType(MagicType::Kind _kind):
case Kind::Message: case Kind::Message:
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}}); {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}});
break; break;
case Kind::Transaction: case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},

40
libsolidity/Types.h

@ -76,15 +76,17 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public: public:
enum class Category enum class Category
{ {
Integer, IntegerConstant, Bool, Real, Integer, IntegerConstant, Bool, Real, String,
String, Contract, Struct, Function, ByteArray, Mapping,
Mapping, Void, TypeType, Modifier, Magic Contract, Struct, Function,
Void, TypeType, Modifier, Magic
}; };
///@{ ///@{
///@name Factory functions ///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type. /// Factory functions that convert an AST @ref TypeName to a Type.
static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName); static TypePointer fromMapping(Mapping const& _typeName);
static TypePointer fromFunction(FunctionDefinition const& _function); static TypePointer fromFunction(FunctionDefinition const& _function);
@ -263,7 +265,7 @@ class BoolType: public Type
{ {
public: public:
BoolType() {} BoolType() {}
virtual Category getCategory() const { return Category::Bool; } virtual Category getCategory() const override { return Category::Bool; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
@ -275,6 +277,36 @@ public:
virtual u256 literalValue(Literal const* _literal) const override; virtual u256 literalValue(Literal const* _literal) const override;
}; };
/**
* The type of a byte array, prototype for a general array.
*/
class ByteArrayType: public Type
{
public:
enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::ByteArray; }
explicit ByteArrayType(Location _location, u256 const& _offset = 0, u256 const& _length = 0,
bool _dynamicLength = false):
m_location(_location), m_offset(_offset), m_length(_length), m_dynamicLength(_dynamicLength) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(const Type& _other) const override;
virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override { return "bytes"; }
Location getLocation() const { return m_location; }
u256 const& getOffset() const { return m_offset; }
u256 const& getLength() const { return m_length; }
bool hasDynamicLength() const { return m_dynamicLength; }
private:
Location m_location;
u256 m_offset;
u256 m_length;
bool m_dynamicLength;
};
/** /**
* The type of a contract instance, there is one distinct type for each contract definition. * The type of a contract instance, there is one distinct type for each contract definition.
*/ */

6
libwebthree/WebThree.cpp

@ -35,15 +35,15 @@ using namespace dev::p2p;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::shh; using namespace dev::shh;
WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set<std::string> const& _interfaces, NetworkPreferences const& _n, bytesConstRef _network): WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set<std::string> const& _interfaces, NetworkPreferences const& _n, bytesConstRef _network, int miners):
m_clientVersion(_clientVersion), m_clientVersion(_clientVersion),
m_net(_clientVersion, _n, _network) m_net(_clientVersion, _n, _network)
{ {
if (_dbPath.size()) if (_dbPath.size())
Defaults::setDBPath(_dbPath); Defaults::setDBPath(_dbPath);
if (_interfaces.count("eth")) if (_interfaces.count("eth"))
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean)); m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean, 0, miners));
if (_interfaces.count("shh")) if (_interfaces.count("shh"))
m_whisper = m_net.registerCapability<WhisperHost>(new WhisperHost); m_whisper = m_net.registerCapability<WhisperHost>(new WhisperHost);

2
libwebthree/WebThree.h

@ -103,7 +103,7 @@ class WebThreeDirect : public WebThreeNetworkFace
public: public:
/// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception. /// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception.
/// ethereum() may be safely static_cast()ed to a eth::Client*. /// ethereum() may be safely static_cast()ed to a eth::Client*.
WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set<std::string> const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(), bytesConstRef _network = bytesConstRef()); WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set<std::string> const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(), bytesConstRef _network = bytesConstRef(), int miners = -1);
/// Destructor. /// Destructor.
~WebThreeDirect(); ~WebThreeDirect();

1
lllc/CMakeLists.txt

@ -4,6 +4,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..) include_directories(..)
set(EXECUTABLE lllc) set(EXECUTABLE lllc)

1
mix/CMakeLists.txt

@ -10,6 +10,7 @@ endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..) include_directories(..)
find_package (Qt5WebEngine QUIET) find_package (Qt5WebEngine QUIET)

81
neth/main.cpp

@ -81,6 +81,7 @@ void help()
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl << " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl << " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl << " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl
<< " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl << " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (default: 5)." << endl << " -x,--peers <number> Attempt to connect to given number of peers (default: 5)." << endl
@ -307,6 +308,7 @@ int main(int argc, char** argv)
unsigned mining = ~(unsigned)0; unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full; NodeMode mode = NodeMode::Full;
unsigned peers = 5; unsigned peers = 5;
int miners = -1;
#if ETH_JSONRPC #if ETH_JSONRPC
int jsonrpc = 8080; int jsonrpc = 8080;
#endif #endif
@ -366,7 +368,23 @@ int main(int argc, char** argv)
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i]; clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
coinbase = h160(fromHex(argv[++i])); {
try
{
coinbase = h160(fromHex(argv[++i], ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
break;
}
catch (...)
{
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i]))); us = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
@ -401,6 +419,8 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]); g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]); peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if (arg == "-h" || arg == "--help") else if (arg == "-h" || arg == "--help")
help(); help();
else if (arg == "-V" || arg == "--version") else if (arg == "-V" || arg == "--version")
@ -422,7 +442,9 @@ int main(int argc, char** argv)
false, false,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(), mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs, netPrefs,
&nodesState); &nodesState,
miners
);
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -708,9 +730,21 @@ int main(int argc, char** argv)
} }
else else
{ {
Secret secret = h256(fromHex(sechex)); try
Address dest = h160(fromHex(fields[0])); {
c->transact(secret, amount, dest, data, gas, gasPrice); Secret secret = h256(fromHex(sechex, ThrowType::Throw));
Address dest = h160(fromHex(fields[0], ThrowType::Throw));
c->transact(secret, amount, dest, data, gas, gasPrice);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
} }
} }
} }
@ -748,8 +782,20 @@ int main(int argc, char** argv)
auto blockData = bc.block(h); auto blockData = bc.block(h);
BlockInfo info(blockData); BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0); u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(fields[0])); try
c->transact(us.secret(), amount, dest, bytes(), minGas); {
Address dest = h160(fromHex(fields[0], ThrowType::Throw));
c->transact(us.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
} }
} }
} }
@ -802,14 +848,31 @@ int main(int argc, char** argv)
{ {
cnote << "Assembled:"; cnote << "Assembled:";
stringstream ssc; stringstream ssc;
init = fromHex(sinit); try
{
init = fromHex(sinit, ThrowType::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string()); ssc.str(string());
ssc << disassemble(init); ssc << disassemble(init);
cnote << "Init:"; cnote << "Init:";
cnote << ssc.str(); cnote << ssc.str();
} }
u256 minGas = (u256)Client::txGas(init, 0); u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0) if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment"; cwarn << "Invalid endowment";
else if (gas < minGas) else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas; cwarn << "Minimum gas amount is" << minGas;

1
solc/CMakeLists.txt

@ -4,6 +4,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..) include_directories(..)
set(EXECUTABLE solc) set(EXECUTABLE solc)

2
test/SolidityABIJSON.cpp

@ -22,7 +22,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <jsoncpp/json/json.h> #include <json/json.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
namespace dev namespace dev

139
test/SolidityEndToEndTest.cpp

@ -668,7 +668,7 @@ BOOST_AUTO_TEST_CASE(mapping_state)
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// voting without vote right shourd be rejected // voting without vote right should be rejected
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2)); testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
@ -963,7 +963,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors)
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("data()") == encodeArgs(8)); BOOST_CHECK(callContractFunction("data()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina")); BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina"));
BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes{0x7b}))); BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes(1, 0x7b))));
BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337)))); BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337))));
BOOST_CHECK(callContractFunction("super_secret_data()") == bytes()); BOOST_CHECK(callContractFunction("super_secret_data()") == bytes());
} }
@ -2203,7 +2203,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals)
dev::sha3( dev::sha3(
toBigEndian(u256(10)) + toBigEndian(u256(10)) +
bytes{0x0, 0xc} + bytes{0x0, 0xc} +
bytes{0x91}))); bytes(1, 0x91))));
} }
BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals)
@ -2227,7 +2227,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals)
dev::sha3( dev::sha3(
toBigEndian(u256(10)) + toBigEndian(u256(10)) +
bytes{0x0, 0xc} + bytes{0x0, 0xc} +
bytes{0x91} + bytes(1, 0x91) +
bytes{0x66, 0x6f, 0x6f}))); bytes{0x66, 0x6f, 0x6f})));
} }
@ -2254,6 +2254,137 @@ BOOST_AUTO_TEST_CASE(generic_call)
BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2); BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2);
} }
BOOST_AUTO_TEST_CASE(store_bytes)
{
// this test just checks that the copy loop does not mess up the stack
char const* sourceCode = R"(
contract C {
function save() returns (uint r) {
r = 23;
savedData = msg.data;
r = 24;
}
bytes savedData;
}
)";
compileAndRun(sourceCode);
// empty copy loop
BOOST_CHECK(callContractFunction("save()") == encodeArgs(24));
BOOST_CHECK(callContractFunction("save()", "abcdefg") == encodeArgs(24));
}
BOOST_AUTO_TEST_CASE(call_forward_bytes)
{
char const* sourceCode = R"(
contract receiver {
uint public received;
function receive(uint x) { received += x + 1; }
function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData = msg.data; }
function forward() returns (bool) { rec.call(savedData); return true; }
function clear() returns (bool) { delete savedData; return true; }
function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData;
}
)";
compileAndRun(sourceCode, 0, "sender");
BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("forward()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("clear()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
}
BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
{
char const* sourceCode = R"(
contract receiver {
uint public received;
function receive(uint x) { received += x + 1; }
function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData1 = savedData2 = msg.data; }
function forward(bool selector) returns (bool) {
if (selector) { rec.call(savedData1); delete savedData1; }
else { rec.call(savedData2); delete savedData2; }
return true;
}
function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData1;
bytes savedData2;
}
)";
compileAndRun(sourceCode, 0, "sender");
BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("forward(bool)", true) == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward(bool)", false) == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(16));
BOOST_CHECK(callContractFunction("forward(bool)", true) == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
}
BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function() { data = msg.data; }
function del() returns (bool) { delete data; return true; }
bytes data;
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("---", 7) == bytes());
BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
BOOST_CHECK(callContractFunction("del()", 7) == encodeArgs(true));
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function set() returns (bool) { data = msg.data; return true; }
function() { data = msg.data; }
bytes data;
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5) == encodeArgs(true));
BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
sendMessage(bytes(), false);
BOOST_CHECK(m_output == bytes());
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_CASE(copy_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function set() returns (bool) { data1 = msg.data; return true; }
function reset() returns (bool) { data1 = data2; return true; }
bytes data1;
bytes data2;
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5) == encodeArgs(true));
BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
BOOST_CHECK(callContractFunction("reset()") == encodeArgs(true));
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

2
test/SolidityNatspecJSON.cpp

@ -21,7 +21,7 @@
*/ */
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <jsoncpp/json/json.h> #include <json/json.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>

2
test/solidityExecutionFramework.h

@ -139,6 +139,7 @@ private:
return encode(_cppFunction(_arguments...)); return encode(_cppFunction(_arguments...));
} }
protected:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{ {
m_state.addBalance(m_sender, _value); // just in case m_state.addBalance(m_sender, _value); // just in case
@ -171,7 +172,6 @@ private:
m_logs = executive.logs(); m_logs = executive.logs();
} }
protected:
bool m_optimize = false; bool m_optimize = false;
bool m_addStandardSources = false; bool m_addStandardSources = false;
Address m_sender; Address m_sender;

Loading…
Cancel
Save