mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add push() for dynamic storage arrays
This commit is contained in:
@@ -1889,7 +1889,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
FunctionCallAnnotation& funcCallAnno = _functionCall.annotation();
|
||||
FunctionTypePointer functionType = nullptr;
|
||||
|
||||
// Determine and assign function call kind, purity and function type for this FunctionCall node
|
||||
// Determine and assign function call kind, lvalue, purity and function type for this FunctionCall node
|
||||
switch (expressionType->category())
|
||||
{
|
||||
case Type::Category::Function:
|
||||
@@ -1903,6 +1903,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
functionType &&
|
||||
functionType->isPure();
|
||||
|
||||
if (
|
||||
functionType->kind() == FunctionType::Kind::ArrayPush ||
|
||||
functionType->kind() == FunctionType::Kind::ByteArrayPush
|
||||
)
|
||||
funcCallAnno.isLValue = functionType->parameterTypes().empty();
|
||||
|
||||
break;
|
||||
|
||||
case Type::Category::TypeType:
|
||||
@@ -2625,7 +2631,10 @@ void TypeChecker::requireLValue(Expression const& _expression)
|
||||
return "Calldata structs are read-only.";
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
||||
if (memberAccess->memberName() == "length")
|
||||
if (
|
||||
memberAccess->memberName() == "length" ||
|
||||
memberAccess->memberName() == "push"
|
||||
)
|
||||
switch (arrayType->location())
|
||||
{
|
||||
case DataLocation::Memory:
|
||||
|
||||
@@ -1781,10 +1781,17 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
|
||||
if (isDynamicallySized() && location() == DataLocation::Storage)
|
||||
{
|
||||
members.emplace_back("push", TypeProvider::function(
|
||||
TypePointers{},
|
||||
TypePointers{baseType()},
|
||||
TypePointers{TypeProvider::uint256()},
|
||||
strings{},
|
||||
strings{string()},
|
||||
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
|
||||
));
|
||||
members.emplace_back("push", TypeProvider::function(
|
||||
TypePointers{baseType()},
|
||||
TypePointers{},
|
||||
strings{string()},
|
||||
strings{},
|
||||
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
|
||||
));
|
||||
members.emplace_back("pop", TypeProvider::function(
|
||||
|
||||
@@ -842,41 +842,66 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ArrayPush:
|
||||
{
|
||||
_functionCall.expression().accept(*this);
|
||||
solAssert(function.parameterTypes().size() == 1, "");
|
||||
solAssert(!!function.parameterTypes()[0], "");
|
||||
TypePointer paramType = function.parameterTypes()[0];
|
||||
ArrayType const* arrayType =
|
||||
function.kind() == FunctionType::Kind::ArrayPush ?
|
||||
TypeProvider::array(DataLocation::Storage, paramType) :
|
||||
TypeProvider::array(DataLocation::Storage);
|
||||
|
||||
// stack: ArrayReference
|
||||
arguments[0]->accept(*this);
|
||||
TypePointer const& argType = arguments[0]->annotation().type;
|
||||
// stack: ArrayReference argValue
|
||||
utils().moveToStackTop(argType->sizeOnStack(), 1);
|
||||
// stack: argValue ArrayReference
|
||||
m_context << Instruction::DUP1;
|
||||
ArrayUtils(m_context).incrementDynamicArraySize(*arrayType);
|
||||
// stack: argValue ArrayReference newLength
|
||||
m_context << Instruction::SWAP1;
|
||||
// stack: argValue newLength ArrayReference
|
||||
m_context << u256(1) << Instruction::DUP3 << Instruction::SUB;
|
||||
// stack: argValue newLength ArrayReference (newLength-1)
|
||||
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
||||
// stack: argValue newLength storageSlot slotOffset
|
||||
utils().moveToStackTop(3, argType->sizeOnStack());
|
||||
// stack: newLength storageSlot slotOffset argValue
|
||||
TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
|
||||
solAssert(type, "");
|
||||
utils().convertType(*argType, *type);
|
||||
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||
// stack: newLength argValue storageSlot slotOffset
|
||||
if (function.kind() == FunctionType::Kind::ArrayPush)
|
||||
StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
|
||||
if (function.parameterTypes().size() == 0)
|
||||
{
|
||||
auto paramType = function.returnParameterTypes().at(0);
|
||||
solAssert(paramType, "");
|
||||
|
||||
ArrayType const* arrayType =
|
||||
function.kind() == FunctionType::Kind::ArrayPush ?
|
||||
TypeProvider::array(DataLocation::Storage, paramType) :
|
||||
TypeProvider::bytesStorage();
|
||||
|
||||
// stack: ArrayReference
|
||||
m_context << u256(1) << Instruction::DUP2;
|
||||
ArrayUtils(m_context).incrementDynamicArraySize(*arrayType);
|
||||
// stack: ArrayReference 1 newLength
|
||||
m_context << Instruction::SUB;
|
||||
// stack: ArrayReference (newLength-1)
|
||||
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
||||
|
||||
if (arrayType->isByteArray())
|
||||
setLValue<StorageByteArrayElement>(_functionCall);
|
||||
else
|
||||
setLValueToStorageItem(_functionCall);
|
||||
}
|
||||
else
|
||||
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
|
||||
{
|
||||
solAssert(function.parameterTypes().size() == 1, "");
|
||||
solAssert(!!function.parameterTypes()[0], "");
|
||||
TypePointer paramType = function.parameterTypes()[0];
|
||||
ArrayType const* arrayType =
|
||||
function.kind() == FunctionType::Kind::ArrayPush ?
|
||||
TypeProvider::array(DataLocation::Storage, paramType) :
|
||||
TypeProvider::bytesStorage();
|
||||
|
||||
// stack: ArrayReference
|
||||
arguments[0]->accept(*this);
|
||||
TypePointer const& argType = arguments[0]->annotation().type;
|
||||
// stack: ArrayReference argValue
|
||||
utils().moveToStackTop(argType->sizeOnStack(), 1);
|
||||
// stack: argValue ArrayReference
|
||||
m_context << Instruction::DUP1;
|
||||
ArrayUtils(m_context).incrementDynamicArraySize(*arrayType);
|
||||
// stack: argValue ArrayReference newLength
|
||||
m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB;
|
||||
// stack: argValue ArrayReference (newLength-1)
|
||||
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
||||
// stack: argValue storageSlot slotOffset
|
||||
utils().moveToStackTop(2, argType->sizeOnStack());
|
||||
// stack: storageSlot slotOffset argValue
|
||||
TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
|
||||
solAssert(type, "");
|
||||
utils().convertType(*argType, *type);
|
||||
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||
// stack: argValue storageSlot slotOffset
|
||||
if (function.kind() == FunctionType::Kind::ArrayPush)
|
||||
StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
|
||||
else
|
||||
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::ArrayPop:
|
||||
|
||||
Reference in New Issue
Block a user