mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Code generator for function types.
This commit is contained in:
parent
cc8583ec7d
commit
dd173f83e3
@ -2065,6 +2065,16 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer FunctionType::encodingType() const
|
||||||
|
{
|
||||||
|
// Only external functions can be encoded, internal functions cannot leave code boundaries.
|
||||||
|
if (m_location == Location::External)
|
||||||
|
// This looks like bytes24, but bytes24 is stored differently on the stack.
|
||||||
|
return shared_from_this();
|
||||||
|
else
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer FunctionType::interfaceType(bool _inLibrary) const
|
TypePointer FunctionType::interfaceType(bool _inLibrary) const
|
||||||
{
|
{
|
||||||
if (m_location != Location::External && m_location != Location::Internal)
|
if (m_location != Location::External && m_location != Location::Internal)
|
||||||
@ -2072,7 +2082,7 @@ TypePointer FunctionType::interfaceType(bool _inLibrary) const
|
|||||||
if (_inLibrary)
|
if (_inLibrary)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
else
|
else
|
||||||
return make_shared<FixedBytesType>(storageBytes());
|
return make_shared<IntegerType>(8 * storageBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
|
bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
|
||||||
|
@ -901,6 +901,7 @@ public:
|
|||||||
virtual bool canLiveOutsideStorage() const override { return m_location == Location::Internal || m_location == Location::External; }
|
virtual bool canLiveOutsideStorage() const override { return m_location == Location::Internal || m_location == Location::External; }
|
||||||
virtual unsigned sizeOnStack() const override;
|
virtual unsigned sizeOnStack() const override;
|
||||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
|
virtual TypePointer encodingType() const override;
|
||||||
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
||||||
|
|
||||||
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
|
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
|
||||||
|
@ -133,6 +133,17 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
m_context << u256(str->value().size());
|
m_context << u256(str->value().size());
|
||||||
m_context << Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
}
|
}
|
||||||
|
else if (
|
||||||
|
_type.category() == Type::Category::Function &&
|
||||||
|
dynamic_cast<FunctionType const&>(_type).location() == FunctionType::Location::External
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
|
||||||
|
m_context << u256(0xffffffffUL) << Instruction::AND << (u256(1) << 160) << Instruction::MUL << Instruction::SWAP1;
|
||||||
|
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::OR;
|
||||||
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
|
m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
|
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
|
||||||
@ -206,7 +217,8 @@ void CompilerUtils::encodeToMemory(
|
|||||||
else if (
|
else if (
|
||||||
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
||||||
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
|
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
|
||||||
_givenTypes[i]->category() == Type::Category::StringLiteral
|
_givenTypes[i]->category() == Type::Category::StringLiteral ||
|
||||||
|
_givenTypes[i]->category() == Type::Category::Function
|
||||||
)
|
)
|
||||||
type = _givenTypes[i]; // delay conversion
|
type = _givenTypes[i]; // delay conversion
|
||||||
else
|
else
|
||||||
@ -678,6 +690,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
|
|
||||||
void CompilerUtils::pushZeroValue(Type const& _type)
|
void CompilerUtils::pushZeroValue(Type const& _type)
|
||||||
{
|
{
|
||||||
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
|
{
|
||||||
|
if (funType->location() == FunctionType::Location::Internal)
|
||||||
|
{
|
||||||
|
m_context << m_context.errorTag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
|
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
|
||||||
if (!referenceType || referenceType->location() == DataLocation::Storage)
|
if (!referenceType || referenceType->location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
@ -839,6 +859,18 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
|
{
|
||||||
|
if (funType->location() == FunctionType::Location::External)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +293,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
|
|||||||
{
|
{
|
||||||
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
||||||
TypePointer type = parameterType->decodingType();
|
TypePointer type = parameterType->decodingType();
|
||||||
|
solAssert(type, "No decoding type found.");
|
||||||
if (type->category() == Type::Category::Array)
|
if (type->category() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
|
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
|
||||||
|
@ -7606,6 +7606,29 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call)
|
|||||||
BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7)));
|
BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(calling_uninitialized_function)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function intern() returns (uint) {
|
||||||
|
function (uint) internal returns (uint) x;
|
||||||
|
x();
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
function extern() returns (uint) {
|
||||||
|
function (uint) external returns (uint) x;
|
||||||
|
x();
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
// This should throw exceptions
|
||||||
|
BOOST_CHECK(callContractFunction("intern()") == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("extern()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(pass_function_types_internally)
|
BOOST_AUTO_TEST_CASE(pass_function_types_internally)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -7613,7 +7636,7 @@ BOOST_AUTO_TEST_CASE(pass_function_types_internally)
|
|||||||
function f(uint x) returns (uint) {
|
function f(uint x) returns (uint) {
|
||||||
return eval(g, x);
|
return eval(g, x);
|
||||||
}
|
}
|
||||||
function eval(function(uint) returns (uint) x, uint a) returns (uint) {
|
function eval(function(uint) returns (uint) x, uint a) internal returns (uint) {
|
||||||
return x(a);
|
return x(a);
|
||||||
}
|
}
|
||||||
function g(uint x) returns (uint) { return x + 1; }
|
function g(uint x) returns (uint) { return x + 1; }
|
||||||
@ -7624,6 +7647,30 @@ BOOST_AUTO_TEST_CASE(pass_function_types_internally)
|
|||||||
BOOST_CHECK(callContractFunction("f(uint256)", 7) == encodeArgs(u256(8)));
|
BOOST_CHECK(callContractFunction("f(uint256)", 7) == encodeArgs(u256(8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(pass_function_types_externally)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(uint x) returns (uint) {
|
||||||
|
return this.eval(this.g, x);
|
||||||
|
}
|
||||||
|
function f2(uint x) returns (uint) {
|
||||||
|
return eval(this.g, x);
|
||||||
|
}
|
||||||
|
function eval(function(uint) external returns (uint) x, uint a) returns (uint) {
|
||||||
|
return x(a);
|
||||||
|
}
|
||||||
|
function g(uint x) returns (uint) { return x + 1; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256)", 7) == encodeArgs(u256(8)));
|
||||||
|
BOOST_CHECK(callContractFunction("f2(uint256)", 7) == encodeArgs(u256(8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: storage, arrays
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(shift_constant_left)
|
BOOST_AUTO_TEST_CASE(shift_constant_left)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
@ -4180,6 +4180,39 @@ BOOST_AUTO_TEST_CASE(public_function_type)
|
|||||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter)
|
||||||
|
{
|
||||||
|
// It should not be possible to give internal functions
|
||||||
|
// as parameters to external functions.
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f(function(uint) returns (uint) x) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library L {
|
||||||
|
function f(function(uint) returns (uint) x) internal {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library L {
|
||||||
|
function f(function(uint) returns (uint) x) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(invalid_fixed_point_literal)
|
BOOST_AUTO_TEST_CASE(invalid_fixed_point_literal)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user