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 | ||||
| { | ||||
| 	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 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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,7 @@ 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; } | ||||
| @ -7624,6 +7647,30 @@ BOOST_AUTO_TEST_CASE(pass_function_types_internally) | ||||
| 	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) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
|  | ||||
| @ -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) | ||||
| { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user