aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-03-16 01:07:52 +0800
committerGitHub <noreply@github.com>2017-03-16 01:07:52 +0800
commitf0d539ae05739e35336cc9cc8f44bd9798a95c28 (patch)
tree13ef1dea012c7f093122d5e7578dc3c893636e59 /test
parent364da425d3116a4b85863df39a1864340861d71e (diff)
parent59099908c53129b2f5723bd0d5283c4da089e398 (diff)
downloaddexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.tar
dexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.tar.gz
dexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.tar.bz2
dexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.tar.lz
dexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.tar.xz
dexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.tar.zst
dexon-solidity-f0d539ae05739e35336cc9cc8f44bd9798a95c28.zip
Merge pull request #1782 from ethereum/develop
Solidity 0.4.10
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt22
-rw-r--r--test/ExecutionFramework.cpp10
-rw-r--r--test/ExecutionFramework.h2
-rw-r--r--test/RPCSession.cpp120
-rw-r--r--test/RPCSession.h24
-rwxr-xr-xtest/cmdlineTests.sh40
-rw-r--r--test/fuzzer.cpp91
-rw-r--r--test/libdevcore/Checksum.cpp8
-rw-r--r--test/libdevcore/SwarmHash.cpp8
-rw-r--r--test/libsolidity/Assembly.cpp3
-rw-r--r--test/libsolidity/InlineAssembly.cpp196
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp214
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp3
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp363
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp62
-rw-r--r--test/libsolidity/SolidityParser.cpp15
-rw-r--r--test/libsolidity/SolidityScanner.cpp35
17 files changed, 1056 insertions, 160 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 609aaab3..4d56ec9d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -7,23 +7,9 @@ aux_source_directory(libsolidity SRC_LIST)
aux_source_directory(contracts SRC_LIST)
aux_source_directory(liblll SRC_LIST)
-get_filename_component(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
+list(REMOVE_ITEM SRC_LIST "./fuzzer.cpp")
-# search for test names and create ctest tests
-enable_testing()
-foreach(file ${SRC_LIST})
- file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/${file} test_list_raw REGEX "BOOST_.*TEST_(SUITE|CASE)")
- set(TestSuite "DEFAULT")
- foreach(test_raw ${test_list_raw})
- string(REGEX REPLACE ".*TEST_(SUITE|CASE)\\(([^ ,\\)]*).*" "\\1 \\2" test ${test_raw})
- if(test MATCHES "^SUITE .*")
- string(SUBSTRING ${test} 6 -1 TestSuite)
- elseif(test MATCHES "^CASE .*")
- string(SUBSTRING ${test} 5 -1 TestCase)
- add_test(NAME ${TestSuite}/${TestCase} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND test -t ${TestSuite}/${TestCase})
- endif(test MATCHES "^SUITE .*")
- endforeach(test_raw)
-endforeach(file)
+get_filename_component(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
file(GLOB HEADERS "*.h" "*/*.h")
set(EXECUTABLE soltest)
@@ -34,5 +20,5 @@ eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Solidity::lll)
include_directories(BEFORE ..)
target_link_libraries(${EXECUTABLE} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
-enable_testing()
-set(CTEST_OUTPUT_ON_FAILURE TRUE)
+add_executable(solfuzzer fuzzer.cpp)
+target_link_libraries(solfuzzer soljson)
diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp
index ddcd9cb6..f4e5fcef 100644
--- a/test/ExecutionFramework.cpp
+++ b/test/ExecutionFramework.cpp
@@ -82,6 +82,8 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
m_rpc.test_mineBlocks(1);
RPCSession::TransactionReceipt receipt(m_rpc.eth_getTransactionReceipt(txHash));
+ m_blockNumber = u256(receipt.blockNumber);
+
if (_isCreation)
{
m_contractAddress = Address(receipt.contractAddress);
@@ -125,7 +127,13 @@ void ExecutionFramework::sendEther(Address const& _to, u256 const& _value)
size_t ExecutionFramework::currentTimestamp()
{
- auto latestBlock = m_rpc.rpcCall("eth_getBlockByNumber", {"\"latest\"", "false"});
+ auto latestBlock = m_rpc.eth_getBlockByNumber("latest", false);
+ return size_t(u256(latestBlock.get("timestamp", "invalid").asString()));
+}
+
+size_t ExecutionFramework::blockTimestamp(u256 _number)
+{
+ auto latestBlock = m_rpc.eth_getBlockByNumber(toString(_number), false);
return size_t(u256(latestBlock.get("timestamp", "invalid").asString()));
}
diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h
index 733fd56d..76d0fd8c 100644
--- a/test/ExecutionFramework.h
+++ b/test/ExecutionFramework.h
@@ -262,6 +262,7 @@ protected:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0);
void sendEther(Address const& _to, u256 const& _value);
size_t currentTimestamp();
+ size_t blockTimestamp(u256 number);
/// @returns the (potentially newly created) _ith address.
Address account(size_t _i);
@@ -284,6 +285,7 @@ protected:
bool m_showMessages = false;
Address m_sender;
Address m_contractAddress;
+ u256 m_blockNumber;
u256 const m_gasPrice = 100 * szabo;
u256 const m_gas = 100000000;
bytes m_output;
diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp
index 44d21d69..f8b364d1 100644
--- a/test/RPCSession.cpp
+++ b/test/RPCSession.cpp
@@ -16,18 +16,20 @@
The Implementation originally from https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592(v=vs.85).aspx
*/
-/** @file RPCSession.cpp
- * @author Dimtiry Khokhlov <dimitry@ethdev.com>
- * @date 2016
- */
+/// @file RPCSession.cpp
+/// Low-level IPC communication between the test framework and the Ethereum node.
+
+#include "RPCSession.h"
-#include <string>
-#include <stdio.h>
-#include <thread>
#include <libdevcore/CommonData.h>
+
#include <json/reader.h>
#include <json/writer.h>
-#include "RPCSession.h"
+
+#include <string>
+#include <stdio.h>
+#include <thread>
+#include <chrono>
using namespace std;
using namespace dev;
@@ -73,15 +75,13 @@ IPCSocket::IPCSocket(string const& _path): m_path(_path)
if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), sizeof(struct sockaddr_un)) < 0)
BOOST_FAIL("Error connecting to IPC socket: " << _path);
-
- m_fp = fdopen(m_socket, "r");
#endif
}
string IPCSocket::sendRequest(string const& _req)
{
#if defined(_WIN32)
- string returnStr;
+ // Write to the pipe.
DWORD cbWritten;
BOOL fSuccess = WriteFile(
m_socket, // pipe handle
@@ -90,40 +90,44 @@ string IPCSocket::sendRequest(string const& _req)
&cbWritten, // bytes written
NULL); // not overlapped
- if (!fSuccess)
+ if (!fSuccess || (_req.size() != cbWritten))
BOOST_FAIL("WriteFile to pipe failed");
- DWORD cbRead;
- TCHAR chBuf[c_buffsize];
-
// Read from the pipe.
+ DWORD cbRead;
fSuccess = ReadFile(
- m_socket, // pipe handle
- chBuf, // buffer to receive reply
- c_buffsize,// size of buffer
- &cbRead, // number of bytes read
- NULL); // not overlapped
-
- returnStr += chBuf;
+ m_socket, // pipe handle
+ m_readBuf, // buffer to receive reply
+ sizeof(m_readBuf), // size of buffer
+ &cbRead, // number of bytes read
+ NULL); // not overlapped
if (!fSuccess)
BOOST_FAIL("ReadFile from pipe failed");
- cerr << "."; //Output for log activity
- return returnStr;
+ return string(m_readBuf, m_readBuf + cbRead);
#else
- send(m_socket, _req.c_str(), _req.length(), 0);
+ if (send(m_socket, _req.c_str(), _req.length(), 0) != (ssize_t)_req.length())
+ BOOST_FAIL("Writing on IPC failed.");
- char c;
- string response;
- while ((c = fgetc(m_fp)) != EOF)
+ auto start = chrono::steady_clock::now();
+ ssize_t ret;
+ do
{
- if (c != '\n')
- response += c;
- else
- break;
+ ret = recv(m_socket, m_readBuf, sizeof(m_readBuf), 0);
+ // Also consider closed socket an error.
+ if (ret < 0)
+ BOOST_FAIL("Reading on IPC failed.");
}
- return response;
+ while (
+ ret == 0 &&
+ chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start).count() < m_readTimeOutMS
+ );
+
+ if (ret == 0)
+ BOOST_FAIL("Timeout reading on IPC.");
+
+ return string(m_readBuf, m_readBuf + ret);
#endif
}
@@ -139,6 +143,12 @@ string RPCSession::eth_getCode(string const& _address, string const& _blockNumbe
return rpcCall("eth_getCode", { quote(_address), quote(_blockNumber) }).asString();
}
+Json::Value RPCSession::eth_getBlockByNumber(string const& _blockNumber, bool _fullObjects)
+{
+ // NOTE: to_string() converts bool to 0 or 1
+ return rpcCall("eth_getBlockByNumber", { quote(_blockNumber), _fullObjects ? "true" : "false" });
+}
+
RPCSession::TransactionReceipt RPCSession::eth_getTransactionReceipt(string const& _transactionHash)
{
TransactionReceipt receipt;
@@ -146,6 +156,7 @@ RPCSession::TransactionReceipt RPCSession::eth_getTransactionReceipt(string cons
BOOST_REQUIRE(!result.isNull());
receipt.gasUsed = result["gasUsed"].asString();
receipt.contractAddress = result["contractAddress"].asString();
+ receipt.blockNumber = result["blockNumber"].asString();
for (auto const& log: result["logs"])
{
LogEntry entry;
@@ -187,12 +198,17 @@ string RPCSession::eth_getStorageRoot(string const& _address, string const& _blo
void RPCSession::personal_unlockAccount(string const& _address, string const& _password, int _duration)
{
- rpcCall("personal_unlockAccount", { quote(_address), quote(_password), to_string(_duration) });
+ BOOST_REQUIRE_MESSAGE(
+ rpcCall("personal_unlockAccount", { quote(_address), quote(_password), to_string(_duration) }),
+ "Error unlocking account " + _address
+ );
}
string RPCSession::personal_newAccount(string const& _password)
{
- return rpcCall("personal_newAccount", { quote(_password) }).asString();
+ string addr = rpcCall("personal_newAccount", { quote(_password) }).asString();
+ BOOST_TEST_MESSAGE("Created account " + addr);
+ return addr;
}
void RPCSession::test_setChainParams(vector<string> const& _accounts)
@@ -231,40 +247,43 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts)
void RPCSession::test_setChainParams(string const& _config)
{
- rpcCall("test_setChainParams", { _config });
+ BOOST_REQUIRE(rpcCall("test_setChainParams", { _config }) == true);
}
void RPCSession::test_rewindToBlock(size_t _blockNr)
{
- rpcCall("test_rewindToBlock", { to_string(_blockNr) });
+ BOOST_REQUIRE(rpcCall("test_rewindToBlock", { to_string(_blockNr) }) == true);
}
void RPCSession::test_mineBlocks(int _number)
{
u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString()));
- rpcCall("test_mineBlocks", { to_string(_number) }, true);
-
- bool mined = false;
+ BOOST_REQUIRE(rpcCall("test_mineBlocks", { to_string(_number) }, true) == true);
// We auto-calibrate the time it takes to mine the transaction.
// It would be better to go without polling, but that would probably need a change to the test client
+ auto startTime = std::chrono::steady_clock::now();
unsigned sleepTime = m_sleepTime;
- size_t polls = 0;
- for (; polls < 14 && !mined; ++polls)
+ size_t tries = 0;
+ for (; ; ++tries)
{
std::this_thread::sleep_for(chrono::milliseconds(sleepTime));
+ auto endTime = std::chrono::steady_clock::now();
+ unsigned timeSpent = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
+ if (timeSpent > m_maxMiningTime)
+ BOOST_FAIL("Error in test_mineBlocks: block mining timeout!");
if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number)
- mined = true;
+ break;
else
sleepTime *= 2;
}
- if (polls > 1)
+ if (tries > 1)
{
m_successfulMineRuns = 0;
m_sleepTime += 2;
}
- else if (polls == 1)
+ else if (tries == 1)
{
m_successfulMineRuns++;
if (m_successfulMineRuns > 5)
@@ -274,14 +293,11 @@ void RPCSession::test_mineBlocks(int _number)
m_sleepTime--;
}
}
-
- if (!mined)
- BOOST_FAIL("Error in test_mineBlocks: block mining timeout!");
}
void RPCSession::test_modifyTimestamp(size_t _timestamp)
{
- rpcCall("test_modifyTimestamp", { to_string(_timestamp) });
+ BOOST_REQUIRE(rpcCall("test_modifyTimestamp", { to_string(_timestamp) }) == true);
}
Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& _args, bool _canFail)
@@ -297,12 +313,12 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const&
request += "],\"id\":" + to_string(m_rpcSequence) + "}";
++m_rpcSequence;
- //cout << "Request: " << request << endl;
+ // cout << "Request: " << request << endl;
string reply = m_ipcSocket.sendRequest(request);
- //cout << "Reply: " << reply << endl;
+ // cout << "Reply: " << reply << endl;
Json::Value result;
- Json::Reader().parse(reply, result, false);
+ BOOST_REQUIRE(Json::Reader().parse(reply, result, false));
if (result.isMember("error"))
{
diff --git a/test/RPCSession.h b/test/RPCSession.h
index fc166b99..b37cc322 100644
--- a/test/RPCSession.h
+++ b/test/RPCSession.h
@@ -28,14 +28,16 @@
#include <sys/un.h>
#endif
+#include <json/value.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/test/unit_test.hpp>
+
#include <string>
#include <stdio.h>
#include <map>
-#include <json/value.h>
-#include <boost/test/unit_test.hpp>
#if defined(_WIN32)
-const int c_buffsize = 5120000; //because windows pipe is broken and wont work as in examples. use larger buffer limit to receive whole package in one call
class IPCSocket : public boost::noncopyable
{
public:
@@ -47,7 +49,8 @@ public:
private:
std::string m_path;
- HANDLE m_socket;
+ HANDLE m_socket;
+ TCHAR m_readBuf[512000];
};
#else
class IPCSocket: public boost::noncopyable
@@ -55,14 +58,18 @@ class IPCSocket: public boost::noncopyable
public:
IPCSocket(std::string const& _path);
std::string sendRequest(std::string const& _req);
- ~IPCSocket() { close(m_socket); fclose(m_fp); }
+ ~IPCSocket() { close(m_socket); }
std::string const& path() const { return m_path; }
private:
- FILE *m_fp;
+
std::string m_path;
int m_socket;
+ /// Socket read timeout in milliseconds. Needs to be large because the key generation routine
+ /// might take long.
+ unsigned static constexpr m_readTimeOutMS = 15000;
+ char m_readBuf[512000];
};
#endif
@@ -92,11 +99,13 @@ public:
std::string gasUsed;
std::string contractAddress;
std::vector<LogEntry> logEntries;
+ std::string blockNumber;
};
static RPCSession& instance(std::string const& _path);
std::string eth_getCode(std::string const& _address, std::string const& _blockNumber);
+ Json::Value eth_getBlockByNumber(std::string const& _blockNumber, bool _fullObjects);
std::string eth_call(TransactionData const& _td, std::string const& _blockNumber);
TransactionReceipt eth_getTransactionReceipt(std::string const& _transactionHash);
std::string eth_sendTransaction(TransactionData const& _transactionData);
@@ -124,7 +133,8 @@ private:
IPCSocket m_ipcSocket;
size_t m_rpcSequence = 1;
- unsigned m_sleepTime = 10;
+ unsigned m_maxMiningTime = 15000; // 15 seconds
+ unsigned m_sleepTime = 10; // 10 milliseconds
unsigned m_successfulMineRuns = 0;
std::vector<std::string> m_accounts;
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index fc48654a..e2ee6a5e 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -31,7 +31,7 @@ set -e
REPO_ROOT="$(dirname "$0")"/..
SOLC="$REPO_ROOT/build/solc/solc"
- # Compile all files in std and examples.
+# Compile all files in std and examples.
for f in "$REPO_ROOT"/std/*.sol
do
@@ -46,6 +46,38 @@ do
test -z "$output" -a "$failed" -eq 0
done
-# Test library checksum
-echo 'contact C {}' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222
-! echo 'contract C {}' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null
+echo "Testing library checksum..."
+echo '' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222
+! echo '' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null
+
+echo "Testing long library names..."
+echo '' | "$SOLC" --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname:0x90f20564390eAe531E810af625A22f51385Cd222
+
+echo "Testing overwriting files"
+TMPDIR=$(mktemp -d)
+(
+ set -e
+ # First time it works
+ echo 'contract C {} ' | "$SOLC" --bin -o "$TMPDIR/non-existing-stuff-to-create" 2>/dev/null
+ # Second time it fails
+ ! echo 'contract C {} ' | "$SOLC" --bin -o "$TMPDIR/non-existing-stuff-to-create" 2>/dev/null
+ # Unless we force
+ echo 'contract C {} ' | "$SOLC" --overwrite --bin -o "$TMPDIR/non-existing-stuff-to-create" 2>/dev/null
+)
+rm -rf "$TMPDIR"
+
+echo "Testing soljson via the fuzzer..."
+TMPDIR=$(mktemp -d)
+(
+ set -e
+ cd "$REPO_ROOT"
+ REPO_ROOT=$(pwd) # make it absolute
+ cd "$TMPDIR"
+ "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/contracts/* "$REPO_ROOT"/test/libsolidity/*EndToEnd*
+ for f in *.sol
+ do
+ "$REPO_ROOT"/build/test/solfuzzer < "$f"
+ done
+)
+rm -rf "$TMPDIR"
+echo "Done."
diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp
new file mode 100644
index 00000000..410313c5
--- /dev/null
+++ b/test/fuzzer.cpp
@@ -0,0 +1,91 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Executable for use with AFL <http://lcamtuf.coredump.cx/afl>.
+ * Reads a single source from stdin and signals a failure for internal errors.
+ */
+
+#include <json/json.h>
+
+#include <string>
+#include <iostream>
+
+using namespace std;
+
+extern "C"
+{
+extern char const* compileJSON(char const* _input, bool _optimize);
+}
+
+string contains(string const& _haystack, vector<string> const& _needles)
+{
+ for (string const& needle: _needles)
+ if (_haystack.find(needle) != string::npos)
+ return needle;
+ return "";
+}
+
+int main()
+{
+ string input;
+ while (!cin.eof())
+ {
+ string s;
+ getline(cin, s);
+ input += s + '\n';
+ }
+
+ bool optimize = true;
+ string outputString(compileJSON(input.c_str(), optimize));
+ Json::Value outputJson;
+ if (!Json::Reader().parse(outputString, outputJson))
+ {
+ cout << "Compiler produced invalid JSON output." << endl;
+ abort();
+ }
+ if (outputJson.isMember("errors"))
+ {
+ if (!outputJson["errors"].isArray())
+ {
+ cout << "Output JSON has \"errors\" but it is not an array." << endl;
+ abort();
+ }
+ for (Json::Value const& error: outputJson["errors"])
+ {
+ string invalid = contains(error.asString(), vector<string>{
+ "Internal compiler error",
+ "Exception during compilation",
+ "Unknown exception during compilation",
+ "Unknown exception while generating contract data output",
+ "Unknown exception while generating formal method output",
+ "Unknown exception while generating source name output",
+ "Unknown error while generating JSON"
+ });
+ if (!invalid.empty())
+ {
+ cout << "Invalid error: \"" << error.asString() << "\"" << endl;
+ abort();
+ }
+ }
+ }
+ else if (!outputJson.isMember("contracts"))
+ {
+ cout << "Output JSON has neither \"errors\" nor \"contracts\"." << endl;
+ abort();
+ }
+ return 0;
+}
diff --git a/test/libdevcore/Checksum.cpp b/test/libdevcore/Checksum.cpp
index 32260888..17a17d22 100644
--- a/test/libdevcore/Checksum.cpp
+++ b/test/libdevcore/Checksum.cpp
@@ -1,18 +1,18 @@
/*
- This file is part of cpp-ethereum.
+ This file is part of solidity.
- cpp-ethereum is free software: you can redistribute it and/or modify
+ solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- cpp-ethereum is distributed in the hope that it will be useful,
+ solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Unit tests for the address checksum.
diff --git a/test/libdevcore/SwarmHash.cpp b/test/libdevcore/SwarmHash.cpp
index 7f3186ac..1ed1da18 100644
--- a/test/libdevcore/SwarmHash.cpp
+++ b/test/libdevcore/SwarmHash.cpp
@@ -1,18 +1,18 @@
/*
- This file is part of cpp-ethereum.
+ This file is part of solidity.
- cpp-ethereum is free software: you can redistribute it and/or modify
+ solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- cpp-ethereum is distributed in the hope that it will be useful,
+ solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Unit tests for the swarm hash computation routine.
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index 497bfc77..c4ec0d20 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -53,7 +53,8 @@ eth::AssemblyItems compileContract(const string& _sourceCode)
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
BOOST_CHECK(!!sourceUnit);
- NameAndTypeResolver resolver({}, errors);
+ map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
+ NameAndTypeResolver resolver({}, scopes, errors);
solAssert(Error::containsOnlyWarnings(errors), "");
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index cf0343a9..9035599b 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -20,14 +20,19 @@
* Unit tests for inline assembly.
*/
-#include <string>
-#include <memory>
-#include <libevmasm/Assembly.h>
-#include <libsolidity/parsing/Scanner.h>
+#include "../TestHelper.h"
+
#include <libsolidity/inlineasm/AsmStack.h>
+#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST.h>
-#include "../TestHelper.h"
+#include <test/libsolidity/ErrorCheck.h>
+#include <libevmasm/Assembly.h>
+
+#include <boost/optional.hpp>
+
+#include <string>
+#include <memory>
using namespace std;
@@ -41,31 +46,44 @@ namespace test
namespace
{
-bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true)
+boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _assemble = false, bool _allowWarnings = true)
{
assembly::InlineAssemblyStack stack;
+ bool success = false;
try
{
- if (!stack.parse(std::make_shared<Scanner>(CharStream(_source))))
- return false;
- if (_assemble)
- {
+ success = stack.parse(std::make_shared<Scanner>(CharStream(_source)));
+ if (success && _assemble)
stack.assemble();
- if (!stack.errors().empty())
- if (!_allowWarnings || !Error::containsOnlyWarnings(stack.errors()))
- return false;
- }
}
catch (FatalError const&)
{
- if (Error::containsErrorOfType(stack.errors(), Error::Type::ParserError))
- return false;
+ BOOST_FAIL("Fatal error leaked.");
+ success = false;
+ }
+ if (!success)
+ {
+ BOOST_CHECK_EQUAL(stack.errors().size(), 1);
+ return *stack.errors().front();
}
- if (Error::containsErrorOfType(stack.errors(), Error::Type::ParserError))
- return false;
+ else
+ {
+ // If success is true, there might still be an error in the assembly stage.
+ if (_allowWarnings && Error::containsOnlyWarnings(stack.errors()))
+ return {};
+ else if (!stack.errors().empty())
+ {
+ if (!_allowWarnings)
+ BOOST_CHECK_EQUAL(stack.errors().size(), 1);
+ return *stack.errors().front();
+ }
+ }
+ return {};
+}
- BOOST_CHECK(Error::containsOnlyWarnings(stack.errors()));
- return true;
+bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true)
+{
+ return !parseAndReturnFirstError(_source, _assemble, _allowWarnings);
}
bool successAssemble(string const& _source, bool _allowWarnings = true)
@@ -73,11 +91,45 @@ bool successAssemble(string const& _source, bool _allowWarnings = true)
return successParse(_source, true, _allowWarnings);
}
+Error expectError(std::string const& _source, bool _assemble, bool _allowWarnings = false)
+{
+
+ auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings);
+ BOOST_REQUIRE(error);
+ return *error;
}
+void parsePrintCompare(string const& _source)
+{
+ assembly::InlineAssemblyStack stack;
+ BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(_source))));
+ BOOST_REQUIRE(stack.errors().empty());
+ BOOST_CHECK_EQUAL(stack.toString(), _source);
+}
+
+}
+
+#define CHECK_ERROR(text, assemble, typ, substring) \
+do \
+{ \
+ Error err = expectError((text), (assemble), false); \
+ BOOST_CHECK(err.type() == (Error::Type::typ)); \
+ BOOST_CHECK(searchErrorMessage(err, (substring))); \
+} while(0)
+
+#define CHECK_PARSE_ERROR(text, type, substring) \
+CHECK_ERROR(text, false, type, substring)
+
+#define CHECK_ASSEMBLE_ERROR(text, type, substring) \
+CHECK_ERROR(text, true, type, substring)
+
+
BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)
+
+BOOST_AUTO_TEST_SUITE(Parsing)
+
BOOST_AUTO_TEST_CASE(smoke_test)
{
BOOST_CHECK(successParse("{ }"));
@@ -148,6 +200,85 @@ BOOST_AUTO_TEST_CASE(blocks)
BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }"));
}
+BOOST_AUTO_TEST_CASE(function_definitions)
+{
+ BOOST_CHECK(successParse("{ function f() { } function g(a) -> (x) { } }"));
+}
+
+BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
+{
+ BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> (x, y) { } }"));
+}
+
+BOOST_AUTO_TEST_CASE(function_calls)
+{
+ BOOST_CHECK(successParse("{ g(1, 2, f(mul(2, 3))) x() }"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(Printing)
+
+BOOST_AUTO_TEST_CASE(print_smoke)
+{
+ parsePrintCompare("{\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_instructions)
+{
+ parsePrintCompare("{\n 7\n 8\n mul\n dup10\n add\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_subblock)
+{
+ parsePrintCompare("{\n {\n dup4\n add\n }\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_functional)
+{
+ parsePrintCompare("{\n mul(sload(0x12), 7)\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_label)
+{
+ parsePrintCompare("{\n loop:\n jump(loop)\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_assignments)
+{
+ parsePrintCompare("{\n let x := mul(2, 3)\n 7\n =: x\n x := add(1, 2)\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_string_literals)
+{
+ parsePrintCompare("{\n \"\\n'\\xab\\x95\\\"\"\n}");
+}
+
+BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
+{
+ string source = "{ \"\\u1bac\" }";
+ string parsed = "{\n \"\\xe1\\xae\\xac\"\n}";
+ assembly::InlineAssemblyStack stack;
+ BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(source))));
+ BOOST_REQUIRE(stack.errors().empty());
+ BOOST_CHECK_EQUAL(stack.toString(), parsed);
+ parsePrintCompare(parsed);
+}
+
+BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
+{
+ parsePrintCompare("{\n function f(a, d)\n {\n mstore(a, d)\n }\n function g(a, d) -> (x, y)\n {\n }\n}");
+}
+
+BOOST_AUTO_TEST_CASE(function_calls)
+{
+ parsePrintCompare("{\n g(1, mul(2, x), f(mul(2, 3)))\n x()\n}");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(Analysis)
+
BOOST_AUTO_TEST_CASE(string_literals)
{
BOOST_CHECK(successAssemble("{ let x := \"12345678901234567890123456789012\" }"));
@@ -155,7 +286,7 @@ BOOST_AUTO_TEST_CASE(string_literals)
BOOST_AUTO_TEST_CASE(oversize_string_literals)
{
- BOOST_CHECK(!successAssemble("{ let x := \"123456789012345678901234567890123\" }"));
+ CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
}
BOOST_AUTO_TEST_CASE(assignment_after_tag)
@@ -165,15 +296,16 @@ BOOST_AUTO_TEST_CASE(assignment_after_tag)
BOOST_AUTO_TEST_CASE(magic_variables)
{
- BOOST_CHECK(!successAssemble("{ this }"));
- BOOST_CHECK(!successAssemble("{ ecrecover }"));
+ CHECK_ASSEMBLE_ERROR("{ this pop }", DeclarationError, "Identifier not found or not unique");
+ CHECK_ASSEMBLE_ERROR("{ ecrecover pop }", DeclarationError, "Identifier not found or not unique");
BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover }"));
}
BOOST_AUTO_TEST_CASE(imbalanced_stack)
{
BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
- BOOST_CHECK(!successAssemble("{ 1 }", false));
+ CHECK_ASSEMBLE_ERROR("{ 1 }", Warning, "Inline assembly block is not balanced. It leaves");
+ CHECK_ASSEMBLE_ERROR("{ pop }", Warning, "Inline assembly block is not balanced. It takes");
BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
}
@@ -189,22 +321,26 @@ BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
{
- // Error message: "Cannot use instruction names for identifier names."
- BOOST_CHECK(!successAssemble("{ let gas := 1 }"));
+ CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use instruction names for identifier names.");
}
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
{
- // Error message: "Identifier expected, got instruction name."
- BOOST_CHECK(!successAssemble("{ 2 =: gas }"));
+ CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got instruction name.");
}
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
{
- // Error message: "Cannot use instruction names for identifier names."
- BOOST_CHECK(!successAssemble("{ gas := 2 }"));
+ CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Label name / variable name must precede \":\"");
}
+BOOST_AUTO_TEST_CASE(revert)
+{
+ BOOST_CHECK(successAssemble("{ revert(0, 0) }"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 4075a016..7ef34383 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -1482,9 +1482,15 @@ BOOST_AUTO_TEST_CASE(now)
}
}
)";
- m_rpc.test_modifyTimestamp(0x776347e2);
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true, 0x776347e3));
+ u256 startBlock = m_blockNumber;
+ size_t startTime = blockTimestamp(startBlock);
+ auto ret = callContractFunction("someInfo()");
+ u256 endBlock = m_blockNumber;
+ size_t endTime = blockTimestamp(endBlock);
+ BOOST_CHECK(startBlock != endBlock);
+ BOOST_CHECK(startTime != endTime);
+ BOOST_CHECK(ret == encodeArgs(true, endTime));
}
BOOST_AUTO_TEST_CASE(type_conversions_cleanup)
@@ -1675,6 +1681,42 @@ BOOST_AUTO_TEST_CASE(send_ether)
BOOST_CHECK_EQUAL(balanceAt(address), amount);
}
+BOOST_AUTO_TEST_CASE(transfer_ether)
+{
+ char const* sourceCode = R"(
+ contract A {
+ function A() payable {}
+ function a(address addr, uint amount) returns (uint) {
+ addr.transfer(amount);
+ return this.balance;
+ }
+ function b(address addr, uint amount) {
+ addr.transfer(amount);
+ }
+ }
+
+ contract B {
+ }
+
+ contract C {
+ function () payable {
+ throw;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "B");
+ u160 const nonPayableRecipient = m_contractAddress;
+ compileAndRun(sourceCode, 0, "C");
+ u160 const oogRecipient = m_contractAddress;
+ compileAndRun(sourceCode, 20, "A");
+ u160 payableRecipient(23);
+ BOOST_CHECK(callContractFunction("a(address,uint256)", payableRecipient, 10) == encodeArgs(10));
+ BOOST_CHECK_EQUAL(balanceAt(payableRecipient), 10);
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10);
+ BOOST_CHECK(callContractFunction("b(address,uint256)", nonPayableRecipient, 10) == encodeArgs());
+ BOOST_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10) == encodeArgs());
+}
+
BOOST_AUTO_TEST_CASE(log0)
{
char const* sourceCode = R"(
@@ -4500,7 +4542,6 @@ BOOST_AUTO_TEST_CASE(simple_constant_variables_test)
BOOST_AUTO_TEST_CASE(constant_variables)
{
- //for now constant specifier is valid only for uint, bytesXX, string and enums
char const* sourceCode = R"(
contract Foo {
uint constant x = 56;
@@ -4511,6 +4552,58 @@ BOOST_AUTO_TEST_CASE(constant_variables)
compileAndRun(sourceCode);
}
+BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_expression)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint constant x = 0x123 + 0x456;
+ function f() returns (uint) { return x + 1; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(0x123 + 0x456 + 1));
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak)
+{
+ char const* sourceCode = R"(
+ contract C {
+ bytes32 constant x = keccak256("abc");
+ function f() returns (bytes32) { return x; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(dev::keccak256("abc")));
+}
+
+// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented
+//BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars)
+//{
+// char const* sourceCode = R"(
+// contract C {
+// uint[3] constant x = [uint(1), 2, 3];
+// uint constant y = x[0] + x[1] + x[2];
+// function f() returns (uint) { return y; }
+// }
+// )";
+// compileAndRun(sourceCode);
+// BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 + 2 + 3));
+//}
+
+// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented
+//BOOST_AUTO_TEST_CASE(constant_struct)
+//{
+// char const* sourceCode = R"(
+// contract C {
+// struct S { uint x; uint[] y; }
+// S constant x = S(5, new uint[](4));
+// function f() returns (uint) { return x.x; }
+// }
+// )";
+// compileAndRun(sourceCode);
+// BOOST_CHECK(callContractFunction("f()") == encodeArgs(5));
+//}
+
BOOST_AUTO_TEST_CASE(packed_storage_structs_uint)
{
char const* sourceCode = R"(
@@ -7218,6 +7311,20 @@ BOOST_AUTO_TEST_CASE(inline_array_return)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(1, 2, 3, 4, 5));
}
+BOOST_AUTO_TEST_CASE(inline_array_singleton)
+{
+ // This caused a failure since the type was not converted to its mobile type.
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ return [4][0];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(4)));
+}
+
BOOST_AUTO_TEST_CASE(inline_long_string_return)
{
char const* sourceCode = R"(
@@ -8462,6 +8569,25 @@ BOOST_AUTO_TEST_CASE(function_array_cross_calls)
BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(5), u256(6), u256(7)));
}
+BOOST_AUTO_TEST_CASE(external_function_to_address)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (bool) {
+ return address(this.f) == address(this);
+ }
+ function g(function() external cb) returns (address) {
+ return address(cb);
+ }
+ }
+ )";
+
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("g(function)", fromHex("00000000000000000000000000000000000004226121ff00000000000000000")) == encodeArgs(u160(0x42)));
+}
+
+
BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage)
{
char const* sourceCode = R"(
@@ -9058,6 +9184,88 @@ BOOST_AUTO_TEST_CASE(invalid_instruction)
BOOST_CHECK(callContractFunction("f()") == encodeArgs());
}
+BOOST_AUTO_TEST_CASE(assert_require)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() {
+ assert(false);
+ }
+ function g(bool val) returns (bool) {
+ assert(val == true);
+ return true;
+ }
+ function h(bool val) returns (bool) {
+ require(val);
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs());
+ BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("h(bool)", false) == encodeArgs());
+ BOOST_CHECK(callContractFunction("h(bool)", true) == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(revert)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint public a = 42;
+ function f() {
+ a = 1;
+ revert();
+ }
+ function g() {
+ a = 1;
+ assembly {
+ revert(0, 0)
+ }
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(42)));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(42)));
+}
+
+BOOST_AUTO_TEST_CASE(scientific_notation)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ return 2e10 wei;
+ }
+ function g() returns (uint) {
+ return 200e-2 wei;
+ }
+ function h() returns (uint) {
+ return 2.5e1;
+ }
+ function i() returns (int) {
+ return -2e10;
+ }
+ function j() returns (int) {
+ return -200e-2;
+ }
+ function k() returns (int) {
+ return -2.5e1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(20000000000)));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(2)));
+ BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(25)));
+ BOOST_CHECK(callContractFunction("i()") == encodeArgs(u256(-20000000000)));
+ BOOST_CHECK(callContractFunction("j()") == encodeArgs(u256(-2)));
+ BOOST_CHECK(callContractFunction("k()") == encodeArgs(u256(-25)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
index a769776e..3116aea8 100644
--- a/test/libsolidity/SolidityExpressionCompiler.cpp
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -114,7 +114,8 @@ bytes compileFirstExpression(
declarations.push_back(variable.get());
ErrorList errors;
- NameAndTypeResolver resolver(declarations, errors);
+ map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
+ NameAndTypeResolver resolver(declarations, scopes, errors);
resolver.registerDeclarations(*sourceUnit);
vector<ContractDefinition const*> inheritanceHierarchy;
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 472067ec..fa310434 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -20,19 +20,23 @@
* Unit tests for the name and type resolution of the solidity parser.
*/
-#include <string>
+#include <test/libsolidity/ErrorCheck.h>
+
+#include <test/TestHelper.h>
-#include <libdevcore/SHA3.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/StaticAnalyzer.h>
+#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/TypeChecker.h>
-#include "../TestHelper.h"
-#include "ErrorCheck.h"
+
+#include <libdevcore/SHA3.h>
+
+#include <string>
using namespace std;
@@ -66,7 +70,8 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false,
return make_pair(sourceUnit, errors.at(0));
std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>();
- NameAndTypeResolver resolver(globalContext->declarations(), errors);
+ map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
+ NameAndTypeResolver resolver(globalContext->declarations(), scopes, errors);
solAssert(Error::containsOnlyWarnings(errors), "");
resolver.registerDeclarations(*sourceUnit);
@@ -92,10 +97,11 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false,
BOOST_CHECK(success || !errors.empty());
}
if (success)
- {
- StaticAnalyzer staticAnalyzer(errors);
- staticAnalyzer.analyze(*sourceUnit);
- }
+ if (!PostTypeChecker(errors).check(*sourceUnit))
+ success = false;
+ if (success)
+ if (!StaticAnalyzer(errors).analyze(*sourceUnit))
+ success = false;
if (errors.size() > 1 && !_allowMultipleErrors)
BOOST_FAIL("Multiple errors found");
for (auto const& currentError: errors)
@@ -1080,7 +1086,7 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value)
modifier mod(uint a) { _; return 7; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Return arguments not allowed.");
}
BOOST_AUTO_TEST_CASE(state_variable_accessors)
@@ -1657,6 +1663,36 @@ BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big)
CHECK_ERROR(sourceCode, TypeError, "");
}
+BOOST_AUTO_TEST_CASE(exp_warn_literal_base)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) {
+ uint8 x = 100;
+ return 10**x;
+ }
+ }
+ )";
+ CHECK_WARNING(sourceCode, "might overflow");
+ sourceCode = R"(
+ contract test {
+ function f() returns(uint d) {
+ uint8 x = 100;
+ return uint8(10)**x;
+ }
+ }
+ )";
+ CHECK_SUCCESS(sourceCode);
+ sourceCode = R"(
+ contract test {
+ function f() returns(uint d) {
+ return 2**80;
+ }
+ }
+ )";
+ CHECK_SUCCESS(sourceCode);
+}
+
BOOST_AUTO_TEST_CASE(enum_member_access)
{
char const* text = R"(
@@ -1919,6 +1955,16 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
CHECK_ERROR(text, TypeError, "");
}
+BOOST_AUTO_TEST_CASE(array_with_negative_length)
+{
+ char const* text = R"(
+ contract c {
+ function f(uint a) { uint8[-1] x; }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Array with negative length specified");
+}
+
BOOST_AUTO_TEST_CASE(array_copy_with_different_types1)
{
char const* text = R"(
@@ -2127,16 +2173,94 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable)
CHECK_ERROR(text, TypeError, "");
}
-BOOST_AUTO_TEST_CASE(complex_const_variable)
+BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable)
{
- //for now constant specifier is valid only for uint bytesXX and enums
char const* text = R"(
- contract Foo {
- mapping(uint => bool) x;
- mapping(uint => bool) constant mapVar = x;
+ contract C {
+ address constant x = msg.sender;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ // Change to TypeError for 0.5.0.
+ CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant.");
+}
+
+BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment)
+{
+ char const* text = R"(
+ contract Test {
+ string constant x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca";
+ function f() {
+ x[0] = "f";
+ }
+ }
+ )";
+
+ // Even if this is made possible in the future, we should not allow assignment
+ // to elements of constant arrays.
+ CHECK_ERROR(text, TypeError, "Index access for string is not possible.");
+}
+
+BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant)
+{
+ char const* text = R"(
+ contract C {
+ function () constant returns (uint) x;
+ uint constant y = x();
+ }
+ )";
+ // Change to TypeError for 0.5.0.
+ CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant.");
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_conversion)
+{
+ char const* text = R"(
+ contract C {
+ C constant x = C(0x123);
+ }
+ )";
+ CHECK_SUCCESS(text);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_expression)
+{
+ char const* text = R"(
+ contract C {
+ uint constant x = 0x123 + 0x456;
+ }
+ )";
+ CHECK_SUCCESS(text);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak)
+{
+ char const* text = R"(
+ contract C {
+ bytes32 constant x = keccak256("abc");
+ }
+ )";
+ CHECK_SUCCESS(text);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars)
+{
+ char const* text = R"(
+ contract C {
+ uint[3] constant x = [uint(1), 2, 3];
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "implemented");
+}
+
+BOOST_AUTO_TEST_CASE(constant_struct)
+{
+ char const* text = R"(
+ contract C {
+ struct S { uint x; uint[] y; }
+ S constant x = S(5, new uint[](4));
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "implemented");
}
BOOST_AUTO_TEST_CASE(uninitialized_const_variable)
@@ -2490,6 +2614,30 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable)
CHECK_ERROR(sourceCode, TypeError, "");
}
+BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() {
+ mapping(uint => uint) x;
+ }
+ }
+ )";
+ CHECK_ERROR(sourceCode, TypeError, "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable");
+}
+
+BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() {
+ mapping(uint => uint)[] x;
+ }
+ }
+ )";
+ CHECK_WARNING(sourceCode, "Uninitialized storage pointer");
+}
+
BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers)
{
char const* sourceCode = R"(
@@ -2642,18 +2790,6 @@ BOOST_AUTO_TEST_CASE(literal_strings)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp)
-{
- char const* text = R"(
- contract Foo {
- function f() {
- var x = 1e2;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "");
-}
-
BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
{
char const* text = R"(
@@ -2939,6 +3075,31 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6)
CHECK_ERROR(text, TypeError, "");
}
+BOOST_AUTO_TEST_CASE(tuple_assignment_from_void_function)
+{
+ char const* text = R"(
+ contract C {
+ function f() { }
+ function g() {
+ var (x,) = (f(), f());
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Cannot declare variable with void (empty tuple) type.");
+}
+
+BOOST_AUTO_TEST_CASE(tuple_compound_assignment)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (uint a, uint b) {
+ (a, b) += (1, 1);
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Compound assignment is not allowed for tuple types.");
+}
+
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
{
char const* text = R"(
@@ -4247,7 +4408,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_send)
}
}
)";
- CHECK_WARNING(text, "Return value of low-level calls not used");
+ CHECK_WARNING(text, "Failure condition of 'send' ignored. Consider using 'transfer' instead.");
}
BOOST_AUTO_TEST_CASE(unused_return_value_call)
@@ -4725,16 +4886,57 @@ BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid)
CHECK_ERROR(text, TypeError, "");
}
-BOOST_AUTO_TEST_CASE(invalid_fixed_point_literal)
+BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter)
{
+ // This is a test that checks that the type of the `bytes` parameter is
+ // correctly changed from its own type `bytes calldata` to `bytes memory`
+ // when converting to a function type.
char const* text = R"(
- contract A {
- function a() {
- .8E0;
+ contract C {
+ function f(function(bytes memory x) external g) { }
+ function callback(bytes x) external {}
+ function g() {
+ f(this.callback);
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_SUCCESS(text);
+}
+
+BOOST_AUTO_TEST_CASE(external_function_type_to_address)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (address) {
+ return address(this.f);
+ }
+ }
+ )";
+ CHECK_SUCCESS(text);
+}
+
+BOOST_AUTO_TEST_CASE(internal_function_type_to_address)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (address) {
+ return address(f);
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed");
+}
+
+BOOST_AUTO_TEST_CASE(external_function_type_to_uint)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (uint) {
+ return uint(this.f);
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed");
}
BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue)
@@ -4805,6 +5007,19 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack)
CHECK_WARNING(text, "Inline assembly block is not balanced");
}
+BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load)
+{
+ char const* text = R"(
+ contract c {
+ uint8 x;
+ function f() {
+ assembly { x pop }
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Inline assembly block is not balanced");
+}
+
BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier)
{
char const* text = R"(
@@ -4970,6 +5185,24 @@ BOOST_AUTO_TEST_CASE(inconstructible_internal_constructor)
CHECK_ERROR(text, TypeError, "Contract with internal constructor cannot be created directly.");
}
+BOOST_AUTO_TEST_CASE(inconstructible_internal_constructor_inverted)
+{
+ // Previously, the type information for A was not yet available at the point of
+ // "new A".
+ char const* text = R"(
+ contract B {
+ A a;
+ function B() {
+ a = new A(this);
+ }
+ }
+ contract A {
+ function A(address a) internal {}
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Contract with internal constructor cannot be created directly.");
+}
+
BOOST_AUTO_TEST_CASE(constructible_internal_constructor)
{
char const* text = R"(
@@ -5032,6 +5265,68 @@ BOOST_AUTO_TEST_CASE(invalid_address_length)
CHECK_WARNING(text, "checksum");
}
+BOOST_AUTO_TEST_CASE(early_exit_on_fatal_errors)
+{
+ // This tests a crash that occured because we did not stop for fatal errors.
+ char const* text = R"(
+ contract C {
+ struct S {
+ ftring a;
+ }
+ S public s;
+ function s() s {
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Identifier not found or not unique");
+}
+
+BOOST_AUTO_TEST_CASE(address_methods)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ address addr;
+ uint balance = addr.balance;
+ bool callRet = addr.call();
+ bool callcodeRet = addr.callcode();
+ bool delegatecallRet = addr.delegatecall();
+ bool sendRet = addr.send(1);
+ addr.transfer(1);
+ }
+ }
+ )";
+ CHECK_SUCCESS(text);
+}
+
+BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants)
+{
+ char const* text = R"(
+ contract C {
+ uint constant a = a;
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "cyclic dependency via a");
+ text = R"(
+ contract C {
+ uint constant a = b * c;
+ uint constant b = 7;
+ uint constant c = b + uint(sha3(d));
+ uint constant d = 2 + a;
+ }
+ )";
+ CHECK_ERROR_ALLOW_MULTI(text, TypeError, "a has a cyclic dependency via c");
+ text = R"(
+ contract C {
+ uint constant a = b * c;
+ uint constant b = 7;
+ uint constant c = 4 + uint(sha3(d));
+ uint constant d = 2 + b;
+ }
+ )";
+ CHECK_SUCCESS(text);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
index 2e2e0c6c..bcf6cd49 100644
--- a/test/libsolidity/SolidityOptimizer.cpp
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -31,6 +31,7 @@
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
+#include <chrono>
#include <string>
#include <tuple>
#include <memory>
@@ -1234,6 +1235,67 @@ BOOST_AUTO_TEST_CASE(computing_constants)
) == optimizedBytecode.cend());
}
+
+BOOST_AUTO_TEST_CASE(constant_optimization_early_exit)
+{
+ // This tests that the constant optimizer does not try to find the best representation
+ // indefinitely but instead stops after some number of iterations.
+ char const* sourceCode = R"(
+ pragma solidity ^0.4.0;
+
+ contract HexEncoding {
+ function hexEncodeTest(address addr) returns (bytes32 ret) {
+ uint x = uint(addr) / 2**32;
+
+ // Nibble interleave
+ x = x & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
+ x = (x | (x * 2**64)) & 0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff;
+ x = (x | (x * 2**32)) & 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff;
+ x = (x | (x * 2**16)) & 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff;
+ x = (x | (x * 2** 8)) & 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff;
+ x = (x | (x * 2** 4)) & 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
+
+ // Hex encode
+ uint h = (x & 0x0808080808080808080808080808080808080808080808080808080808080808) / 8;
+ uint i = (x & 0x0404040404040404040404040404040404040404040404040404040404040404) / 4;
+ uint j = (x & 0x0202020202020202020202020202020202020202020202020202020202020202) / 2;
+ x = x + (h & (i | j)) * 0x27 + 0x3030303030303030303030303030303030303030303030303030303030303030;
+
+ // Store and load next batch
+ assembly {
+ mstore(0, x)
+ }
+ x = uint(addr) * 2**96;
+
+ // Nibble interleave
+ x = x & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
+ x = (x | (x * 2**64)) & 0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff;
+ x = (x | (x * 2**32)) & 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff;
+ x = (x | (x * 2**16)) & 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff;
+ x = (x | (x * 2** 8)) & 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff;
+ x = (x | (x * 2** 4)) & 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
+
+ // Hex encode
+ h = (x & 0x0808080808080808080808080808080808080808080808080808080808080808) / 8;
+ i = (x & 0x0404040404040404040404040404040404040404040404040404040404040404) / 4;
+ j = (x & 0x0202020202020202020202020202020202020202020202020202020202020202) / 2;
+ x = x + (h & (i | j)) * 0x27 + 0x3030303030303030303030303030303030303030303030303030303030303030;
+
+ // Store and hash
+ assembly {
+ mstore(32, x)
+ ret := sha3(0, 40)
+ }
+ }
+ }
+ )";
+ auto start = std::chrono::steady_clock::now();
+ compileBothVersions(sourceCode);
+ double duration = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
+ BOOST_CHECK_MESSAGE(duration < 20, "Compilation of constants took longer than 20 seconds.");
+ compareVersions("hexEncodeTest(address)", u256(0x123456789));
+}
+
BOOST_AUTO_TEST_CASE(inconsistency)
{
// This is a test of a bug in the optimizer.
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index e5362e78..ffb4b6f2 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -1479,6 +1479,21 @@ BOOST_AUTO_TEST_CASE(function_type_state_variable)
BOOST_CHECK(successParse(text));
}
+BOOST_AUTO_TEST_CASE(scientific_notation)
+{
+ char const* text = R"(
+ contract test {
+ uint256 a = 2e10;
+ uint256 b = 2E10;
+ uint256 c = 200e-2;
+ uint256 d = 2E10 wei;
+ uint256 e = 2.5e10;
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp
index eb2f042c..020bce7f 100644
--- a/test/libsolidity/SolidityScanner.cpp
+++ b/test/libsolidity/SolidityScanner.cpp
@@ -97,9 +97,39 @@ BOOST_AUTO_TEST_CASE(hex_numbers)
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
+BOOST_AUTO_TEST_CASE(octal_numbers)
+{
+ Scanner scanner(CharStream("07"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal);
+ scanner.reset(CharStream("007"), "");
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal);
+ scanner.reset(CharStream("-07"), "");
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
+ scanner.reset(CharStream("-.07"), "");
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ scanner.reset(CharStream("0"), "");
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number);
+ scanner.reset(CharStream("0.1"), "");
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number);
+}
+
+BOOST_AUTO_TEST_CASE(scientific_notation)
+{
+ Scanner scanner(CharStream("var x = 2e10;"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "2e10");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+}
+
BOOST_AUTO_TEST_CASE(negative_numbers)
{
- Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9;"));
+ Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;"));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
@@ -117,6 +147,9 @@ BOOST_AUTO_TEST_CASE(negative_numbers)
BOOST_CHECK_EQUAL(scanner.next(), Token::Add);
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "8.9");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Add);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "2e-2");
BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}