Merge pull request #130 from LefterisJP/dynamic_array_push

Dynamic array push
This commit is contained in:
chriseth
2015-10-15 17:37:15 +02:00
4 changed files with 135 additions and 24 deletions
+65 -16
View File
@@ -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();
if (!type.isDynamicallySized())
{
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
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;
}
solAssert(false, "Illegal array member.");
break;
}
default:
+24 -2
View File
@@ -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)
+6 -6
View File
@@ -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; }