aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Beregszaszi <alex@rtfs.hu>2017-10-02 20:38:21 +0800
committerGitHub <noreply@github.com>2017-10-02 20:38:21 +0800
commit6cbb726fb8970c6cb98e9b6a2928ef612ad9d760 (patch)
treefcf65dc5db8179404b60a0332ad740cf24ab9a73
parent2a8e965bb135a806aaaab9aa5df24da6d7def1ba (diff)
parentd010d6300c667bbe81469a23e6bf76015623e76b (diff)
downloaddexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.tar
dexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.tar.gz
dexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.tar.bz2
dexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.tar.lz
dexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.tar.xz
dexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.tar.zst
dexon-solidity-6cbb726fb8970c6cb98e9b6a2928ef612ad9d760.zip
Merge pull request #2622 from benjaminion/lll-switch
LLL: Implement a "switch" expression
-rw-r--r--liblll/CodeFragment.cpp38
-rw-r--r--test/liblll/Compiler.cpp128
-rw-r--r--test/liblll/EndToEndTest.cpp86
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"(