aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/ast/Types.cpp12
-rw-r--r--libsolidity/ast/Types.h1
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp34
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp1
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp49
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp33
6 files changed, 127 insertions, 3 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 808b0c55..19a1b9d1 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -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
{
if (m_location != Location::External && m_location != Location::Internal)
@@ -2072,7 +2082,7 @@ TypePointer FunctionType::interfaceType(bool _inLibrary) const
if (_inLibrary)
return shared_from_this();
else
- return make_shared<FixedBytesType>(storageBytes());
+ return make_shared<IntegerType>(8 * storageBytes());
}
bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 358c7efc..691ddf29 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -901,6 +901,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return m_location == Location::Internal || m_location == Location::External; }
virtual unsigned sizeOnStack() const override;
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
+ virtual TypePointer encodingType() const override;
virtual TypePointer interfaceType(bool _inLibrary) const override;
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 58d1caa9..38a7a23b 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -133,6 +133,17 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
m_context << u256(str->value().size());
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
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
@@ -206,7 +217,8 @@ void CompilerUtils::encodeToMemory(
else if (
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
_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
else
@@ -678,6 +690,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
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);
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;
}
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 2aec3055..9cd893e8 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -293,6 +293,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
{
// stack: v1 v2 ... v(k-1) base_offset current_offset
TypePointer type = parameterType->decodingType();
+ solAssert(type, "No decoding type found.");
if (type->category() == Type::Category::Array)
{
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index c9097663..76df1970 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -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_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)
{
char const* sourceCode = R"(
@@ -7613,7 +7636,28 @@ BOOST_AUTO_TEST_CASE(pass_function_types_internally)
function f(uint x) returns (uint) {
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);
+ }
+ function g(uint x) returns (uint) { return x + 1; }
+ }
+ )";
+
+ compileAndRun(sourceCode, 0, "C");
+ 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; }
@@ -7622,8 +7666,11 @@ BOOST_AUTO_TEST_CASE(pass_function_types_internally)
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)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 7f12cd86..2d469cdc 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -4180,6 +4180,39 @@ BOOST_AUTO_TEST_CASE(public_function_type)
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)
{