Browse Source

Global functions.

cl-refactor
Christian 10 years ago
parent
commit
68acfc0544
  1. 11
      libsolidity/AST.h
  2. 55
      libsolidity/ExpressionCompiler.cpp
  3. 37
      libsolidity/GlobalContext.cpp
  4. 2
      libsolidity/Types.h
  5. 91
      test/solidityEndToEndTest.cpp

11
libsolidity/AST.h

@ -233,23 +233,20 @@ private:
}; };
/** /**
* Pseudo AST node that is used as declaration for "this", "msg", "tx" and "block" when the * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* identifier is encountered. Will never have a valid location in the source code. * functions when such an identifier is encountered. Will never have a valid location in the source code.
*/ */
class MagicVariableDeclaration: public Declaration class MagicVariableDeclaration: public Declaration
{ {
public: public:
enum class VariableKind { THIS, MSG, TX, BLOCK }; MagicVariableDeclaration(ASTString const& _name, std::shared_ptr<Type const> const& _type):
MagicVariableDeclaration(VariableKind _kind, ASTString const& _name, std::shared_ptr<Type const> const& _type): Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {}
Declaration(Location(), std::make_shared<ASTString>(_name)), m_kind(_kind), m_type(_type) {}
virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); } << errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
VariableKind getKind() const { return m_kind; }
private: private:
VariableKind m_kind;
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type;
}; };

55
libsolidity/ExpressionCompiler.cpp

@ -160,6 +160,7 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
bool ExpressionCompiler::visit(FunctionCall& _functionCall) bool ExpressionCompiler::visit(FunctionCall& _functionCall)
{ {
using Location = FunctionType::Location;
if (_functionCall.isTypeConversion()) if (_functionCall.isTypeConversion())
{ {
//@todo struct construction //@todo struct construction
@ -184,7 +185,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
if (asserts(arguments.size() == function.getParameterTypes().size())) if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
if (function.getLocation() == FunctionType::Location::INTERNAL) if (function.getLocation() == Location::INTERNAL)
{ {
// Calling convention: Caller pushes return address and arguments // Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values // Callee removes them and pushes return values
@ -208,29 +209,57 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
} }
else if (function.getLocation() == FunctionType::Location::EXTERNAL) else if (function.getLocation() == Location::EXTERNAL)
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
}
else else
{ {
switch (function.getLocation()) switch (function.getLocation())
{ {
case FunctionType::Location::SEND: case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0); m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this); arguments.front()->accept(*this);
//@todo might not be necessary //@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front()); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this); _functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL << eth::Instruction::CALL
<< eth::Instruction::POP; << eth::Instruction::POP;
break; break;
case FunctionType::Location::SUICIDE: case Location::SUICIDE:
arguments.front()->accept(*this); arguments.front()->accept(*this);
//@todo might not be necessary //@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front()); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE; m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
{
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
// @todo later, combine this code with external function call
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
// @todo move this once we actually use memory
m_context << u256(i * 32) << eth::Instruction::MSTORE;
}
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP
<< u256(0) << eth::Instruction::MLOAD;
break;
}
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented."));
} }
@ -246,9 +275,15 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{ {
case Type::Category::INTEGER: case Type::Category::INTEGER:
if (member == "balance") if (member == "balance")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
m_context << eth::Instruction::BALANCE; m_context << eth::Instruction::BALANCE;
}
else if (member == "send") else if (member == "send")
{ // no modification {
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
} }
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
@ -319,7 +354,7 @@ void ExpressionCompiler::endVisit(Identifier& _identifier)
Declaration* declaration = _identifier.getReferencedDeclaration(); Declaration* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration)) if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration))
{ {
if (magicVar->getKind() == MagicVariableDeclaration::VariableKind::THIS) if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS; m_context << eth::Instruction::ADDRESS;
return; return;
} }

37
libsolidity/GlobalContext.cpp

@ -33,15 +33,33 @@ namespace solidity
{ {
GlobalContext::GlobalContext(): GlobalContext::GlobalContext():
m_magicVariables{make_shared<MagicVariableDeclaration>(MagicVariableDeclaration::VariableKind::BLOCK, m_magicVariables{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::BLOCK)),
"block", make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::MSG)),
make_shared<MagicType>(MagicType::Kind::BLOCK)), make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)),
make_shared<MagicVariableDeclaration>(MagicVariableDeclaration::VariableKind::MSG, make_shared<MagicVariableDeclaration>("suicide",
"msg", make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(0,
make_shared<MagicType>(MagicType::Kind::MSG)), IntegerType::Modifier::ADDRESS)}),
make_shared<MagicVariableDeclaration>(MagicVariableDeclaration::VariableKind::TX, TypePointers(),
"tx", FunctionType::Location::SUICIDE)),
make_shared<MagicType>(MagicType::Kind::TX))} make_shared<MagicVariableDeclaration>("sha3",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA3)),
make_shared<MagicVariableDeclaration>("sha256",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA256)),
make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(8, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}),
FunctionType::Location::ECRECOVER)),
make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::RIPEMD160))}
{ {
} }
@ -64,7 +82,6 @@ MagicVariableDeclaration*GlobalContext::getCurrentThis() const
{ {
if (!m_thisPointer[m_currentContract]) if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
MagicVariableDeclaration::VariableKind::THIS,
"this", make_shared<ContractType>(*m_currentContract)); "this", make_shared<ContractType>(*m_currentContract));
return m_thisPointer[m_currentContract].get(); return m_thisPointer[m_currentContract].get();

2
libsolidity/Types.h

@ -356,7 +356,7 @@ private:
class MagicType: public Type class MagicType: public Type
{ {
public: public:
enum class Kind { BLOCK, MSG, TX }; //@todo should be unified with MagicVariableDeclaration::VariableKind; enum class Kind { BLOCK, MSG, TX };
virtual Category getCategory() const override { return Category::MAGIC; } virtual Category getCategory() const override { return Category::MAGIC; }
MagicType(Kind _kind); MagicType(Kind _kind);

91
test/solidityEndToEndTest.cpp

@ -27,12 +27,14 @@
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libdevcrypto/SHA3.h>
using namespace std; using namespace std;
namespace dev namespace dev
{ {
/// Provider another overload for toBigEndian to encode arguments and return values. /// Provides additional overloads for toBigEndian to encode arguments and return values.
inline bytes toBigEndian(byte _value) { return bytes({_value}); }
inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); } inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); }
namespace solidity namespace solidity
@ -809,6 +811,93 @@ BOOST_AUTO_TEST_CASE(send_ether)
BOOST_CHECK_EQUAL(m_state.balance(address), amount); BOOST_CHECK_EQUAL(m_state.balance(address), amount);
} }
BOOST_AUTO_TEST_CASE(suicide)
{
char const* sourceCode = "contract test {\n"
" function a(address receiver) returns (uint ret) {\n"
" suicide(receiver);\n"
" return 10;\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount);
u160 address(23);
BOOST_CHECK(callContractFunction(0, address) == bytes());
BOOST_CHECK(!m_state.addressHasCode(m_contractAddress));
BOOST_CHECK_EQUAL(m_state.balance(address), amount);
}
BOOST_AUTO_TEST_CASE(sha3)
{
char const* sourceCode = "contract test {\n"
" function a(hash input) returns (hash sha3hash) {\n"
" return sha3(input);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto f = [&](u256 const& _x) -> u256
{
return dev::sha3(toBigEndian(_x));
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
}
BOOST_AUTO_TEST_CASE(sha256)
{
char const* sourceCode = "contract test {\n"
" function a(hash input) returns (hash sha256hash) {\n"
" return sha256(input);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto f = [&](u256 const& _input) -> u256
{
h256 ret;
dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return ret;
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
}
BOOST_AUTO_TEST_CASE(ripemd)
{
char const* sourceCode = "contract test {\n"
" function a(hash input) returns (hash sha256hash) {\n"
" return ripemd160(input);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto f = [&](u256 const& _input) -> u256
{
h256 ret;
dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return u256(ret) >> (256 - 160);
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
}
BOOST_AUTO_TEST_CASE(ecrecover)
{
char const* sourceCode = "contract test {\n"
" function a(hash h, uint8 v, hash r, hash s) returns (address addr) {\n"
" return ecrecover(h, v, r, s);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
u256 h("0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c");
byte v = 28;
u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f");
u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549");
u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save