diff options
author | chriseth <c@ethdev.com> | 2016-09-29 01:22:23 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2016-11-16 21:37:17 +0800 |
commit | dd173f83e3a6a9046d1aa7e64cb171598a73b272 (patch) | |
tree | 7c5b47b6a4b15a151284d6503af27667993a782b | |
parent | cc8583ec7d6fd86ca7e129475fde32b76d102e79 (diff) | |
download | dexon-solidity-dd173f83e3a6a9046d1aa7e64cb171598a73b272.tar.gz dexon-solidity-dd173f83e3a6a9046d1aa7e64cb171598a73b272.tar.zst dexon-solidity-dd173f83e3a6a9046d1aa7e64cb171598a73b272.zip |
Code generator for function types.
-rw-r--r-- | libsolidity/ast/Types.cpp | 12 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 1 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 34 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 1 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 49 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 33 |
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) { |