mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #130 from LefterisJP/dynamic_array_push
Dynamic array push
This commit is contained in:
commit
7ba42f4707
@ -623,6 +623,44 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
appendExternalFunctionCall(function, arguments);
|
appendExternalFunctionCall(function, arguments);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Location::ByteArrayPush:
|
||||||
|
case Location::ArrayPush:
|
||||||
|
{
|
||||||
|
_functionCall.expression().accept(*this);
|
||||||
|
solAssert(function.parameterTypes().size() == 1, "");
|
||||||
|
solAssert(!!function.parameterTypes()[0], "");
|
||||||
|
TypePointer const& paramType = function.parameterTypes()[0];
|
||||||
|
shared_ptr<ArrayType> arrayType =
|
||||||
|
function.location() == Location::ArrayPush ?
|
||||||
|
make_shared<ArrayType>(DataLocation::Storage, paramType) :
|
||||||
|
make_shared<ArrayType>(DataLocation::Storage);
|
||||||
|
// get the current length
|
||||||
|
ArrayUtils(m_context).retrieveLength(*arrayType);
|
||||||
|
m_context << eth::Instruction::DUP1;
|
||||||
|
// stack: ArrayReference currentLength currentLength
|
||||||
|
m_context << u256(1) << eth::Instruction::ADD;
|
||||||
|
// stack: ArrayReference currentLength newLength
|
||||||
|
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP2;
|
||||||
|
ArrayUtils(m_context).resizeDynamicArray(*arrayType);
|
||||||
|
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
|
||||||
|
// stack: newLength ArrayReference oldLength
|
||||||
|
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
||||||
|
|
||||||
|
// stack: newLength storageSlot slotOffset
|
||||||
|
arguments[0]->accept(*this);
|
||||||
|
// stack: newLength storageSlot slotOffset argValue
|
||||||
|
TypePointer type = arguments[0]->annotation().type;
|
||||||
|
utils().convertType(*type, *arrayType->baseType());
|
||||||
|
type = arrayType->baseType();
|
||||||
|
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||||
|
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||||
|
// stack: newLength argValue storageSlot slotOffset
|
||||||
|
if (function.location() == Location::ArrayPush)
|
||||||
|
StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
|
||||||
|
else
|
||||||
|
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
|
||||||
}
|
}
|
||||||
@ -784,26 +822,37 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
}
|
}
|
||||||
case Type::Category::Array:
|
case Type::Category::Array:
|
||||||
{
|
{
|
||||||
solAssert(member == "length", "Illegal array member.");
|
|
||||||
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
|
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
|
||||||
if (!type.isDynamicallySized())
|
if (member == "length")
|
||||||
{
|
{
|
||||||
utils().popStackElement(type);
|
if (!type.isDynamicallySized())
|
||||||
m_context << type.length();
|
{
|
||||||
|
utils().popStackElement(type);
|
||||||
|
m_context << type.length();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
switch (type.location())
|
||||||
|
{
|
||||||
|
case DataLocation::CallData:
|
||||||
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
||||||
|
break;
|
||||||
|
case DataLocation::Storage:
|
||||||
|
setLValue<StorageArrayLength>(_memberAccess, type);
|
||||||
|
break;
|
||||||
|
case DataLocation::Memory:
|
||||||
|
m_context << eth::Instruction::MLOAD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (member == "push")
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
type.isDynamicallySized() && type.location() == DataLocation::Storage,
|
||||||
|
"Tried to use .push() on a non-dynamically sized array"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
switch (type.location())
|
solAssert(false, "Illegal array member.");
|
||||||
{
|
|
||||||
case DataLocation::CallData:
|
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
|
||||||
break;
|
|
||||||
case DataLocation::Storage:
|
|
||||||
setLValue<StorageArrayLength>(_memberAccess, type);
|
|
||||||
break;
|
|
||||||
case DataLocation::Memory:
|
|
||||||
m_context << eth::Instruction::MLOAD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -858,6 +858,28 @@ string ArrayType::canonicalName(bool _addDataLocation) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemberList const& ArrayType::members() const
|
||||||
|
{
|
||||||
|
if (!m_members)
|
||||||
|
{
|
||||||
|
MemberList::MemberMap members;
|
||||||
|
if (!isString())
|
||||||
|
{
|
||||||
|
members.push_back({"length", make_shared<IntegerType>(256)});
|
||||||
|
if (isDynamicallySized() && location() == DataLocation::Storage)
|
||||||
|
members.push_back({"push", make_shared<FunctionType>(
|
||||||
|
TypePointers{baseType()},
|
||||||
|
TypePointers{make_shared<IntegerType>(256)},
|
||||||
|
strings{string()},
|
||||||
|
strings{string()},
|
||||||
|
isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush
|
||||||
|
)});
|
||||||
|
}
|
||||||
|
m_members.reset(new MemberList(members));
|
||||||
|
}
|
||||||
|
return *m_members;
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer ArrayType::encodingType() const
|
TypePointer ArrayType::encodingType() const
|
||||||
{
|
{
|
||||||
if (location() == DataLocation::Storage)
|
if (location() == DataLocation::Storage)
|
||||||
@ -913,8 +935,6 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer)
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberList ArrayType::s_arrayTypeMemberList({{"length", make_shared<IntegerType>(256)}});
|
|
||||||
|
|
||||||
bool ContractType::operator==(Type const& _other) const
|
bool ContractType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.category() != category())
|
if (_other.category() != category())
|
||||||
@ -1422,6 +1442,8 @@ unsigned FunctionType::sizeOnStack() const
|
|||||||
size = 1;
|
size = 1;
|
||||||
else if (location == Location::Internal)
|
else if (location == Location::Internal)
|
||||||
size = 1;
|
size = 1;
|
||||||
|
else if (location == Location::ArrayPush || location == Location::ByteArrayPush)
|
||||||
|
size = 1;
|
||||||
if (m_gasSet)
|
if (m_gasSet)
|
||||||
size++;
|
size++;
|
||||||
if (m_valueSet)
|
if (m_valueSet)
|
||||||
|
@ -505,10 +505,7 @@ public:
|
|||||||
virtual unsigned sizeOnStack() const override;
|
virtual unsigned sizeOnStack() const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual std::string canonicalName(bool _addDataLocation) const override;
|
virtual std::string canonicalName(bool _addDataLocation) const override;
|
||||||
virtual MemberList const& members() const override
|
virtual MemberList const& members() const override;
|
||||||
{
|
|
||||||
return isString() ? EmptyMemberList : s_arrayTypeMemberList;
|
|
||||||
}
|
|
||||||
virtual TypePointer encodingType() const override;
|
virtual TypePointer encodingType() const override;
|
||||||
virtual TypePointer decodingType() const override;
|
virtual TypePointer decodingType() const override;
|
||||||
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
||||||
@ -532,7 +529,8 @@ private:
|
|||||||
TypePointer m_baseType;
|
TypePointer m_baseType;
|
||||||
bool m_hasDynamicLength = true;
|
bool m_hasDynamicLength = true;
|
||||||
u256 m_length;
|
u256 m_length;
|
||||||
static const MemberList s_arrayTypeMemberList;
|
/// List of member types, will be lazy-initialized because of recursive references.
|
||||||
|
mutable std::unique_ptr<MemberList> m_members;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -736,7 +734,9 @@ public:
|
|||||||
Event, ///< syntactic sugar for LOG*
|
Event, ///< syntactic sugar for LOG*
|
||||||
SetGas, ///< modify the default gas value for the function call
|
SetGas, ///< modify the default gas value for the function call
|
||||||
SetValue, ///< modify the default value transfer for the function call
|
SetValue, ///< modify the default value transfer for the function call
|
||||||
BlockHash ///< BLOCKHASH
|
BlockHash, ///< BLOCKHASH
|
||||||
|
ArrayPush, ///< .push() to a dynamically sized array in storage
|
||||||
|
ByteArrayPush ///< .push() to a dynamically sized byte array in storage
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Category category() const override { return Category::Function; }
|
virtual Category category() const override { return Category::Function; }
|
||||||
|
@ -3452,6 +3452,7 @@ BOOST_AUTO_TEST_CASE(array_copy_target_leftover2)
|
|||||||
asString(fromHex("0000000000000000"))
|
asString(fromHex("0000000000000000"))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
|
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -3476,6 +3477,45 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
|
|||||||
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
|
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(array_push)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract c {
|
||||||
|
uint[] data;
|
||||||
|
function test() returns (uint x, uint y, uint z, uint l) {
|
||||||
|
data.push(5);
|
||||||
|
x = data[0];
|
||||||
|
data.push(4);
|
||||||
|
y = data[1];
|
||||||
|
l = data.push(3);
|
||||||
|
z = data[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs(5, 4, 3, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(byte_array_push)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract c {
|
||||||
|
bytes data;
|
||||||
|
function test() returns (bool x) {
|
||||||
|
if (data.push(5) != 1) return true;
|
||||||
|
if (data[0] != 5) return true;
|
||||||
|
data.push(4);
|
||||||
|
if (data[1] != 4) return true;
|
||||||
|
uint l = data.push(3);
|
||||||
|
if (data[2] != 3) return true;
|
||||||
|
if (l != 3) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(external_array_args)
|
BOOST_AUTO_TEST_CASE(external_array_args)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
Loading…
Reference in New Issue
Block a user