aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/ExpressionCompiler.cpp81
-rw-r--r--libsolidity/Types.cpp26
-rw-r--r--libsolidity/Types.h12
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp40
4 files changed, 135 insertions, 24 deletions
diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp
index f6eed0de..85302afc 100644
--- a/libsolidity/ExpressionCompiler.cpp
+++ b/libsolidity/ExpressionCompiler.cpp
@@ -623,6 +623,44 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
appendExternalFunctionCall(function, arguments);
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:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
}
@@ -784,26 +822,37 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
}
case Type::Category::Array:
{
- solAssert(member == "length", "Illegal array member.");
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
- if (!type.isDynamicallySized())
+ if (member == "length")
{
- utils().popStackElement(type);
- m_context << type.length();
- }
- else
- switch (type.location())
+ if (!type.isDynamicallySized())
{
- 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;
+ 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
+ solAssert(false, "Illegal array member.");
break;
}
default:
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index 3ccaaf5d..51df5fbf 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -858,6 +858,28 @@ string ArrayType::canonicalName(bool _addDataLocation) const
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
{
if (location() == DataLocation::Storage)
@@ -913,8 +935,6 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer)
return copy;
}
-const MemberList ArrayType::s_arrayTypeMemberList({{"length", make_shared<IntegerType>(256)}});
-
bool ContractType::operator==(Type const& _other) const
{
if (_other.category() != category())
@@ -1422,6 +1442,8 @@ unsigned FunctionType::sizeOnStack() const
size = 1;
else if (location == Location::Internal)
size = 1;
+ else if (location == Location::ArrayPush || location == Location::ByteArrayPush)
+ size = 1;
if (m_gasSet)
size++;
if (m_valueSet)
diff --git a/libsolidity/Types.h b/libsolidity/Types.h
index e73cd3cd..5c4aacdc 100644
--- a/libsolidity/Types.h
+++ b/libsolidity/Types.h
@@ -505,10 +505,7 @@ public:
virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
- virtual MemberList const& members() const override
- {
- return isString() ? EmptyMemberList : s_arrayTypeMemberList;
- }
+ virtual MemberList const& members() const override;
virtual TypePointer encodingType() const override;
virtual TypePointer decodingType() const override;
virtual TypePointer interfaceType(bool _inLibrary) const override;
@@ -532,7 +529,8 @@ private:
TypePointer m_baseType;
bool m_hasDynamicLength = true;
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*
SetGas, ///< modify the default gas value 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; }
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index cccca0ba..bee19010 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -3452,6 +3452,7 @@ BOOST_AUTO_TEST_CASE(array_copy_target_leftover2)
asString(fromHex("0000000000000000"))
));
}
+
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
{
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_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)
{
char const* sourceCode = R"(