mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
External functions in storage.
This commit is contained in:
parent
6f19559de0
commit
95d7555e3c
@ -139,8 +139,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
|
solAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
|
||||||
m_context << u256(0xffffffffUL) << Instruction::AND << (u256(1) << 160) << Instruction::MUL << Instruction::SWAP1;
|
combineExternalFunctionType();
|
||||||
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::OR;
|
|
||||||
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD;
|
m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
@ -316,6 +315,21 @@ void CompilerUtils::memoryCopy()
|
|||||||
m_context << Instruction::POP; // ignore return value
|
m_context << Instruction::POP; // ignore return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::splitExternalFunctionType()
|
||||||
|
{
|
||||||
|
// We have to split the right-aligned <function identifier><address> into two stack slots:
|
||||||
|
// address (right aligned), function identifier (right aligned)
|
||||||
|
m_context << Instruction::DUP1 << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
|
||||||
|
m_context << (u256(1) << 160) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
|
m_context << u256(0xffffffffUL) << Instruction::AND;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::combineExternalFunctionType()
|
||||||
|
{
|
||||||
|
m_context << u256(0xffffffffUL) << Instruction::AND << (u256(1) << 160) << Instruction::MUL << Instruction::SWAP1;
|
||||||
|
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::OR;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
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
|
// For a type extension, we need to remove all higher-order bits that we might have ignored in
|
||||||
@ -860,16 +874,8 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
{
|
|
||||||
if (funType->location() == FunctionType::Location::External)
|
if (funType->location() == FunctionType::Location::External)
|
||||||
{
|
splitExternalFunctionType();
|
||||||
// We have to split the right-aligned <function identifier><address> into two stack slots:
|
|
||||||
// address (right aligned), function identifier (right aligned)
|
|
||||||
m_context << Instruction::DUP1 << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
|
|
||||||
m_context << (u256(1) << 160) << Instruction::SWAP1 << Instruction::DIV;
|
|
||||||
m_context << u256(0xffffffffUL) << Instruction::AND;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numBytes;
|
return numBytes;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,13 @@ public:
|
|||||||
/// Stack post:
|
/// Stack post:
|
||||||
void memoryCopy();
|
void memoryCopy();
|
||||||
|
|
||||||
|
/// Converts the combined and right-aligned external function type
|
||||||
|
/// <function identifier><address> into two stack slots:
|
||||||
|
/// address (right aligned), function identifier (right aligned)
|
||||||
|
void splitExternalFunctionType();
|
||||||
|
/// Performs the opposite operation of splitExternalFunctionType()
|
||||||
|
void combineExternalFunctionType();
|
||||||
|
|
||||||
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
|
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
|
||||||
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
|
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
|
||||||
/// if a reference type is converted from calldata or storage to memory.
|
/// if a reference type is converted from calldata or storage to memory.
|
||||||
|
@ -153,7 +153,8 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
|||||||
{
|
{
|
||||||
if (m_dataType->isValueType())
|
if (m_dataType->isValueType())
|
||||||
{
|
{
|
||||||
solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
|
if (m_dataType->category() != Type::Category::Function)
|
||||||
|
solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
|
||||||
solAssert(m_dataType->storageSize() == 1, "Invalid storage size.");
|
solAssert(m_dataType->storageSize() == 1, "Invalid storage size.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,8 +190,16 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
||||||
)
|
)
|
||||||
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
||||||
|
else if (
|
||||||
|
m_dataType->category() == Type::Category::Function &&
|
||||||
|
dynamic_cast<FunctionType const&>(*m_dataType).location() == FunctionType::Location::External
|
||||||
|
)
|
||||||
|
CompilerUtils(m_context).splitExternalFunctionType();
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
solAssert(m_dataType->sizeOnStack() == 1, "");
|
||||||
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
|
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +213,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size.");
|
solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
if (m_dataType->storageBytes() == 32)
|
if (m_dataType->storageBytes() == 32)
|
||||||
{
|
{
|
||||||
|
solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size.");
|
||||||
// offset should be zero
|
// offset should be zero
|
||||||
m_context << Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
if (!_move)
|
if (!_move)
|
||||||
@ -222,16 +232,23 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
m_context
|
m_context
|
||||||
<< Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
<< Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
||||||
<< Instruction::MUL;
|
<< Instruction::MUL;
|
||||||
m_context << Instruction::NOT << Instruction::AND;
|
m_context << Instruction::NOT << Instruction::AND << Instruction::SWAP1;
|
||||||
// stack: value storage_ref multiplier cleared_value
|
// stack: value storage_ref cleared_value multiplier
|
||||||
m_context
|
utils.copyToStackTop(4, m_dataType->sizeOnStack());
|
||||||
<< Instruction::SWAP1 << Instruction::DUP4;
|
|
||||||
// stack: value storage_ref cleared_value multiplier value
|
// stack: value storage_ref cleared_value multiplier value
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (
|
||||||
|
m_dataType->category() == Type::Category::Function &&
|
||||||
|
dynamic_cast<FunctionType const&>(*m_dataType).location() == FunctionType::Location::External
|
||||||
|
)
|
||||||
|
// Combine the two-item function type into a single stack slot.
|
||||||
|
utils.combineExternalFunctionType();
|
||||||
|
else if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
m_context
|
m_context
|
||||||
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
|
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
|
||||||
<< Instruction::SWAP1 << Instruction::DIV;
|
<< Instruction::SWAP1 << Instruction::DIV;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size for opaque type.");
|
||||||
// remove the higher order bits
|
// remove the higher order bits
|
||||||
m_context
|
m_context
|
||||||
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
|
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
|
||||||
@ -239,11 +256,12 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
<< Instruction::DUP2
|
<< Instruction::DUP2
|
||||||
<< Instruction::MUL
|
<< Instruction::MUL
|
||||||
<< Instruction::DIV;
|
<< Instruction::DIV;
|
||||||
|
}
|
||||||
m_context << Instruction::MUL << Instruction::OR;
|
m_context << Instruction::MUL << Instruction::OR;
|
||||||
// stack: value storage_ref updated_value
|
// stack: value storage_ref updated_value
|
||||||
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << Instruction::POP;
|
utils.popStackElement(*m_dataType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -7695,7 +7695,33 @@ BOOST_AUTO_TEST_CASE(store_function)
|
|||||||
BOOST_CHECK(callContractFunction("t()") == encodeArgs(u256(9)));
|
BOOST_CHECK(callContractFunction("t()") == encodeArgs(u256(9)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: arrays, libraries
|
BOOST_AUTO_TEST_CASE(function_type_library_internal)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
library Utils {
|
||||||
|
function reduce(uint[] memory array, function(uint, uint) returns (uint) f, uint init) internal returns (uint) {
|
||||||
|
for (uint i = 0; i < array.length; i++) {
|
||||||
|
init = f(array[i], init);
|
||||||
|
}
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
function sum(uint a, uint b) internal returns (uint) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function f(uint[] x) returns (uint) {
|
||||||
|
return Utils.reduce(x, Utils.sum, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256[])", 0x20, 3, u256(1), u256(7), u256(3)) == encodeArgs(u256(11)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: arrays, libraries with external functions
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(shift_constant_left)
|
BOOST_AUTO_TEST_CASE(shift_constant_left)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user