mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Padding for ABI types.
This commit is contained in:
parent
396f638ce1
commit
fe16922087
13
Compiler.cpp
13
Compiler.cpp
@ -95,7 +95,7 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
|
|||||||
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
||||||
unsigned argumentSize = 0;
|
unsigned argumentSize = 0;
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
||||||
argumentSize += var->getType()->getCalldataEncodedSize();
|
argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
|
||||||
if (argumentSize > 0)
|
if (argumentSize > 0)
|
||||||
{
|
{
|
||||||
m_context << u256(argumentSize);
|
m_context << u256(argumentSize);
|
||||||
@ -159,9 +159,9 @@ 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."));
|
||||||
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
|
bool const leftAligned = var->getType()->getCategory() == Type::Category::STRING;
|
||||||
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
|
bool const padToWords = true;
|
||||||
dataOffset += numBytes;
|
dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory, padToWords);
|
||||||
}
|
}
|
||||||
return dataOffset;
|
return dataOffset;
|
||||||
}
|
}
|
||||||
@ -181,10 +181,11 @@ 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);
|
||||||
|
ExpressionCompiler::appendTypeConversion(m_context, paramType, paramType, true);
|
||||||
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
|
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
|
||||||
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
bool const padToWords = true;
|
||||||
|
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned, padToWords);
|
||||||
stackDepth -= paramType.getSizeOnStack();
|
stackDepth -= paramType.getSizeOnStack();
|
||||||
dataOffset += numBytes;
|
|
||||||
}
|
}
|
||||||
// note that the stack is not cleaned up here
|
// note that the stack is not cleaned up here
|
||||||
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
|
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
|
||||||
|
@ -33,17 +33,21 @@ namespace solidity
|
|||||||
|
|
||||||
const unsigned int CompilerUtils::dataStartOffset = 4;
|
const unsigned int CompilerUtils::dataStartOffset = 4;
|
||||||
|
|
||||||
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
|
unsigned CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned,
|
||||||
|
bool _fromCalldata, bool _padToWordBoundaries)
|
||||||
{
|
{
|
||||||
if (_bytes == 0)
|
if (_bytes == 0)
|
||||||
{
|
{
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
|
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
|
||||||
solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested.");
|
solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested.");
|
||||||
if (_bytes == 32)
|
if (_bytes == 32 || _padToWordBoundaries)
|
||||||
|
{
|
||||||
m_context << u256(_offset) << load;
|
m_context << u256(_offset) << load;
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
|
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||||
@ -54,21 +58,24 @@ void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _left
|
|||||||
m_context << u256(_offset) << load << eth::Instruction::DIV;
|
m_context << u256(_offset) << load << eth::Instruction::DIV;
|
||||||
if (_leftAligned)
|
if (_leftAligned)
|
||||||
m_context << eth::Instruction::MUL;
|
m_context << eth::Instruction::MUL;
|
||||||
|
return _bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
|
unsigned CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned,
|
||||||
|
bool _padToWordBoundaries)
|
||||||
{
|
{
|
||||||
if (_bytes == 0)
|
if (_bytes == 0)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
|
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
|
||||||
if (_bytes != 32 && !_leftAligned)
|
if (_bytes != 32 && !_leftAligned && !_padToWordBoundaries)
|
||||||
// shift the value accordingly before storing
|
// shift the value accordingly before storing
|
||||||
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
|
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
|
||||||
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
||||||
|
return _padToWordBoundaries ? 32 : _bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||||
|
@ -40,12 +40,23 @@ public:
|
|||||||
/// @param _bytes number of bytes to load
|
/// @param _bytes number of bytes to load
|
||||||
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
|
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
|
||||||
/// @param _fromCalldata if true, load from calldata, not from memory
|
/// @param _fromCalldata if true, load from calldata, not from memory
|
||||||
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
|
/// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries
|
||||||
|
/// @returns the number of bytes consumed in memory (can be different from _bytes if
|
||||||
|
/// _padToWordBoundaries is true)
|
||||||
|
unsigned loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false,
|
||||||
|
bool _fromCalldata = false, bool _padToWordBoundaries = false);
|
||||||
/// Stores data from stack in memory.
|
/// Stores data from stack in memory.
|
||||||
/// @param _offset offset in memory
|
/// @param _offset offset in memory
|
||||||
/// @param _bytes number of bytes to store
|
/// @param _bytes number of bytes to store
|
||||||
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
|
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
|
||||||
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
|
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
|
||||||
|
/// @returns the number of bytes written to memory (can be different from _bytes if
|
||||||
|
/// _padToWordBoundaries is true)
|
||||||
|
unsigned storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false,
|
||||||
|
bool _padToWordBoundaries = false);
|
||||||
|
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
|
||||||
|
/// padded calldata)
|
||||||
|
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
|
||||||
|
|
||||||
/// 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);
|
||||||
|
@ -41,11 +41,11 @@ void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression
|
|||||||
_expression.accept(compiler);
|
_expression.accept(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
|
void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
|
||||||
Type const& _typeOnStack, Type const& _targetType)
|
Type const& _targetType, bool _cleanupNeeded)
|
||||||
{
|
{
|
||||||
ExpressionCompiler compiler(_context);
|
ExpressionCompiler compiler(_context);
|
||||||
compiler.appendTypeConversion(_typeOnStack, _targetType);
|
compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExpressionCompiler::visit(Assignment const& _assignment)
|
bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||||
@ -295,7 +295,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
FunctionCallOptions options;
|
FunctionCallOptions options;
|
||||||
options.bare = true;
|
options.bare = true;
|
||||||
options.obtainAddress = [&]() { m_context << contractAddress; };
|
options.obtainAddress = [&]() { m_context << contractAddress; };
|
||||||
options.packDensely = false;
|
|
||||||
appendExternalFunctionCall(function, arguments, options);
|
appendExternalFunctionCall(function, arguments, options);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -327,15 +326,15 @@ bool ExpressionCompiler::visit(NewExpression const& _newExpression)
|
|||||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
{
|
{
|
||||||
arguments[i]->accept(*this);
|
arguments[i]->accept(*this);
|
||||||
appendTypeConversion(*arguments[i]->getType(), *types[i]);
|
appendTypeConversion(*arguments[i]->getType(), *types[i], true);
|
||||||
unsigned const numBytes = types[i]->getCalldataEncodedSize();
|
unsigned const numBytes = types[i]->getCalldataEncodedSize();
|
||||||
if (numBytes > 32)
|
if (numBytes > 32)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(arguments[i]->getLocation())
|
<< errinfo_sourceLocation(arguments[i]->getLocation())
|
||||||
<< errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
|
<< errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
|
||||||
bool const leftAligned = types[i]->getCategory() == Type::Category::STRING;
|
bool const leftAligned = types[i]->getCategory() == Type::Category::STRING;
|
||||||
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
bool const padToWords = true;
|
||||||
dataOffset += numBytes;
|
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned, padToWords);
|
||||||
}
|
}
|
||||||
// size, offset, endowment
|
// size, offset, endowment
|
||||||
m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
|
m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
|
||||||
@ -634,22 +633,20 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
|
|||||||
{
|
{
|
||||||
_arguments[i]->accept(*this);
|
_arguments[i]->accept(*this);
|
||||||
Type const& type = *_functionType.getParameterTypes()[i];
|
Type const& type = *_functionType.getParameterTypes()[i];
|
||||||
appendTypeConversion(*_arguments[i]->getType(), type);
|
appendTypeConversion(*_arguments[i]->getType(), type, true);
|
||||||
unsigned const numBytes = _options.packDensely ? type.getCalldataEncodedSize() : 32;
|
unsigned const numBytes = type.getCalldataEncodedSize();
|
||||||
if (numBytes == 0 || numBytes > 32)
|
if (numBytes == 0 || numBytes > 32)
|
||||||
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."));
|
||||||
bool const leftAligned = type.getCategory() == Type::Category::STRING;
|
bool const leftAligned = type.getCategory() == Type::Category::STRING;
|
||||||
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
bool const padToWords = true;
|
||||||
dataOffset += numBytes;
|
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned, padToWords);
|
||||||
}
|
}
|
||||||
//@todo only return the first return value for now
|
//@todo only return the first return value for now
|
||||||
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
|
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
|
||||||
_functionType.getReturnParameterTypes().front().get();
|
_functionType.getReturnParameterTypes().front().get();
|
||||||
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
|
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
|
||||||
if (!_options.packDensely && retSize > 0)
|
|
||||||
retSize = 32;
|
|
||||||
// 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);
|
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
|
||||||
if (_options.obtainValue)
|
if (_options.obtainValue)
|
||||||
@ -666,7 +663,7 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
|
|||||||
if (retSize > 0)
|
if (retSize > 0)
|
||||||
{
|
{
|
||||||
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
|
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
|
||||||
CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
|
CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,8 @@ public:
|
|||||||
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
|
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
|
||||||
|
|
||||||
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
|
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
|
||||||
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
|
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
|
||||||
|
Type const& _targetType, bool _cleanupNeeded = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
|
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
|
||||||
@ -96,9 +97,6 @@ private:
|
|||||||
std::function<void()> obtainValue;
|
std::function<void()> obtainValue;
|
||||||
/// If true, do not prepend function index to call data
|
/// If true, do not prepend function index to call data
|
||||||
bool bare = false;
|
bool bare = false;
|
||||||
/// If false, use calling convention that all arguments and return values are packed as
|
|
||||||
/// 32 byte values with padding.
|
|
||||||
bool packDensely = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Appends code to call a function of the given type with the given arguments.
|
/// Appends code to call a function of the given type with the given arguments.
|
||||||
|
3
Types.h
3
Types.h
@ -115,7 +115,8 @@ public:
|
|||||||
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
|
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
|
||||||
|
|
||||||
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
|
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
|
||||||
/// is not a simple big-endian encoding or the type cannot be stored on the stack.
|
/// is not a simple big-endian encoding or the type cannot be stored in calldata.
|
||||||
|
/// Note that irrespective of this size, each calldata element is padded to a multiple of 32 bytes.
|
||||||
virtual unsigned getCalldataEncodedSize() const { return 0; }
|
virtual unsigned getCalldataEncodedSize() const { return 0; }
|
||||||
/// @returns number of bytes required to hold this value in storage.
|
/// @returns number of bytes required to hold this value in storage.
|
||||||
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
|
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
|
||||||
|
Loading…
Reference in New Issue
Block a user