mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Helper functions to access memory.
This commit is contained in:
parent
130ff85e85
commit
373f0da267
17
Compiler.cpp
17
Compiler.cpp
@ -130,7 +130,6 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
|
|||||||
{
|
{
|
||||||
// We do not check the calldata size, everything is zero-padded.
|
// We do not check the calldata size, everything is zero-padded.
|
||||||
unsigned dataOffset = 1;
|
unsigned dataOffset = 1;
|
||||||
eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;
|
|
||||||
|
|
||||||
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
|
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
|
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
|
||||||
@ -140,14 +139,8 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
|
|||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(var->getLocation())
|
<< errinfo_sourceLocation(var->getLocation())
|
||||||
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
|
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
|
||||||
if (numBytes == 32)
|
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
|
||||||
m_context << u256(dataOffset) << load;
|
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
|
||||||
else if (var->getType()->getCategory() == Type::Category::STRING)
|
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::DUP1
|
|
||||||
<< u256(dataOffset) << load << eth::Instruction::DIV << eth::Instruction::MUL;
|
|
||||||
else
|
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
|
|
||||||
<< load << eth::Instruction::DIV;
|
|
||||||
dataOffset += numBytes;
|
dataOffset += numBytes;
|
||||||
}
|
}
|
||||||
return dataOffset;
|
return dataOffset;
|
||||||
@ -168,10 +161,8 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
|
|||||||
<< errinfo_sourceLocation(parameters[i]->getLocation())
|
<< errinfo_sourceLocation(parameters[i]->getLocation())
|
||||||
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
|
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
|
||||||
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
|
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
|
||||||
if (numBytes != 32)
|
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
|
||||||
if (paramType.getCategory() != Type::Category::STRING)
|
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
|
||||||
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
|
|
||||||
stackDepth -= paramType.getSizeOnStack();
|
stackDepth -= paramType.getSizeOnStack();
|
||||||
dataOffset += numBytes;
|
dataOffset += numBytes;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,36 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
|
||||||
|
{
|
||||||
|
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
|
||||||
|
if (asserts(0 < _bytes && _bytes <= 32))
|
||||||
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of 0 or more than 32 bytes requested."));
|
||||||
|
if (_bytes == 32)
|
||||||
|
m_context << u256(_offset) << load;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||||
|
u256 shiftFactor = u256(1) << ((32 - _bytes) * 8);
|
||||||
|
m_context << shiftFactor;
|
||||||
|
if (_leftAligned)
|
||||||
|
m_context << eth::Instruction::DUP1;
|
||||||
|
m_context << u256(_offset) << load << eth::Instruction::DIV;
|
||||||
|
if (_leftAligned)
|
||||||
|
m_context << eth::Instruction::MUL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
|
||||||
|
{
|
||||||
|
if (asserts(0 < _bytes && _bytes <= 32))
|
||||||
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of 0 or more than 32 bytes requested."));
|
||||||
|
if (_bytes != 32 && !_leftAligned)
|
||||||
|
// shift the value accordingly before storing
|
||||||
|
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
|
||||||
|
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||||
{
|
{
|
||||||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
||||||
|
@ -35,6 +35,18 @@ class CompilerUtils
|
|||||||
public:
|
public:
|
||||||
CompilerUtils(CompilerContext& _context): m_context(_context) {}
|
CompilerUtils(CompilerContext& _context): m_context(_context) {}
|
||||||
|
|
||||||
|
/// Loads data from memory to the stack.
|
||||||
|
/// @param _offset offset in memory (or calldata)
|
||||||
|
/// @param _bytes number of bytes to load
|
||||||
|
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
|
||||||
|
/// @param _fromCalldata if true, load from calldata, not from memory
|
||||||
|
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
|
||||||
|
/// Stores data from stack in memory.
|
||||||
|
/// @param _offset offset in memory
|
||||||
|
/// @param _bytes number of bytes to store
|
||||||
|
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
|
||||||
|
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
|
||||||
|
|
||||||
/// Moves the value that is at the top of the stack to a stack variable.
|
/// Moves the value that is at the top of the stack to a stack variable.
|
||||||
void moveToStackVariable(VariableDeclaration const& _variable);
|
void moveToStackVariable(VariableDeclaration const& _variable);
|
||||||
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
|
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
|
||||||
|
@ -225,15 +225,14 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(arguments[i]->getLocation())
|
<< errinfo_sourceLocation(arguments[i]->getLocation())
|
||||||
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
|
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
|
||||||
if (numBytes != 32)
|
bool const leftAligned = type.getCategory() == Type::Category::STRING;
|
||||||
if (type.getCategory() != Type::Category::STRING)
|
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
|
||||||
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
|
|
||||||
dataOffset += numBytes;
|
dataOffset += numBytes;
|
||||||
}
|
}
|
||||||
//@todo only return the first return value for now
|
//@todo only return the first return value for now
|
||||||
unsigned retSize = function.getReturnParameterTypes().empty() ? 0
|
Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr :
|
||||||
: function.getReturnParameterTypes().front()->getCalldataEncodedSize();
|
function.getReturnParameterTypes().front().get();
|
||||||
|
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
|
||||||
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
||||||
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
|
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
|
||||||
_functionCall.getExpression().accept(*this); // pushes addr and function index
|
_functionCall.getExpression().accept(*this); // pushes addr and function index
|
||||||
@ -241,17 +240,10 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
|
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
|
||||||
<< eth::Instruction::CALL
|
<< eth::Instruction::CALL
|
||||||
<< eth::Instruction::POP; // @todo do not ignore failure indicator
|
<< eth::Instruction::POP; // @todo do not ignore failure indicator
|
||||||
if (retSize == 32)
|
if (retSize > 0)
|
||||||
m_context << u256(0) << eth::Instruction::MLOAD;
|
|
||||||
else if (retSize > 0)
|
|
||||||
{
|
{
|
||||||
if (function.getReturnParameterTypes().front()->getCategory() == Type::Category::STRING)
|
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
|
||||||
m_context << (u256(1) << ((32 - retSize) * 8)) << eth::Instruction::DUP1
|
CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
|
||||||
<< u256(0) << eth::Instruction::MLOAD
|
|
||||||
<< eth::Instruction::DIV << eth::Instruction::MUL;
|
|
||||||
else
|
|
||||||
m_context << (u256(1) << ((32 - retSize) * 8))
|
|
||||||
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -275,7 +267,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
||||||
// @todo move this once we actually use memory
|
// @todo move this once we actually use memory
|
||||||
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
|
CompilerUtils(m_context).storeInMemory(0);
|
||||||
|
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
|
||||||
break;
|
break;
|
||||||
case Location::ECRECOVER:
|
case Location::ECRECOVER:
|
||||||
case Location::SHA256:
|
case Location::SHA256:
|
||||||
@ -291,13 +284,13 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
arguments[i]->accept(*this);
|
arguments[i]->accept(*this);
|
||||||
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
|
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
|
||||||
// @todo move this once we actually use memory
|
// @todo move this once we actually use memory
|
||||||
m_context << u256(i * 32) << eth::Instruction::MSTORE;
|
CompilerUtils(m_context).storeInMemory(i * 32);
|
||||||
}
|
}
|
||||||
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
|
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
|
||||||
<< contractAddress << u256(500) //@todo determine actual gas requirement
|
<< contractAddress << u256(500) //@todo determine actual gas requirement
|
||||||
<< eth::Instruction::CALL
|
<< eth::Instruction::CALL
|
||||||
<< eth::Instruction::POP
|
<< eth::Instruction::POP;
|
||||||
<< u256(0) << eth::Instruction::MLOAD;
|
CompilerUtils(m_context).loadFromMemory(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -381,7 +374,8 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
|
|||||||
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
|
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
|
||||||
true);
|
true);
|
||||||
// @todo move this once we actually use memory
|
// @todo move this once we actually use memory
|
||||||
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
|
CompilerUtils(m_context).storeInMemory(0);
|
||||||
|
CompilerUtils(m_context).storeInMemory(32);
|
||||||
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
|
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
|
||||||
|
|
||||||
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
|
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
|
||||||
|
Loading…
Reference in New Issue
Block a user