aboutsummaryrefslogtreecommitdiffstats
path: root/solidityCompiler.cpp
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2014-10-30 08:20:32 +0800
committerChristian <c@ethdev.com>2014-10-30 08:25:42 +0800
commitb0d4e0dedfdabe7c02bd1d7f3870d19fd498b74b (patch)
treedb5ca37bd9a880415fcf5d2e7f3d4699f13d9a60 /solidityCompiler.cpp
parente08065a2fb67558300a7164d76b3da31f7fcc6d4 (diff)
downloaddexon-solidity-b0d4e0dedfdabe7c02bd1d7f3870d19fd498b74b.tar.gz
dexon-solidity-b0d4e0dedfdabe7c02bd1d7f3870d19fd498b74b.tar.zst
dexon-solidity-b0d4e0dedfdabe7c02bd1d7f3870d19fd498b74b.zip
Contract compiler and also add ExpressionStatement to AST.
ExpressionStatement functions as glue between Statements and Expressions. This way it is possible to detect when the border between statements and expressions is crossed while walking the AST. Note that ExpressionStatement is not the only border, almost every statement can contains expressions.
Diffstat (limited to 'solidityCompiler.cpp')
-rw-r--r--solidityCompiler.cpp323
1 files changed, 130 insertions, 193 deletions
diff --git a/solidityCompiler.cpp b/solidityCompiler.cpp
index 591330dd..c9cf464d 100644
--- a/solidityCompiler.cpp
+++ b/solidityCompiler.cpp
@@ -18,7 +18,7 @@
/**
* @author Christian <c@ethdev.com>
* @date 2014
- * Unit tests for the name and type resolution of the solidity parser.
+ * Unit tests for the solidity compiler.
*/
#include <string>
@@ -31,6 +31,9 @@
#include <libsolidity/AST.h>
#include <boost/test/unit_test.hpp>
+using namespace std;
+using namespace dev::eth;
+
namespace dev
{
namespace solidity
@@ -41,233 +44,167 @@ namespace test
namespace
{
-/// Helper class that extracts the first expression in an AST.
-class FirstExpressionExtractor: private ASTVisitor
-{
-public:
- FirstExpressionExtractor(ASTNode& _node): m_expression(nullptr) { _node.accept(*this); }
- Expression* getExpression() const { return m_expression; }
-private:
- virtual bool visit(Expression& _expression) override { return checkExpression(_expression); }
- virtual bool visit(Assignment& _expression) override { return checkExpression(_expression); }
- virtual bool visit(UnaryOperation& _expression) override { return checkExpression(_expression); }
- virtual bool visit(BinaryOperation& _expression) override { return checkExpression(_expression); }
- virtual bool visit(FunctionCall& _expression) override { return checkExpression(_expression); }
- virtual bool visit(MemberAccess& _expression) override { return checkExpression(_expression); }
- virtual bool visit(IndexAccess& _expression) override { return checkExpression(_expression); }
- virtual bool visit(PrimaryExpression& _expression) override { return checkExpression(_expression); }
- virtual bool visit(Identifier& _expression) override { return checkExpression(_expression); }
- virtual bool visit(ElementaryTypeNameExpression& _expression) override { return checkExpression(_expression); }
- virtual bool visit(Literal& _expression) override { return checkExpression(_expression); }
- bool checkExpression(Expression& _expression)
- {
- if (m_expression == nullptr)
- m_expression = &_expression;
- return false;
- }
-private:
- Expression* m_expression;
-};
-
-bytes compileFirstExpression(const std::string& _sourceCode)
+bytes compileContract(const string& _sourceCode)
{
Parser parser;
ASTPointer<ContractDefinition> contract;
- BOOST_REQUIRE_NO_THROW(contract = parser.parse(std::make_shared<Scanner>(CharStream(_sourceCode))));
+ BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver;
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
- FirstExpressionExtractor extractor(*contract);
- BOOST_REQUIRE(extractor.getExpression() != nullptr);
- CompilerContext context;
- ExpressionCompiler compiler(context);
- compiler.compile(*extractor.getExpression());
- bytes instructions = compiler.getAssembledBytecode();
+ bytes instructions = Compiler::compile(*contract);
// debug
- //std::cout << eth::disassemble(instructions) << std::endl;
+ //cout << eth::disassemble(instructions) << endl;
return instructions;
}
} // end anonymous namespace
-BOOST_AUTO_TEST_SUITE(SolidityExpressionCompiler)
+BOOST_AUTO_TEST_SUITE(SolidityCompiler)
-BOOST_AUTO_TEST_CASE(literal_true)
+BOOST_AUTO_TEST_CASE(smoke_test)
{
char const* sourceCode = "contract test {\n"
- " function f() { var x = true; }"
+ " function f() { var x = 2; }\n"
"}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH1), 0x1});
+ bytes code = compileContract(sourceCode);
+
+ bytes expectation({byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x0, // initialize local variable x
+ byte(Instruction::PUSH1), 0x2,
+ byte(Instruction::SWAP1),
+ byte(Instruction::POP),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::POP),
+ byte(Instruction::JUMP)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
-BOOST_AUTO_TEST_CASE(literal_false)
+BOOST_AUTO_TEST_CASE(different_argument_numbers)
{
char const* sourceCode = "contract test {\n"
- " function f() { var x = false; }"
+ " function f(uint a, uint b, uint c) returns(uint d) { return b; }\n"
+ " function g() returns (uint e, uint h) { h = f(1, 2, 3); }\n"
"}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH1), 0x0});
+ bytes code = compileContract(sourceCode);
+
+ bytes expectation({byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x0, // initialize return variable d
+ byte(Instruction::DUP3),
+ byte(Instruction::SWAP1), // assign b to d
+ byte(Instruction::POP),
+ byte(Instruction::PUSH1), 0xa, // jump to return
+ byte(Instruction::JUMP),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::SWAP4), // store d and fetch return address
+ byte(Instruction::SWAP3), // store return address
+ byte(Instruction::POP),
+ byte(Instruction::POP),
+ byte(Instruction::POP),
+ byte(Instruction::JUMP), // end of f
+ byte(Instruction::JUMPDEST), // beginning of g
+ byte(Instruction::PUSH1), 0x0,
+ byte(Instruction::DUP1), // initialized e and h
+ byte(Instruction::PUSH1), 0x20, // ret address
+ byte(Instruction::PUSH1), 0x1,
+ byte(Instruction::PUSH1), 0x2,
+ byte(Instruction::PUSH1), 0x3,
+ byte(Instruction::PUSH1), 0x1,
+ // stack here: ret e h 0x20 1 2 3 0x1
+ byte(Instruction::JUMP),
+ byte(Instruction::JUMPDEST),
+ // stack here: ret e h f(1,2,3)
+ byte(Instruction::DUP2),
+ byte(Instruction::POP),
+ byte(Instruction::SWAP1),
+ // stack here: ret e f(1,2,3) h
+ byte(Instruction::POP),
+ byte(Instruction::DUP1), // retrieve it again as "value of expression"
+ byte(Instruction::POP), // end of assignment
+ // stack here: ret e f(1,2,3)
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::SWAP1),
+ // ret e f(1,2,3)
+ byte(Instruction::SWAP2),
+ // f(1,2,3) e ret
+ byte(Instruction::JUMP) // end of g
+ });
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
-BOOST_AUTO_TEST_CASE(int_literal)
+BOOST_AUTO_TEST_CASE(ifStatement)
{
char const* sourceCode = "contract test {\n"
- " function f() { var x = 0x12345678901234567890; }"
+ " function f() { bool x; if (x) 77; else if (!x) 78; else 79; }"
"}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90,
- 0x12, 0x34, 0x56, 0x78, 0x90});
- BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
-}
-
-BOOST_AUTO_TEST_CASE(comparison)
-{
- char const* sourceCode = "contract test {\n"
- " function f() { var x = (0x10aa < 0x11aa) != true; }"
- "}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH2), 0x10, 0xaa,
- byte(eth::Instruction::PUSH2), 0x11, 0xaa,
- byte(eth::Instruction::GT),
- byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::EQ),
- byte(eth::Instruction::NOT)});
- BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
-}
-
-BOOST_AUTO_TEST_CASE(short_circuiting)
-{
- char const* sourceCode = "contract test {\n"
- " function f() { var x = (10 + 8 >= 4 || 2 != 9) != true; }"
- "}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH1), 0xa,
- byte(eth::Instruction::PUSH1), 0x8,
- byte(eth::Instruction::ADD),
- byte(eth::Instruction::PUSH1), 0x4,
- byte(eth::Instruction::GT),
- byte(eth::Instruction::NOT), // after this we have 10 + 8 >= 4
- byte(eth::Instruction::DUP1),
- byte(eth::Instruction::PUSH1), 0x14,
- byte(eth::Instruction::JUMPI), // short-circuit if it is true
- byte(eth::Instruction::PUSH1), 0x2,
- byte(eth::Instruction::PUSH1), 0x9,
- byte(eth::Instruction::EQ),
- byte(eth::Instruction::NOT), // after this we have 2 != 9
- byte(eth::Instruction::JUMPDEST),
- byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::EQ),
- byte(eth::Instruction::NOT)});
- BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
-}
-
-BOOST_AUTO_TEST_CASE(arithmetics)
-{
- char const* sourceCode = "contract test {\n"
- " function f() { var x = (1 * (2 / (3 % (4 + (5 - (6 | (7 & (8 ^ 9)))))))); }"
- "}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::PUSH1), 0x2,
- byte(eth::Instruction::PUSH1), 0x3,
- byte(eth::Instruction::PUSH1), 0x4,
- byte(eth::Instruction::PUSH1), 0x5,
- byte(eth::Instruction::PUSH1), 0x6,
- byte(eth::Instruction::PUSH1), 0x7,
- byte(eth::Instruction::PUSH1), 0x8,
- byte(eth::Instruction::PUSH1), 0x9,
- byte(eth::Instruction::XOR),
- byte(eth::Instruction::AND),
- byte(eth::Instruction::OR),
- byte(eth::Instruction::SWAP1),
- byte(eth::Instruction::SUB),
- byte(eth::Instruction::ADD),
- byte(eth::Instruction::MOD),
- byte(eth::Instruction::DIV),
- byte(eth::Instruction::MUL)});
- BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
-}
-
-BOOST_AUTO_TEST_CASE(unary_operators)
-{
- char const* sourceCode = "contract test {\n"
- " function f() { var x = !(~+-1 == 2); }"
- "}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::PUSH1), 0x0,
- byte(eth::Instruction::SUB),
- byte(eth::Instruction::BNOT),
- byte(eth::Instruction::PUSH1), 0x2,
- byte(eth::Instruction::EQ),
- byte(eth::Instruction::NOT)});
- BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
-}
-
-BOOST_AUTO_TEST_CASE(unary_inc_dec)
-{
- char const* sourceCode = "contract test {\n"
- " function f(uint a) { var x = ((a++ ^ ++a) ^ a--) ^ --a; }"
- "}\n";
- bytes code = compileFirstExpression(sourceCode);
-
- bytes expectation({byte(eth::Instruction::DUP9), // will change as soon as we have real stack tracking
- byte(eth::Instruction::DUP1),
- byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::ADD),
- byte(eth::Instruction::SWAP8), // will change
- byte(eth::Instruction::POP), // first ++
- byte(eth::Instruction::DUP9),
- byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::ADD),
- byte(eth::Instruction::SWAP8), // will change
- byte(eth::Instruction::POP), // second ++
- byte(eth::Instruction::DUP8), // will change
- byte(eth::Instruction::XOR),
- byte(eth::Instruction::DUP9), // will change
- byte(eth::Instruction::DUP1),
- byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::SWAP1),
- byte(eth::Instruction::SUB),
- byte(eth::Instruction::SWAP8), // will change
- byte(eth::Instruction::POP), // first --
- byte(eth::Instruction::XOR),
- byte(eth::Instruction::DUP9),
- byte(eth::Instruction::PUSH1), 0x1,
- byte(eth::Instruction::SWAP1),
- byte(eth::Instruction::SUB),
- byte(eth::Instruction::SWAP8), // will change
- byte(eth::Instruction::POP), // second ++
- byte(eth::Instruction::DUP8), // will change
- byte(eth::Instruction::XOR)});
+ bytes code = compileContract(sourceCode);
+
+ bytes expectation({byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x0,
+ byte(Instruction::DUP1),
+ byte(Instruction::PUSH1), 0x1b, // "true" target
+ byte(Instruction::JUMPI),
+ // new check "else if" condition
+ byte(Instruction::DUP1),
+ byte(Instruction::NOT),
+ byte(Instruction::PUSH1), 0x13,
+ byte(Instruction::JUMPI),
+ // "else" body
+ byte(Instruction::PUSH1), 0x4f,
+ byte(Instruction::POP),
+ byte(Instruction::PUSH1), 0x17, // exit path of second part
+ byte(Instruction::JUMP),
+ // "else if" body
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x4e,
+ byte(Instruction::POP),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x1f,
+ byte(Instruction::JUMP),
+ // "if" body
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x4d,
+ byte(Instruction::POP),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::POP),
+ byte(Instruction::JUMP)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
-BOOST_AUTO_TEST_CASE(assignment)
+BOOST_AUTO_TEST_CASE(loops)
{
char const* sourceCode = "contract test {\n"
- " function f(uint a, uint b) { (a += b) * 2; }"
+ " function f() { while(true){1;break;2;continue;3;return;4;} }"
"}\n";
- bytes code = compileFirstExpression(sourceCode);
+ bytes code = compileContract(sourceCode);
+
+ bytes expectation({byte(Instruction::JUMPDEST),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::PUSH1), 0x1,
+ byte(Instruction::NOT),
+ byte(Instruction::PUSH1), 0x21,
+ byte(Instruction::JUMPI),
+ byte(Instruction::PUSH1), 0x1,
+ byte(Instruction::POP),
+ byte(Instruction::PUSH1), 0x21,
+ byte(Instruction::JUMP), // break
+ byte(Instruction::PUSH1), 0x2,
+ byte(Instruction::POP),
+ byte(Instruction::PUSH1), 0x2,
+ byte(Instruction::JUMP), // continue
+ byte(Instruction::PUSH1), 0x3,
+ byte(Instruction::POP),
+ byte(Instruction::PUSH1), 0x22,
+ byte(Instruction::JUMP), // return
+ byte(Instruction::PUSH1), 0x4,
+ byte(Instruction::POP),
+ byte(Instruction::PUSH1), 0x2,
+ byte(Instruction::JUMP),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::JUMPDEST),
+ byte(Instruction::JUMP)});
- bytes expectation({byte(eth::Instruction::DUP9), // will change as soon as we have real stack tracking
- byte(eth::Instruction::DUP9),
- byte(eth::Instruction::ADD),
- byte(eth::Instruction::SWAP8), // will change
- byte(eth::Instruction::POP), // first ++
- byte(eth::Instruction::DUP8),
- byte(eth::Instruction::PUSH1), 0x2,
- byte(eth::Instruction::MUL)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}