solidity/libsolidity/CompilerUtils.cpp

738 lines
26 KiB
C++
Raw Normal View History

/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Routines used by both the compiler and the expression compiler.
*/
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/AST.h>
#include <libevmcore/Instruction.h>
#include <libevmcore/Params.h>
#include <libsolidity/ArrayUtils.h>
2015-06-23 12:55:33 +00:00
#include <libsolidity/LValue.h>
using namespace std;
namespace dev
{
namespace solidity
{
2015-06-05 15:32:13 +00:00
const unsigned CompilerUtils::dataStartOffset = 4;
const size_t CompilerUtils::freeMemoryPointer = 64;
const unsigned CompilerUtils::identityContractAddress = 4;
2015-06-05 15:32:13 +00:00
void CompilerUtils::initialiseFreeMemoryPointer()
{
m_context << u256(freeMemoryPointer + 32);
storeFreeMemoryPointer();
}
void CompilerUtils::fetchFreeMemoryPointer()
{
m_context << u256(freeMemoryPointer) << eth::Instruction::MLOAD;
}
void CompilerUtils::storeFreeMemoryPointer()
{
m_context << u256(freeMemoryPointer) << eth::Instruction::MSTORE;
}
void CompilerUtils::allocateMemory()
{
fetchFreeMemoryPointer();
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
storeFreeMemoryPointer();
}
void CompilerUtils::toSizeAfterFreeMemoryPointer()
{
fetchFreeMemoryPointer();
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::SUB;
m_context << eth::Instruction::SWAP1;
}
2015-03-05 14:41:39 +00:00
unsigned CompilerUtils::loadFromMemory(
unsigned _offset,
Type const& _type,
bool _fromCalldata,
bool _padToWordBoundaries
)
2014-12-10 16:15:17 +00:00
{
2015-08-31 16:44:29 +00:00
solAssert(_type.category() != Type::Category::Array, "Unable to statically load dynamic type.");
2015-02-15 00:02:38 +00:00
m_context << u256(_offset);
return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
}
2015-03-03 16:55:28 +00:00
void CompilerUtils::loadFromMemoryDynamic(
2015-03-05 14:41:39 +00:00
Type const& _type,
bool _fromCalldata,
bool _padToWordBoundaries,
bool _keepUpdatedMemoryOffset
)
{
2015-03-03 16:55:28 +00:00
if (_keepUpdatedMemoryOffset)
m_context << eth::Instruction::DUP1;
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
2015-03-03 16:55:28 +00:00
{
solAssert(!arrayType->isDynamicallySized(), "");
solAssert(!_fromCalldata, "");
solAssert(_padToWordBoundaries, "");
if (_keepUpdatedMemoryOffset)
m_context << arrayType->memorySize() << eth::Instruction::ADD;
}
else
{
unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
if (_keepUpdatedMemoryOffset)
{
// update memory counter
moveToStackTop(_type.sizeOnStack());
m_context << u256(numBytes) << eth::Instruction::ADD;
}
2015-03-03 16:55:28 +00:00
}
2014-12-10 16:15:17 +00:00
}
void CompilerUtils::storeInMemory(unsigned _offset)
2014-12-10 16:15:17 +00:00
{
unsigned numBytes = prepareMemoryStore(IntegerType(256), true);
2015-02-10 16:53:43 +00:00
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
{
solAssert(ref->location() == DataLocation::Memory, "");
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
}
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
{
m_context << eth::Instruction::DUP1;
storeStringData(bytesConstRef(str->value()));
if (_padToWordBoundaries)
m_context << u256(((str->value().size() + 31) / 32) * 32);
else
m_context << u256(str->value().size());
m_context << eth::Instruction::ADD;
}
2015-02-11 13:32:46 +00:00
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
{
solAssert(
2015-08-31 16:44:29 +00:00
_type.sizeOnStack() == 1,
"Memory store of types with stack size != 1 not implemented."
);
2015-02-11 13:32:46 +00:00
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
m_context << u256(numBytes) << eth::Instruction::ADD;
}
2014-12-11 13:19:11 +00:00
}
2014-12-10 16:15:17 +00:00
}
void CompilerUtils::encodeToMemory(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _padToWordBoundaries,
bool _copyDynamicDataInPlace
)
{
// stack: <v1> <v2> ... <vn> <mem>
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes)
t = t->mobileType()->encodingType();
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
// The values dyn_head_i are added during the first loop and they point to the head part
// of the ith dynamic parameter, which is filled once the dynamic parts are processed.
// store memory start pointer
m_context << eth::Instruction::DUP1;
2015-08-31 16:44:29 +00:00
unsigned argSize = CompilerUtils::sizeOnStack(_givenTypes);
unsigned stackPos = 0; // advances through the argument values
unsigned dynPointers = 0; // number of dynamic head pointers on the stack
for (size_t i = 0; i < _givenTypes.size(); ++i)
{
TypePointer targetType = targetTypes[i];
solAssert(!!targetType, "Externalable type expected.");
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
// leave end_of_mem as dyn head pointer
m_context << eth::Instruction::DUP1 << u256(32) << eth::Instruction::ADD;
dynPointers++;
}
else
{
2015-08-31 16:44:29 +00:00
copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->sizeOnStack());
solAssert(!!targetType, "Externalable type expected.");
TypePointer type = targetType;
2015-06-17 10:01:39 +00:00
if (
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
2015-08-31 16:44:29 +00:00
_givenTypes[i]->category() == Type::Category::StringLiteral
2015-06-17 10:01:39 +00:00
)
type = _givenTypes[i]; // delay conversion
else
convertType(*_givenTypes[i], *targetType, true);
if (auto arrayType = dynamic_cast<ArrayType const*>(type.get()))
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
else
storeInMemoryDynamic(*type, _padToWordBoundaries);
}
2015-08-31 16:44:29 +00:00
stackPos += _givenTypes[i]->sizeOnStack();
}
// now copy the dynamic part
// Stack: <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
stackPos = 0;
unsigned thisDynPointer = 0;
for (size_t i = 0; i < _givenTypes.size(); ++i)
{
TypePointer targetType = targetTypes[i];
solAssert(!!targetType, "Externalable type expected.");
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
// copy tail pointer (=mem_end - mem_start) to memory
m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2;
m_context << eth::Instruction::SUB;
m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer);
m_context << eth::Instruction::MSTORE;
// stack: ... <end_of_mem>
2015-08-31 16:44:29 +00:00
if (_givenTypes[i]->category() == Type::Category::StringLiteral)
{
auto const& strType = dynamic_cast<StringLiteralType const&>(*_givenTypes[i]);
m_context << u256(strType.value().size());
storeInMemoryDynamic(IntegerType(256), true);
// stack: ... <end_of_mem'>
storeInMemoryDynamic(strType, _padToWordBoundaries);
}
else
{
2015-08-31 16:44:29 +00:00
solAssert(_givenTypes[i]->category() == Type::Category::Array, "Unknown dynamic type.");
auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
// now copy the array
2015-08-31 16:44:29 +00:00
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.sizeOnStack());
// stack: ... <end_of_mem> <value...>
// copy length to memory
2015-08-31 16:44:29 +00:00
m_context << eth::dupInstruction(1 + arrayType.sizeOnStack());
ArrayUtils(m_context).retrieveLength(arrayType, 1);
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
storeInMemoryDynamic(IntegerType(256), true);
// stack: ... <end_of_mem> <value...> <end_of_mem''>
// copy the new memory pointer
2015-08-31 16:44:29 +00:00
m_context << eth::swapInstruction(arrayType.sizeOnStack() + 1) << eth::Instruction::POP;
// stack: ... <end_of_mem''> <value...>
// copy data part
ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
// stack: ... <end_of_mem'''>
}
thisDynPointer++;
}
2015-08-31 16:44:29 +00:00
stackPos += _givenTypes[i]->sizeOnStack();
}
// remove unneeded stack elements (and retain memory pointer)
m_context << eth::swapInstruction(argSize + dynPointers + 1);
popStackSlots(argSize + dynPointers + 1);
}
2015-06-23 12:55:33 +00:00
void CompilerUtils::memoryCopy()
{
// Stack here: size target source
// stack for call: outsize target size source value contract gas
//@TODO do not use ::CALL if less than 32 bytes?
m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1;
m_context << u256(0) << u256(identityContractAddress);
// compute gas costs
m_context << u256(32) << eth::Instruction::DUP5 << u256(31) << eth::Instruction::ADD;
m_context << eth::Instruction::DIV << u256(eth::c_identityWordGas) << eth::Instruction::MUL;
m_context << u256(eth::c_identityGas) << eth::Instruction::ADD;
m_context << eth::Instruction::CALL;
m_context << eth::Instruction::POP; // ignore return value
}
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations.
// @todo: store in the AST whether the operand might have "dirty" higher order bits
if (_typeOnStack == _targetType && !_cleanupNeeded)
return;
2015-08-31 16:44:29 +00:00
Type::Category stackTypeCategory = _typeOnStack.category();
Type::Category targetTypeCategory = _targetType.category();
switch (stackTypeCategory)
{
case Type::Category::FixedBytes:
{
FixedBytesType const& typeOnStack = dynamic_cast<FixedBytesType const&>(_typeOnStack);
if (targetTypeCategory == Type::Category::Integer)
{
// conversion from bytes to integer. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
2015-08-31 16:44:29 +00:00
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
else
{
// clear lower-order bytes for conversion to shorter bytes - we always clean
solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
FixedBytesType const& targetType = dynamic_cast<FixedBytesType const&>(_targetType);
if (targetType.numBytes() < typeOnStack.numBytes())
{
if (targetType.numBytes() == 0)
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
else
2015-06-16 09:28:35 +00:00
{
m_context << (u256(1) << (256 - targetType.numBytes() * 8));
2015-06-16 09:28:35 +00:00
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2;
m_context << eth::Instruction::DIV << eth::Instruction::MUL;
}
}
}
}
break;
case Type::Category::Enum:
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, "");
break;
case Type::Category::Integer:
case Type::Category::Contract:
case Type::Category::IntegerConstant:
if (targetTypeCategory == Type::Category::FixedBytes)
{
solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant,
"Invalid conversion to FixedBytesType requested.");
// conversion from bytes to string. no need to clean the high bit
// only to shift left because of opposite alignment
FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
2015-08-31 16:44:29 +00:00
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack);
m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << eth::Instruction::MUL;
}
else if (targetTypeCategory == Type::Category::Enum)
// just clean
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
else
{
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
IntegerType addressType(0, IntegerType::Modifier::Address);
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::IntegerConstant)
{
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
// We know that the stack is clean, we only have to clean for a narrowing conversion
// where cleanup is forced.
2015-08-31 16:44:29 +00:00
if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded)
cleanHigherOrderBits(targetType);
}
else
{
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
// Widening: clean up according to source type width
// Non-widening and force: clean up according to target type bits
2015-08-31 16:44:29 +00:00
if (targetType.numBits() > typeOnStack.numBits())
cleanHigherOrderBits(typeOnStack);
else if (_cleanupNeeded)
cleanHigherOrderBits(targetType);
}
}
break;
case Type::Category::StringLiteral:
{
auto const& literalType = dynamic_cast<StringLiteralType const&>(_typeOnStack);
string const& value = literalType.value();
bytesConstRef data(value);
if (targetTypeCategory == Type::Category::FixedBytes)
{
solAssert(data.size() <= 32, "");
m_context << h256::Arith(h256(data, h256::AlignLeft));
}
else if (targetTypeCategory == Type::Category::Array)
{
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
solAssert(arrayType.isByteArray(), "");
u256 storageSize(32 + ((data.size() + 31) / 32) * 32);
m_context << storageSize;
allocateMemory();
// stack: mempos
m_context << eth::Instruction::DUP1 << u256(data.size());
storeInMemoryDynamic(IntegerType(256));
// stack: mempos datapos
storeStringData(data);
break;
}
else
solAssert(
false,
"Invalid conversion from string literal to " + _targetType.toString(false) + " requested."
);
break;
}
case Type::Category::Array:
{
solAssert(targetTypeCategory == stackTypeCategory, "");
ArrayType const& typeOnStack = dynamic_cast<ArrayType const&>(_typeOnStack);
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_targetType);
switch (targetType.location())
{
2015-06-17 10:01:39 +00:00
case DataLocation::Storage:
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
solAssert(
(targetType.isPointer() || (typeOnStack.isByteArray() && targetType.isByteArray())) &&
2015-06-17 10:01:39 +00:00
typeOnStack.location() == DataLocation::Storage,
"Invalid conversion to storage type."
);
break;
2015-06-17 10:01:39 +00:00
case DataLocation::Memory:
{
// Copy the array to a free position in memory, unless it is already in memory.
2015-06-17 10:01:39 +00:00
if (typeOnStack.location() != DataLocation::Memory)
{
// stack: <source ref> (variably sized)
2015-08-31 16:44:29 +00:00
unsigned stackSize = typeOnStack.sizeOnStack();
ArrayUtils(m_context).retrieveLength(typeOnStack);
// allocate memory
// stack: <source ref> (variably sized) <length>
m_context << eth::Instruction::DUP1;
ArrayUtils(m_context).convertLengthToSize(targetType, true);
// stack: <source ref> (variably sized) <length> <size>
if (targetType.isDynamicallySized())
m_context << u256(0x20) << eth::Instruction::ADD;
allocateMemory();
// stack: <source ref> (variably sized) <length> <mem start>
m_context << eth::Instruction::DUP1;
moveIntoStack(2 + stackSize);
if (targetType.isDynamicallySized())
{
m_context << eth::Instruction::DUP2;
storeInMemoryDynamic(IntegerType(256));
}
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos>
2015-08-31 16:44:29 +00:00
if (targetType.baseType()->isValueType())
2015-06-17 10:01:39 +00:00
{
2015-08-31 16:44:29 +00:00
solAssert(typeOnStack.baseType()->isValueType(), "");
copyToStackTop(2 + stackSize, stackSize);
ArrayUtils(m_context).copyArrayToMemory(typeOnStack);
2015-06-17 10:01:39 +00:00
}
else
{
m_context << u256(0) << eth::Instruction::SWAP1;
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
auto repeat = m_context.newTag();
m_context << repeat;
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
m_context << eth::Instruction::LT << eth::Instruction::ISZERO;
auto loopEnd = m_context.appendConditionalJump();
copyToStackTop(3 + stackSize, stackSize);
copyToStackTop(2 + stackSize, 1);
2015-06-26 18:27:56 +00:00
ArrayUtils(m_context).accessIndex(typeOnStack, false);
if (typeOnStack.location() == DataLocation::Storage)
2015-08-31 16:44:29 +00:00
StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true);
convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded);
storeInMemoryDynamic(*targetType.baseType(), true);
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP1;
m_context.appendJumpTo(repeat);
m_context << loopEnd;
m_context << eth::Instruction::POP;
}
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos updated>
popStackSlots(2 + stackSize);
// Stack: <mem start>
}
break;
}
case DataLocation::CallData:
solAssert(
targetType.isByteArray() &&
typeOnStack.isByteArray() &&
typeOnStack.location() == DataLocation::CallData,
"Invalid conversion to calldata type.");
break;
default:
2015-06-25 15:22:28 +00:00
solAssert(
false,
2015-06-24 15:25:36 +00:00
"Invalid type conversion " +
_typeOnStack.toString(false) +
" to " +
_targetType.toString(false) +
" requested."
);
}
break;
}
case Type::Category::Struct:
{
solAssert(targetTypeCategory == stackTypeCategory, "");
auto& targetType = dynamic_cast<StructType const&>(_targetType);
2015-06-30 09:54:51 +00:00
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
solAssert(
2015-06-30 09:54:51 +00:00
targetType.location() != DataLocation::CallData &&
typeOnStack.location() != DataLocation::CallData
, "");
switch (targetType.location())
{
case DataLocation::Storage:
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
solAssert(
targetType.isPointer() &&
typeOnStack.location() == DataLocation::Storage,
"Invalid conversion to storage type."
);
break;
case DataLocation::Memory:
// Copy the array to a free position in memory, unless it is already in memory.
if (typeOnStack.location() != DataLocation::Memory)
{
solAssert(typeOnStack.location() == DataLocation::Storage, "");
// stack: <source ref>
2015-06-30 09:54:51 +00:00
m_context << typeOnStack.memorySize();
allocateMemory();
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
// stack: <memory ptr> <source ref> <memory ptr>
2015-08-31 16:44:29 +00:00
for (auto const& member: typeOnStack.members())
2015-06-30 09:54:51 +00:00
{
if (!member.type->canLiveOutsideStorage())
continue;
2015-08-31 16:44:29 +00:00
pair<u256, unsigned> const& offsets = typeOnStack.storageOffsetsOfMember(member.name);
2015-06-30 09:54:51 +00:00
m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << u256(offsets.second);
StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true);
2015-08-31 16:44:29 +00:00
TypePointer targetMemberType = targetType.memberType(member.name);
2015-06-30 09:54:51 +00:00
solAssert(!!targetMemberType, "Member not found in target type.");
convertType(*member.type, *targetMemberType, true);
storeInMemoryDynamic(*targetMemberType, true);
}
m_context << eth::Instruction::POP << eth::Instruction::POP;
}
break;
case DataLocation::CallData:
2015-07-03 16:15:05 +00:00
solAssert(false, "Invalid type conversion target location CallData.");
2015-06-30 09:54:51 +00:00
break;
}
break;
}
default:
// All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
break;
}
}
void CompilerUtils::pushZeroValue(const Type& _type)
{
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
if (!referenceType || referenceType->location() == DataLocation::Storage)
{
2015-08-31 16:44:29 +00:00
for (size_t i = 0; i < _type.sizeOnStack(); ++i)
m_context << u256(0);
return;
}
solAssert(referenceType->location() == DataLocation::Memory, "");
2015-08-31 16:44:29 +00:00
m_context << u256(max(32u, _type.calldataEncodedSize()));
allocateMemory();
m_context << eth::Instruction::DUP1;
if (auto structType = dynamic_cast<StructType const*>(&_type))
2015-08-31 16:44:29 +00:00
for (auto const& member: structType->members())
{
pushZeroValue(*member.type);
storeInMemoryDynamic(*member.type);
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
{
if (arrayType->isDynamicallySized())
{
// zero length
m_context << u256(0);
storeInMemoryDynamic(IntegerType(256));
}
2015-08-31 16:44:29 +00:00
else if (arrayType->length() > 0)
{
2015-08-31 16:44:29 +00:00
m_context << arrayType->length() << eth::Instruction::SWAP1;
// stack: items_to_do memory_pos
auto repeat = m_context.newTag();
m_context << repeat;
2015-08-31 16:44:29 +00:00
pushZeroValue(*arrayType->baseType());
storeInMemoryDynamic(*arrayType->baseType());
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1;
m_context << eth::Instruction::SUB << eth::Instruction::SWAP1;
m_context << eth::Instruction::DUP2;
m_context.appendConditionalJumpTo(repeat);
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
}
}
else
solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
// remove the updated memory pointer
m_context << eth::Instruction::POP;
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
2015-08-31 16:44:29 +00:00
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
unsigned const size = _variable.annotation().type->sizeOnStack();
2015-03-13 12:14:51 +00:00
solAssert(stackPosition >= size, "Variable size and position mismatch.");
2014-12-10 13:33:30 +00:00
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
2015-08-31 16:44:29 +00:00
errinfo_sourceLocation(_variable.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < size; ++i)
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
}
2015-03-03 16:55:28 +00:00
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
{
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
2015-03-03 16:55:28 +00:00
for (unsigned i = 0; i < _itemSize; ++i)
m_context << eth::dupInstruction(_stackDepth);
}
2015-04-21 08:59:48 +00:00
void CompilerUtils::moveToStackTop(unsigned _stackDepth)
{
solAssert(_stackDepth <= 15, "Stack too deep, try removing local variables.");
2015-04-21 08:59:48 +00:00
for (unsigned i = 0; i < _stackDepth; ++i)
m_context << eth::swapInstruction(1 + i);
}
void CompilerUtils::moveIntoStack(unsigned _stackDepth)
{
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
for (unsigned i = _stackDepth; i > 0; --i)
m_context << eth::swapInstruction(i);
}
void CompilerUtils::popStackElement(Type const& _type)
{
2015-08-31 16:44:29 +00:00
popStackSlots(_type.sizeOnStack());
}
void CompilerUtils::popStackSlots(size_t _amount)
{
for (size_t i = 0; i < _amount; ++i)
m_context << eth::Instruction::POP;
}
2015-08-31 16:44:29 +00:00
unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
{
unsigned size = 0;
for (shared_ptr<Type const> const& type: _variableTypes)
2015-08-31 16:44:29 +00:00
size += type->sizeOnStack();
return size;
}
void CompilerUtils::computeHashStatic()
2015-02-11 13:24:34 +00:00
{
storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
2015-02-11 13:24:34 +00:00
}
void CompilerUtils::storeStringData(bytesConstRef _data)
{
//@todo provide both alternatives to the optimiser
// stack: mempos
if (_data.size() <= 128)
{
for (unsigned i = 0; i < _data.size(); i += 32)
{
m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft));
storeInMemoryDynamic(IntegerType(256));
}
m_context << eth::Instruction::POP;
}
else
{
// stack: mempos mempos_data
m_context.appendData(_data.toBytes());
m_context << u256(_data.size()) << eth::Instruction::SWAP2;
m_context << eth::Instruction::CODECOPY;
}
}
2015-02-15 00:02:38 +00:00
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{
2015-08-31 16:44:29 +00:00
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
bool leftAligned = _type.category() == Type::Category::FixedBytes;
2015-02-15 00:02:38 +00:00
if (numBytes == 0)
m_context << eth::Instruction::POP << u256(0);
else
{
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
m_context << (_fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD);
if (numBytes != 32)
{
// add leading or trailing zeros by dividing/multiplying depending on alignment
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
m_context << shiftFactor << eth::Instruction::SWAP1 << eth::Instruction::DIV;
if (leftAligned)
m_context << shiftFactor << eth::Instruction::MUL;
}
}
return numBytes;
}
void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
{
2015-08-31 16:44:29 +00:00
if (_typeOnStack.numBits() == 256)
return;
else if (_typeOnStack.isSigned())
2015-08-31 16:44:29 +00:00
m_context << u256(_typeOnStack.numBits() / 8 - 1) << eth::Instruction::SIGNEXTEND;
else
2015-08-31 16:44:29 +00:00
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << eth::Instruction::AND;
}
2015-02-11 13:32:46 +00:00
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
2015-02-10 16:53:43 +00:00
{
2015-08-31 16:44:29 +00:00
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
bool leftAligned = _type.category() == Type::Category::FixedBytes;
2015-02-10 16:53:43 +00:00
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;
}
}
}