Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
7521f3856d
  1. 8
      libethereum/State.cpp
  2. 12
      libsolidity/AST.cpp
  3. 11
      libsolidity/AST.h
  4. 2
      libsolidity/Compiler.cpp
  5. 120
      libsolidity/ExpressionCompiler.cpp
  6. 4
      libsolidity/ExpressionCompiler.h
  7. 37
      libsolidity/GlobalContext.cpp
  8. 57
      libsolidity/Types.cpp
  9. 55
      libsolidity/Types.h
  10. 126
      test/solidityEndToEndTest.cpp

8
libethereum/State.cpp

@ -51,14 +51,16 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
h256 s; h256 s;
} in; } in;
h256 ret;
memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
memset(_out.data(), 0, _out.size());
if (in.v > 28)
return;
SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)}; SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)};
if (!sig.isValid() || in.v > 28) if (!sig.isValid())
return; return;
h256 ret;
byte pubkey[65]; byte pubkey[65];
int pubkeylen = 65; int pubkeylen = 65;
secp256k1_start(); secp256k1_start();

12
libsolidity/AST.cpp

@ -467,19 +467,19 @@ void FunctionCall::checkTypeRequirements()
//@todo would be nice to create a struct type from the arguments //@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the // and then ask if that is implicitly convertible to the struct represented by the
// function parameters // function parameters
FunctionDefinition const& fun = dynamic_cast<FunctionType const&>(*expressionType).getFunction(); FunctionType const& functionType = dynamic_cast<FunctionType const&>(*expressionType);
vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters(); TypePointers const& parameterTypes = functionType.getParameterTypes();
if (parameters.size() != m_arguments.size()) if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i) for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
// @todo actually the return type should be an anonymous struct, // @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs // but we change it to the type of the first return value until we have structs
if (fun.getReturnParameters().empty()) if (functionType.getReturnParameterTypes().empty())
m_type = make_shared<VoidType>(); m_type = make_shared<VoidType>();
else else
m_type = fun.getReturnParameters().front()->getType(); m_type = functionType.getReturnParameterTypes().front();
} }
} }

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;
}; };

2
libsolidity/Compiler.cpp

@ -325,7 +325,7 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
Expression& expression = _expressionStatement.getExpression(); Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression); ExpressionCompiler::compileExpression(m_context, expression);
Type::Category category = expression.getType()->getCategory(); Type::Category category = expression.getType()->getCategory();
if (category != Type::Category::VOID && category != Type::Category::MAGIC) for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
return false; return false;
} }

120
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
@ -179,33 +180,90 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
} }
else else
{ {
//@todo: check for "external call" (to be stored in type) FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
eth::AssemblyItem returnLabel = m_context.pushNewTag();
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments(); std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments();
if (asserts(arguments.size() == function.getParameters().size())) if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
for (unsigned i = 0; i < arguments.size(); ++i)
if (function.getLocation() == Location::INTERNAL)
{ {
arguments[i]->accept(*this); // Calling convention: Caller pushes return address and arguments
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType()); // Callee removes them and pushes return values
eth::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i]);
}
_functionCall.getExpression().accept(*this);
m_context.appendJump();
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP;
}
else if (function.getLocation() == Location::EXTERNAL)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
else
{
switch (function.getLocation())
{
case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP;
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
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:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented."));
}
} }
_functionCall.getExpression().accept(*this);
m_context.appendJump();
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameters().size() - arguments.size() - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameters().size(); ++i)
m_context << eth::Instruction::POP;
} }
return false; return false;
} }
@ -216,9 +274,19 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
switch (_memberAccess.getExpression().getType()->getCategory()) switch (_memberAccess.getExpression().getType()->getCategory())
{ {
case Type::Category::INTEGER: case Type::Category::INTEGER:
if (asserts(member == "balance")) if (member == "balance")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
m_context << eth::Instruction::BALANCE;
}
else if (member == "send")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
m_context << eth::Instruction::BALANCE;
break; break;
case Type::Category::CONTRACT: case Type::Category::CONTRACT:
// call function // call function
@ -286,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;
} }

4
libsolidity/ExpressionCompiler.h

@ -132,6 +132,10 @@ private:
CompilerContext& m_context; CompilerContext& m_context;
LValue m_currentLValue; LValue m_currentLValue;
/// If a "virtual" function (i.e. a bulit-in function without jump tag) is encountered, the
/// actual function is stored here. @todo prevent assignment or store it with assignment
enum class SpecialFunction { NONE, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
SpecialFunction m_currentSpecialFunction;
}; };

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>(160, 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();

57
libsolidity/Types.cpp

@ -189,7 +189,10 @@ u256 IntegerType::literalValue(Literal const& _literal) const
return u256(value); return u256(value);
} }
const MemberList IntegerType::AddressMemberList = MemberList({{"balance", std::make_shared<IntegerType const>(256)}}); const MemberList IntegerType::AddressMemberList =
MemberList({{"balance", make_shared<IntegerType const>(256)},
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
TypePointers(), FunctionType::Location::SEND)}});
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
@ -299,17 +302,63 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
} }
FunctionType::FunctionType(FunctionDefinition const& _function)
{
TypePointers params;
TypePointers retParams;
params.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
params.push_back(var->getType());
retParams.reserve(_function.getReturnParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getReturnParameters())
retParams.push_back(var->getType());
swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes);
m_location = Location::INTERNAL;
}
bool FunctionType::operator==(Type const& _other) const bool FunctionType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
return false; return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other); FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
return other.m_function == m_function;
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
other.m_parameterTypes.cbegin(), typeCompare))
return false;
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
other.m_returnParameterTypes.cbegin(), typeCompare))
return false;
return true;
} }
string FunctionType::toString() const string FunctionType::toString() const
{ {
return "function " + m_function.getName(); string name = "function (";
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += ") returns (";
for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
return name + ")";
}
unsigned FunctionType::getSizeOnStack() const
{
switch (m_location)
{
case Location::INTERNAL:
return 1;
case Location::EXTERNAL:
return 2;
default:
return 0;
}
} }
bool MappingType::operator==(Type const& _other) const bool MappingType::operator==(Type const& _other) const
@ -348,11 +397,11 @@ MagicType::MagicType(MagicType::Kind _kind):
break; break;
case Kind::MSG: case Kind::MSG:
m_members = MemberList({{"sender", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)}, m_members = MemberList({{"sender", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"gas", make_shared<IntegerType const>(256)},
{"value", make_shared<IntegerType const>(256)}}); {"value", make_shared<IntegerType const>(256)}});
break; break;
case Kind::TX: case Kind::TX:
m_members = MemberList({{"origin", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)}, m_members = MemberList({{"origin", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"gas", make_shared<IntegerType const>(256)},
{"gasprice", make_shared<IntegerType const>(256)}}); {"gasprice", make_shared<IntegerType const>(256)}});
break; break;
default: default:

55
libsolidity/Types.h

@ -39,6 +39,8 @@ namespace solidity
// @todo realMxN, string<N> // @todo realMxN, string<N>
class Type; // forward class Type; // forward
using TypePointer = std::shared_ptr<Type const>;
using TypePointers = std::vector<TypePointer>;
/** /**
* List of members of a type. * List of members of a type.
@ -46,7 +48,6 @@ class Type; // forward
class MemberList class MemberList
{ {
public: public:
using TypePointer = std::shared_ptr<Type const>;
using MemberMap = std::map<std::string, TypePointer>; using MemberMap = std::map<std::string, TypePointer>;
MemberList() {} MemberList() {}
@ -54,7 +55,7 @@ public:
TypePointer getMemberType(std::string const& _name) const TypePointer getMemberType(std::string const& _name) const
{ {
auto it = m_memberTypes.find(_name); auto it = m_memberTypes.find(_name);
return it != m_memberTypes.end() ? it->second : std::shared_ptr<Type const>(); return it != m_memberTypes.end() ? it->second : TypePointer();
} }
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); } MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
@ -82,6 +83,7 @@ public:
static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken); static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName); static std::shared_ptr<Type> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type> fromFunction(FunctionDefinition const& _function);
/// @} /// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
@ -113,11 +115,12 @@ public:
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack, /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
/// i.e. it behaves differently in lvalue context and in value context. /// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; } virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; }
/// Returns the list of all members of this type. Default implementation: no members. /// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; } virtual MemberList const& getMembers() const { return EmptyMemberList; }
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
std::shared_ptr<Type const> getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0; virtual std::string toString() const = 0;
virtual u256 literalValue(Literal const&) const virtual u256 literalValue(Literal const&) const
@ -233,6 +236,7 @@ public:
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override;
virtual unsigned getSizeOnStack() const override { return 1; /*@todo*/ }
virtual std::string toString() const override; virtual std::string toString() const override;
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
@ -246,24 +250,41 @@ private:
}; };
/** /**
* The type of a function, there is one distinct type per function definition. * The type of a function, identified by its (return) parameter types.
* @todo the return parameters should also have names, i.e. return parameters should be a struct
* type.
*/ */
class FunctionType: public Type class FunctionType: public Type
{ {
public: public:
/// The meaning of the value(s) on the stack referencing the function:
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
/// OTHERS: special virtual function, nothing on the stack
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
virtual Category getCategory() const override { return Category::FUNCTION; } virtual Category getCategory() const override { return Category::FUNCTION; }
FunctionType(FunctionDefinition const& _function): m_function(_function) {} explicit FunctionType(FunctionDefinition const& _function);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
m_location(_location) {}
FunctionDefinition const& getFunction() const { return m_function; } TypePointers const& getParameterTypes() const { return m_parameterTypes; }
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString() const override;
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
Location getLocation() const { return m_location; }
private: private:
FunctionDefinition const& m_function; TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes;
Location m_location;
}; };
/** /**
@ -273,19 +294,19 @@ class MappingType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::MAPPING; } virtual Category getCategory() const override { return Category::MAPPING; }
MappingType(std::shared_ptr<Type const> _keyType, std::shared_ptr<Type const> _valueType): MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
m_keyType(_keyType), m_valueType(_valueType) {} m_keyType(_keyType), m_valueType(_valueType) {}
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString() const override;
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
std::shared_ptr<Type const> getKeyType() const { return m_keyType; } TypePointer getKeyType() const { return m_keyType; }
std::shared_ptr<Type const> getValueType() const { return m_valueType; } TypePointer getValueType() const { return m_valueType; }
private: private:
std::shared_ptr<Type const> m_keyType; TypePointer m_keyType;
std::shared_ptr<Type const> m_valueType; TypePointer m_valueType;
}; };
/** /**
@ -302,6 +323,7 @@ public:
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
}; };
/** /**
@ -312,9 +334,9 @@ class TypeType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::TYPE; } virtual Category getCategory() const override { return Category::TYPE; }
TypeType(std::shared_ptr<Type const> const& _actualType): m_actualType(_actualType) {} TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; } TypePointer const& getActualType() const { return m_actualType; }
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
@ -323,7 +345,7 @@ public:
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
private: private:
std::shared_ptr<Type const> m_actualType; TypePointer m_actualType;
}; };
@ -334,13 +356,14 @@ 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);
virtual bool operator==(Type const& _other) const; virtual bool operator==(Type const& _other) const;
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual unsigned getSizeOnStack() const override { return 0; }
virtual MemberList const& getMembers() const override { return m_members; } virtual MemberList const& getMembers() const override { return m_members; }
virtual std::string toString() const override; virtual std::string toString() const override;

126
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
@ -774,6 +776,128 @@ BOOST_AUTO_TEST_CASE(blockchain)
BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1))); BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1)));
} }
BOOST_AUTO_TEST_CASE(function_types)
{
char const* sourceCode = "contract test {\n"
" function a(bool selector) returns (uint b) {\n"
" var f = fun1;\n"
" if (selector) f = fun2;\n"
" return f(9);\n"
" }\n"
" function fun1(uint x) returns (uint b) {\n"
" return 11;\n"
" }\n"
" function fun2(uint x) returns (uint b) {\n"
" return 12;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes{0}) == toBigEndian(u256(11)));
BOOST_CHECK(callContractFunction(0, bytes{1}) == toBigEndian(u256(12)));
}
BOOST_AUTO_TEST_CASE(send_ether)
{
char const* sourceCode = "contract test {\n"
" function a(address addr, uint amount) returns (uint ret) {\n"
" addr.send(amount);\n"
" return address(this).balance;\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
BOOST_CHECK(callContractFunction(0, address, amount) == toBigEndian(u256(1)));
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