diff options
-rw-r--r-- | liblll/CodeFragment.cpp | 38 | ||||
-rw-r--r-- | test/liblll/Compiler.cpp | 128 | ||||
-rw-r--r-- | test/liblll/EndToEndTest.cpp | 86 |
3 files changed, 252 insertions, 0 deletions
diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 5e60f964..4fa2c646 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -514,6 +514,44 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.appendJump(begin); m_asm << end.tag(); } + else if (us == "SWITCH") + { + requireMinSize(1); + + bool hasDefault = (code.size() % 2 == 1); + int startDeposit = m_asm.deposit(); + int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0; + + // The conditions + AssemblyItems jumpTags; + for (unsigned i = 0; i < code.size() - 1; i += 2) + { + requireDeposit(i, 1); + m_asm.append(code[i].m_asm); + jumpTags.push_back(m_asm.appendJumpI()); + } + + // The default, if present + if (hasDefault) + m_asm.append(code[code.size() - 1].m_asm); + + // The targets - appending in reverse makes the top case the most efficient. + if (code.size() > 1) + { + auto end = m_asm.appendJump(); + for (int i = 2 * (code.size() / 2 - 1); i >= 0; i -= 2) + { + m_asm << jumpTags[i / 2].tag(); + requireDeposit(i + 1, targetDeposit); + m_asm.append(code[i + 1].m_asm); + if (i != 0) + m_asm.appendJump(end); + } + m_asm << end.tag(); + } + + m_asm.setDeposit(startDeposit + targetDeposit); + } else if (us == "ALLOC") { requireSize(1); diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp new file mode 100644 index 00000000..2d66bce1 --- /dev/null +++ b/test/liblll/Compiler.cpp @@ -0,0 +1,128 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Alex Beregszaszi + * @date 2017 + * Unit tests for the LLL compiler. + */ + +#include <string> +#include <memory> +#include <boost/test/unit_test.hpp> +#include <liblll/Compiler.h> + +using namespace std; + +namespace dev +{ +namespace lll +{ +namespace test +{ + +namespace +{ + +bool successCompile(std::string const& _sourceCode) +{ + std::vector<std::string> errors; + bytes bytecode = eth::compileLLL(_sourceCode, false, &errors); + if (!errors.empty()) + return false; + if (bytecode.empty()) + return false; + return true; +} + +} + +BOOST_AUTO_TEST_SUITE(LLLCompiler) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* sourceCode = "1"; + BOOST_CHECK(successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_valid) +{ + char const* sourceCode = R"( + (switch (origin)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (panic) + 2 (panic)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (panic) + 2 (panic) + (panic)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + (origin)) + )"; + BOOST_CHECK(successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_arg_count) +{ + char const* sourceCode = R"( + (switch) + )"; + BOOST_CHECK(!successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_inconsistent_return_count) +{ + // cannot return stack items if the default case is not present + char const* sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + )"; + BOOST_CHECK(!successCompile(sourceCode)); + // return count mismatch + sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + (panic)) + )"; + BOOST_CHECK(!successCompile(sourceCode)); + // return count mismatch + sourceCode = R"( + (switch + 1 (panic) + 2 (panic) + (origin)) + )"; + BOOST_CHECK(!successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 9292d963..1a5bb490 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -215,6 +215,92 @@ BOOST_AUTO_TEST_CASE(conditional_nested_then) BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); } +BOOST_AUTO_TEST_CASE(conditional_switch) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'input (calldataload 0x04)) + ;; Calculates width in bytes of utf-8 characters. + (return + (switch + (< input 0x80) 1 + (< input 0xE0) 2 + (< input 0xF0) 3 + (< input 0xF8) 4 + (< input 0xFC) 5 + 6)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0x00) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("test()", 0x80) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("test()", 0xe0) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("test()", 0xf0) == encodeArgs(u256(4))); + BOOST_CHECK(callContractFunction("test()", 0xf8) == encodeArgs(u256(5))); + BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_one_arg_with_deposit) +{ + char const* sourceCode = R"( + (returnlll + (return + (switch 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_one_arg_no_deposit) +{ + char const* sourceCode = R"( + (returnlll + (seq + (switch [0]:42) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (switch (= (calldataload 0x04) 1) [0]:42) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_three_args_with_deposit) +{ + char const* sourceCode = R"( + (returnlll + (return + (switch (= (calldataload 0x04) 1) 41 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(42))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(41))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_three_args_no_deposit) +{ + char const* sourceCode = R"( + (returnlll + (switch + (= (calldataload 0x04) 1) (return 41) + (return 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(42))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(41))); +} + BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( |