Browse Source

Dynamic copy to memory.

cl-refactor
Christian 10 years ago
parent
commit
9be8307f22
  1. 7
      libsolidity/Compiler.cpp
  2. 41
      libsolidity/CompilerUtils.cpp
  3. 12
      libsolidity/CompilerUtils.h
  4. 146
      libsolidity/ExpressionCompiler.cpp
  5. 27
      libsolidity/ExpressionCompiler.h
  6. 31
      libsolidity/Types.cpp
  7. 1
      libsolidity/Types.h
  8. 86
      test/SolidityEndToEndTest.cpp

7
libsolidity/Compiler.cpp

@ -207,15 +207,10 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
for (TypePointer const& type: _typeParameters)
{
unsigned numBytes = type->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_leftAligned = type->getCategory() == Type::Category::String;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords);
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
stackDepth -= type->getSizeOnStack();
}
// note that the stack is not cleaned up here

41
libsolidity/CompilerUtils.cpp

@ -62,20 +62,22 @@ unsigned CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _
}
}
unsigned CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned,
bool _padToWordBoundaries)
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
{
if (_bytes == 0)
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
return numBytes;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
{
m_context << eth::Instruction::POP;
return 0;
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
m_context << u256(numBytes) << eth::Instruction::ADD;
}
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
if (_bytes != 32 && !_leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
return _padToWordBoundaries ? 32 : _bytes;
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@ -114,5 +116,22 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size;
}
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries)
{
unsigned _encodedSize = _type.getCalldataEncodedSize();
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
bool leftAligned = _type.getCategory() == Type::Category::String;
if (numBytes == 0)
m_context << eth::Instruction::POP;
else
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
}
return numBytes;
}
}
}

12
libsolidity/CompilerUtils.h

@ -47,13 +47,14 @@ public:
bool _fromCalldata = false, bool _padToWordBoundaries = false);
/// Stores data from stack in memory.
/// @param _offset offset in memory
/// @param _bytes number of bytes to store
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
/// @param _type type of the data on the stack
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
/// @returns the number of bytes written to memory (can be different from _bytes if
/// _padToWordBoundaries is true)
unsigned storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false,
bool _padToWordBoundaries = false);
unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that.
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
/// padded calldata)
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
@ -72,7 +73,10 @@ public:
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset;
private:
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries);
CompilerContext& m_context;
};

146
libsolidity/ExpressionCompiler.cpp

@ -276,10 +276,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
//@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned length = bytecode.size();
length += appendArgumentsCopyToMemory(arguments, function.getParameterTypes(), length);
m_context << u256(bytecode.size());
appendArgumentsCopyToMemory(arguments, function.getParameterTypes());
// size, offset, endowment
m_context << u256(length) << u256(0);
m_context << u256(0);
if (function.valueSet())
m_context << eth::dupInstruction(3);
else
@ -329,8 +329,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::SHA3:
{
unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments());
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
m_context << u256(0);
appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments());
m_context << u256(0) << eth::Instruction::SHA3;
break;
}
case Location::Log0:
@ -345,23 +346,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
}
unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(),
*arguments.front());
solAssert(length == 32, "Log data should be 32 bytes long (for now).");
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
m_context << u256(0);
appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
m_context << u256(0) << eth::logInstruction(logNumber);
break;
}
case Location::Event:
{
_functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
// Copy all non-indexed arguments to memory (data)
unsigned numIndexed = 0;
unsigned memLength = 0;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg],
*arguments[arg], memLength);
// All indexed arguments go to the stack
for (unsigned arg = arguments.size(); arg > 0; --arg)
if (event.getParameters()[arg - 1]->isIndexed())
@ -374,7 +368,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
++numIndexed;
solAssert(numIndexed <= 4, "Too many indexed arguments.");
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed);
// Copy all non-indexed arguments to memory (data)
m_context << u256(0);
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]);
m_context << u256(0) << eth::logInstruction(numIndexed);
break;
}
case Location::BlockHash:
@ -514,12 +513,16 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{
_indexAccess.getBaseExpression().accept(*this);
TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType();
unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression());
solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now).");
// @todo move this once we actually use memory
length += CompilerUtils(m_context).storeInMemory(length);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
Type const& baseType = *_indexAccess.getBaseExpression().getType();
solAssert(baseType.getCategory() == Type::Category::Mapping, "");
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
m_context << u256(0);
appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
solAssert(baseType.getSizeOnStack() == 1,
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
m_context << eth::Instruction::SWAP1;
appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -810,26 +813,30 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
if (!bare)
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
m_context << u256(retSize) << u256(0);
if (bare)
m_context << u256(0);
else
{
// copy function identifier
m_context << eth::dupInstruction(gasValueSize + 1);
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
m_context << eth::dupInstruction(gasValueSize + 3);
CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8));
m_context << u256(CompilerUtils::dataStartOffset);
}
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes.
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset,
_functionType.padArguments(), bare);
appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(),
_functionType.padArguments(), bare);
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
// CALL arguments: outSize, outOff, inSize, (already present up to here)
// inOff, value, addr, gas (stack top)
m_context << u256(0);
if (_functionType.valueSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
@ -858,14 +865,12 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
}
}
unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
unsigned _memoryOffset,
bool _padToWordBoundaries,
bool _padExceptionIfFourBytes)
void ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
bool _padToWordBoundaries,
bool _padExceptionIfFourBytes)
{
solAssert(_types.empty() || _types.size() == _arguments.size(), "");
unsigned length = 0;
for (size_t i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
@ -875,31 +880,55 @@ unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expre
// Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
pad = false;
length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(),
_memoryOffset + length, pad);
appendTypeMoveToMemory(*expectedType, pad);
}
return length;
}
unsigned ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, bool _padToWordBoundaries)
void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries)
{
unsigned const c_encodedSize = _type.getCalldataEncodedSize();
unsigned const c_numBytes = _padToWordBoundaries ? CompilerUtils::getPaddedSize(c_encodedSize) : c_encodedSize;
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_location)
<< errinfo_comment("Type " + _type.toString() + " not yet supported."));
bool const c_leftAligned = _type.getCategory() == Type::Category::String;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, _padToWordBoundaries);
if (_type.getCategory() == Type::Category::ByteArray)
{
auto const& type = dynamic_cast<ByteArrayType const&>(_type);
solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Non-storage byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3
<< eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart
// load and store
<< eth::Instruction::DUP2 << eth::Instruction::SLOAD
<< eth::Instruction::DUP2 << eth::Instruction::MSTORE
// increment storage_data_offset by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment memory offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
else
CompilerUtils(m_context).storeInMemoryDynamic(_type, _padToWordBoundaries);
}
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
Expression const& _expression,
unsigned _memoryOffset)
void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
{
_expression.accept(*this);
appendTypeConversion(*_expression.getType(), _expectedType, true);
return appendTypeMoveToMemory(_expectedType, _expression.getLocation(), _memoryOffset);
appendTypeMoveToMemory(_expectedType);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
@ -910,7 +939,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
TypePointers const& paramTypes = accessorType.getParameterTypes();
// move arguments to memory
for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
length += appendTypeMoveToMemory(*paramType, Location(), length);
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
// retrieve the position of the variable
m_context << m_context.getStorageLocationOfVariable(_varDecl);
@ -1217,6 +1246,7 @@ void ExpressionCompiler::LValue::copyByteArrayToStorage(ByteArrayType const& _ta
break;
}
case ByteArrayType::Location::Storage:
solAssert(false, "Not Yet implemented.");
break;
default:
solAssert(false, "Byte array location not implemented.");

27
libsolidity/ExpressionCompiler.h

@ -92,21 +92,18 @@ private:
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
bool bare = false);
/// Appends code that evaluates the given arguments and moves the result to memory (with optional offset).
/// @returns the number of bytes moved to memory
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
unsigned _memoryOffset = 0,
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory
/// @returns the number of bytes moved to memory
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset,
bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory (with optional offset).
/// @returns the number of bytes moved to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
/// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.
void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory. The memory offset is
/// expected below the stack element and is updated by this call.
void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
/// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);

31
libsolidity/Types.cpp

@ -35,7 +35,7 @@ namespace dev
namespace solidity
{
shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
@ -64,7 +64,12 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
std::string(Token::toString(_typeToken)) + " to type."));
}
shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
TypePointer Type::fromElementaryTypeName(string const& _name)
{
return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}
TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
@ -73,21 +78,21 @@ shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const&
return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract);
return shared_ptr<Type const>();
return TypePointer();
}
shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName)
TypePointer Type::fromMapping(Mapping const& _typeName)
{
shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
TypePointer keyType = _typeName.getKeyType().toType();
if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
shared_ptr<Type const> valueType = _typeName.getValueType().toType();
TypePointer valueType = _typeName.getValueType().toType();
if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType);
}
shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
TypePointer Type::forLiteral(Literal const& _literal)
{
switch (_literal.getToken())
{
@ -561,8 +566,8 @@ MemberList const& ContractType::getMembers() const
if (!m_members)
{
// All address members and all interface functions
map<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end());
map<string, TypePointer> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end());
if (m_super)
{
for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
@ -617,14 +622,14 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const
{
u256 size = 0;
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
for (pair<string, TypePointer> const& member: getMembers())
size += member.second->getStorageSize();
return max<u256>(1, size);
}
bool StructType::canLiveOutsideStorage() const
{
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
for (pair<string, TypePointer> const& member: getMembers())
if (!member.second->canLiveOutsideStorage())
return false;
return true;
@ -640,7 +645,7 @@ MemberList const& StructType::getMembers() const
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
map<string, TypePointer> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members[variable->getName()] = variable->getType();
m_members.reset(new MemberList(members));
@ -847,7 +852,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
TypePointers pointers;
pointers.reserve(_types.size());
for (string const& type: _types)
pointers.push_back(Type::fromElementaryTypeName(Token::fromIdentifierOrKeyword(type)));
pointers.push_back(Type::fromElementaryTypeName(type));
return pointers;
}

1
libsolidity/Types.h

@ -86,6 +86,7 @@ public:
///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName);
static TypePointer fromFunction(FunctionDefinition const& _function);

86
test/SolidityEndToEndTest.cpp

@ -668,7 +668,7 @@ BOOST_AUTO_TEST_CASE(mapping_state)
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// voting without vote right shourd be rejected
// voting without vote right should be rejected
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
@ -2276,68 +2276,66 @@ BOOST_AUTO_TEST_CASE(store_bytes)
BOOST_AUTO_TEST_CASE(call_forward_bytes)
{
char const* sourceCode = R"(
contract C {
function save() {
savedData = msg.data;
}
function forward() {
this.call(savedData);
}
function clear() {
delete savedData;
}
function doubleIt(uint a) { val += a * 2; }
contract receiver {
uint public received;
function receive(uint x) { received += x + 1; }
function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData = msg.data; }
function forward() { rec.call(savedData); }
function clear() { delete savedData; }
function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData;
uint public val;
}
)";
compileAndRun(sourceCode);
FixedHash<4> innerHash(dev::sha3("doubleIt(uint256)"));
BOOST_CHECK(callContractFunction("save()", innerHash.asBytes(), 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(0));
compileAndRun(sourceCode, 0, "sender");
BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("forward()") == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("clear()") == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward()") == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
}
BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
{
char const* sourceCode = R"(
contract C {
function save() {
data1 = data2 = msg.data;
}
contract receiver {
uint public received;
function receive(uint x) { received += x + 1; }
function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData1 = savedData2 = msg.data; }
function forward(bool selector) {
if (selector)
{
this.call(data1);
delete data1;
}
else
{
this.call(data2);
delete data2;
}
if (selector) { rec.call(savedData1); delete savedData1; }
else { rec.call(savedData2); delete savedData2; }
}
function doubleIt(uint a) returns (uint b) { val += a * 2; }
bytes data1;
bytes data2;
uint public val;
function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData1;
bytes savedData2;
}
)";
compileAndRun(sourceCode);
FixedHash<4> innerHash(dev::sha3("doubleIt(uint256)"));
BOOST_CHECK(callContractFunction("save()", innerHash.asBytes(), 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(0));
compileAndRun(sourceCode, 0, "sender");
BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("forward(bool)", true) == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward(bool)", false) == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(28));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(16));
BOOST_CHECK(callContractFunction("forward(bool)", true) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
}
// TODO test that "delete" also clears the values
BOOST_AUTO_TEST_SUITE_END()
}

Loading…
Cancel
Save