mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
This commit is contained in:
		
						commit
						f7f2861f5f
					
				
							
								
								
									
										58
									
								
								AST.cpp
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								AST.cpp
									
									
									
									
									
								
							| @ -209,6 +209,33 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn | |||||||
| 	return *m_interfaceFunctionList; | 	return *m_interfaceFunctionList; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | vector<Declaration const*> const& ContractDefinition::getInheritableMembers() const | ||||||
|  | { | ||||||
|  | 	if (!m_inheritableMembers) | ||||||
|  | 	{ | ||||||
|  | 		set<string> memberSeen; | ||||||
|  | 		m_inheritableMembers.reset(new vector<Declaration const*>()); | ||||||
|  | 		auto addInheritableMember = [&](Declaration const* _decl) | ||||||
|  | 		{ | ||||||
|  | 			if (memberSeen.count(_decl->getName()) == 0 && _decl->isVisibleInDerivedContracts()) | ||||||
|  | 			{ | ||||||
|  | 				memberSeen.insert(_decl->getName()); | ||||||
|  | 				m_inheritableMembers->push_back(_decl); | ||||||
|  | 			} | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		for (ASTPointer<FunctionDefinition> const& f: getDefinedFunctions()) | ||||||
|  | 			addInheritableMember(f.get()); | ||||||
|  | 
 | ||||||
|  | 		for (ASTPointer<VariableDeclaration> const& v: getStateVariables()) | ||||||
|  | 			addInheritableMember(v.get()); | ||||||
|  | 
 | ||||||
|  | 		for (ASTPointer<StructDefinition> const& s: getDefinedStructs()) | ||||||
|  | 			addInheritableMember(s.get()); | ||||||
|  | 	} | ||||||
|  | 	return *m_inheritableMembers; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| TypePointer EnumValue::getType(ContractDefinition const*) const | TypePointer EnumValue::getType(ContractDefinition const*) const | ||||||
| { | { | ||||||
| 	EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(getScope()); | 	EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(getScope()); | ||||||
| @ -281,7 +308,9 @@ void FunctionDefinition::checkTypeRequirements() | |||||||
| 		if (!var->getType()->canLiveOutsideStorage()) | 		if (!var->getType()->canLiveOutsideStorage()) | ||||||
| 			BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); | 			BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); | ||||||
| 	for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers) | 	for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers) | ||||||
| 		modifier->checkTypeRequirements(); | 		modifier->checkTypeRequirements(isConstructor() ? | ||||||
|  | 			dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() : | ||||||
|  | 			vector<ASTPointer<InheritanceSpecifier>>()); | ||||||
| 
 | 
 | ||||||
| 	m_body->checkTypeRequirements(); | 	m_body->checkTypeRequirements(); | ||||||
| } | } | ||||||
| @ -324,19 +353,34 @@ void ModifierDefinition::checkTypeRequirements() | |||||||
| 	m_body->checkTypeRequirements(); | 	m_body->checkTypeRequirements(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ModifierInvocation::checkTypeRequirements() | void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpecifier>> const& _bases) | ||||||
| { | { | ||||||
| 	m_modifierName->checkTypeRequirements(); | 	m_modifierName->checkTypeRequirements(); | ||||||
| 	for (ASTPointer<Expression> const& argument: m_arguments) | 	for (ASTPointer<Expression> const& argument: m_arguments) | ||||||
| 		argument->checkTypeRequirements(); | 		argument->checkTypeRequirements(); | ||||||
| 
 | 
 | ||||||
| 	ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(m_modifierName->getReferencedDeclaration()); | 	auto declaration = m_modifierName->getReferencedDeclaration(); | ||||||
| 	solAssert(modifier, "Function modifier not found."); | 	vector<ASTPointer<VariableDeclaration>> emptyParameterList; | ||||||
| 	vector<ASTPointer<VariableDeclaration>> const& parameters = modifier->getParameters(); | 	vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr; | ||||||
| 	if (parameters.size() != m_arguments.size()) | 	if (auto modifier = dynamic_cast<ModifierDefinition const*>(declaration)) | ||||||
|  | 		parameters = &modifier->getParameters(); | ||||||
|  | 	else | ||||||
|  | 		// check parameters for Base constructors
 | ||||||
|  | 		for (auto const& base: _bases) | ||||||
|  | 			if (declaration == base->getName()->getReferencedDeclaration()) | ||||||
|  | 			{ | ||||||
|  | 				if (auto referencedConstructor = dynamic_cast<ContractDefinition const&>(*declaration).getConstructor()) | ||||||
|  | 					parameters = &referencedConstructor->getParameters(); | ||||||
|  | 				else | ||||||
|  | 					parameters = &emptyParameterList; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 	if (!parameters) | ||||||
|  | 		BOOST_THROW_EXCEPTION(createTypeError("Referenced declaration is neither modifier nor base class.")); | ||||||
|  | 	if (parameters->size() != m_arguments.size()) | ||||||
| 		BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation.")); | 		BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation.")); | ||||||
| 	for (size_t i = 0; i < m_arguments.size(); ++i) | 	for (size_t i = 0; i < m_arguments.size(); ++i) | ||||||
| 		if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) | 		if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*(*parameters)[i]->getType())) | ||||||
| 			BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation.")); | 			BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation.")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								AST.h
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								AST.h
									
									
									
									
									
								
							| @ -144,7 +144,7 @@ public: | |||||||
| 	Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; } | 	Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; } | ||||||
| 	bool isPublic() const { return getVisibility() >= Visibility::Public; } | 	bool isPublic() const { return getVisibility() >= Visibility::Public; } | ||||||
| 	bool isVisibleInContract() const { return getVisibility() != Visibility::External; } | 	bool isVisibleInContract() const { return getVisibility() != Visibility::External; } | ||||||
| 	bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; } | 	virtual bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; } | ||||||
| 
 | 
 | ||||||
| 	/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
 | 	/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
 | ||||||
| 	/// Available only after name and type resolution step.
 | 	/// Available only after name and type resolution step.
 | ||||||
| @ -247,6 +247,9 @@ public: | |||||||
| 	/// as intended for use by the ABI.
 | 	/// as intended for use by the ABI.
 | ||||||
| 	std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const; | 	std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const; | ||||||
| 
 | 
 | ||||||
|  | 	/// @returns a list of the inheritable members of this contract
 | ||||||
|  | 	std::vector<Declaration const*> const& getInheritableMembers() const; | ||||||
|  | 
 | ||||||
| 	/// List of all (direct and indirect) base contracts in order from derived to base, including
 | 	/// List of all (direct and indirect) base contracts in order from derived to base, including
 | ||||||
| 	/// the contract itself. Available after name resolution
 | 	/// the contract itself. Available after name resolution
 | ||||||
| 	std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; } | 	std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; } | ||||||
| @ -273,6 +276,7 @@ private: | |||||||
| 	std::vector<ContractDefinition const*> m_linearizedBaseContracts; | 	std::vector<ContractDefinition const*> m_linearizedBaseContracts; | ||||||
| 	mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList; | 	mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList; | ||||||
| 	mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents; | 	mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents; | ||||||
|  | 	mutable std::unique_ptr<std::vector<Declaration const*>> m_inheritableMembers; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class InheritanceSpecifier: public ASTNode | class InheritanceSpecifier: public ASTNode | ||||||
| @ -405,6 +409,11 @@ public: | |||||||
| 	ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } | 	ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } | ||||||
| 	Block const& getBody() const { return *m_body; } | 	Block const& getBody() const { return *m_body; } | ||||||
| 
 | 
 | ||||||
|  | 	virtual bool isVisibleInDerivedContracts() const override | ||||||
|  | 	{ | ||||||
|  | 		return !isConstructor() && !getName().empty() && isVisibleInContract() && | ||||||
|  | 			getVisibility() >= Visibility::Internal; | ||||||
|  | 	} | ||||||
| 	virtual TypePointer getType(ContractDefinition const*) const override; | 	virtual TypePointer getType(ContractDefinition const*) const override; | ||||||
| 
 | 
 | ||||||
| 	/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
 | 	/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
 | ||||||
| @ -501,7 +510,7 @@ private: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Invocation/usage of a modifier in a function header. |  * Invocation/usage of a modifier in a function header or a base constructor call. | ||||||
|  */ |  */ | ||||||
| class ModifierInvocation: public ASTNode | class ModifierInvocation: public ASTNode | ||||||
| { | { | ||||||
| @ -516,7 +525,8 @@ public: | |||||||
| 	ASTPointer<Identifier> const& getName() const { return m_modifierName; } | 	ASTPointer<Identifier> const& getName() const { return m_modifierName; } | ||||||
| 	std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; } | 	std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; } | ||||||
| 
 | 
 | ||||||
| 	void checkTypeRequirements(); | 	/// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed.
 | ||||||
|  | 	void checkTypeRequirements(std::vector<ASTPointer<InheritanceSpecifier>> const& _bases); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	ASTPointer<Identifier> m_modifierName; | 	ASTPointer<Identifier> m_modifierName; | ||||||
|  | |||||||
							
								
								
									
										304
									
								
								ArrayUtils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								ArrayUtils.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,304 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of cpp-ethereum. | ||||||
|  | 
 | ||||||
|  | 	cpp-ethereum is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	cpp-ethereum is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * @author Christian <c@ethdev.com> | ||||||
|  |  * @date 2015 | ||||||
|  |  * Code generation utils that handle arrays. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <libsolidity/ArrayUtils.h> | ||||||
|  | #include <libevmcore/Instruction.h> | ||||||
|  | #include <libsolidity/CompilerContext.h> | ||||||
|  | #include <libsolidity/CompilerUtils.h> | ||||||
|  | #include <libsolidity/Types.h> | ||||||
|  | #include <libsolidity/Utils.h> | ||||||
|  | #include <libsolidity/LValue.h> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace dev; | ||||||
|  | using namespace solidity; | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const | ||||||
|  | { | ||||||
|  | 	// stack layout: [source_ref] target_ref (top)
 | ||||||
|  | 	// need to leave target_ref on the stack at the end
 | ||||||
|  | 	solAssert(_targetType.getLocation() == ArrayType::Location::Storage, ""); | ||||||
|  | 
 | ||||||
|  | 	IntegerType uint256(256); | ||||||
|  | 	Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.getBaseType()); | ||||||
|  | 	Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.getBaseType()); | ||||||
|  | 
 | ||||||
|  | 	switch (_sourceType.getLocation()) | ||||||
|  | 	{ | ||||||
|  | 	case ArrayType::Location::CallData: | ||||||
|  | 	{ | ||||||
|  | 		solAssert(_targetType.isByteArray(), "Non byte arrays not yet implemented here."); | ||||||
|  | 		solAssert(_sourceType.isByteArray(), "Non byte arrays not yet implemented here."); | ||||||
|  | 		// This also assumes that after "length" we only have zeros, i.e. it cannot be used to
 | ||||||
|  | 		// slice a byte array from calldata.
 | ||||||
|  | 
 | ||||||
|  | 		// stack: source_offset source_len target_ref
 | ||||||
|  | 		// fetch old length and convert to words
 | ||||||
|  | 		m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; | ||||||
|  | 		convertLengthToSize(_targetType); | ||||||
|  | 		// stack here: source_offset source_len target_ref target_length_words
 | ||||||
|  | 		// actual array data is stored at SHA3(storage_offset)
 | ||||||
|  | 		m_context << eth::Instruction::DUP2; | ||||||
|  | 		CompilerUtils(m_context).computeHashStatic(); | ||||||
|  | 		// compute target_data_end
 | ||||||
|  | 		m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD | ||||||
|  | 				  << eth::Instruction::SWAP1; | ||||||
|  | 		// stack here: source_offset source_len target_ref target_data_end target_data_ref
 | ||||||
|  | 		// store length (in bytes)
 | ||||||
|  | 		m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::DUP5 | ||||||
|  | 			<< eth::Instruction::SSTORE; | ||||||
|  | 		// jump to end if length is zero
 | ||||||
|  | 		m_context << eth::Instruction::ISZERO; | ||||||
|  | 		eth::AssemblyItem copyLoopEnd = m_context.newTag(); | ||||||
|  | 		m_context.appendConditionalJumpTo(copyLoopEnd); | ||||||
|  | 		// store start offset
 | ||||||
|  | 		m_context << eth::Instruction::DUP5; | ||||||
|  | 		// stack now: source_offset source_len target_ref target_data_end target_data_ref calldata_offset
 | ||||||
|  | 		eth::AssemblyItem copyLoopStart = m_context.newTag(); | ||||||
|  | 		m_context << copyLoopStart | ||||||
|  | 				  // copy from calldata and store
 | ||||||
|  | 				  << eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD | ||||||
|  | 				  << eth::Instruction::DUP3 << eth::Instruction::SSTORE | ||||||
|  | 				  // increment target_data_ref by 1
 | ||||||
|  | 				  << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD | ||||||
|  | 				  // increment calldata_offset by 32
 | ||||||
|  | 				  << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD | ||||||
|  | 				  // check for loop condition
 | ||||||
|  | 				  << eth::Instruction::DUP1 << eth::Instruction::DUP6 << eth::Instruction::GT; | ||||||
|  | 		m_context.appendConditionalJumpTo(copyLoopStart); | ||||||
|  | 		m_context << eth::Instruction::POP; | ||||||
|  | 		m_context << copyLoopEnd; | ||||||
|  | 
 | ||||||
|  | 		// now clear leftover bytes of the old value
 | ||||||
|  | 		// stack now: source_offset source_len target_ref target_data_end target_data_ref
 | ||||||
|  | 		clearStorageLoop(IntegerType(256)); | ||||||
|  | 		// stack now: source_offset source_len target_ref target_data_end
 | ||||||
|  | 
 | ||||||
|  | 		m_context << eth::Instruction::POP << eth::Instruction::SWAP2 | ||||||
|  | 			<< eth::Instruction::POP << eth::Instruction::POP; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	case ArrayType::Location::Storage: | ||||||
|  | 	{ | ||||||
|  | 		// this copies source to target and also clears target if it was larger
 | ||||||
|  | 
 | ||||||
|  | 		solAssert(sourceBaseType->getStorageSize() == targetBaseType->getStorageSize(), | ||||||
|  | 			"Copying with different storage sizes not yet implemented."); | ||||||
|  | 		// stack: source_ref target_ref
 | ||||||
|  | 		// store target_ref
 | ||||||
|  | 		m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; | ||||||
|  | 		// stack: target_ref source_ref target_ref
 | ||||||
|  | 		// fetch lengthes
 | ||||||
|  | 		retrieveLength(_targetType); | ||||||
|  | 		m_context << eth::Instruction::SWAP2; | ||||||
|  | 		// stack: target_ref target_len target_ref source_ref
 | ||||||
|  | 		retrieveLength(_sourceType); | ||||||
|  | 		// stack: target_ref target_len target_ref source_ref source_len
 | ||||||
|  | 		if (_targetType.isDynamicallySized()) | ||||||
|  | 			// store new target length
 | ||||||
|  | 			m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE; | ||||||
|  | 		// compute hashes (data positions)
 | ||||||
|  | 		m_context << eth::Instruction::SWAP2; | ||||||
|  | 		if (_targetType.isDynamicallySized()) | ||||||
|  | 			CompilerUtils(m_context).computeHashStatic(); | ||||||
|  | 		m_context << eth::Instruction::SWAP1; | ||||||
|  | 		if (_sourceType.isDynamicallySized()) | ||||||
|  | 			CompilerUtils(m_context).computeHashStatic(); | ||||||
|  | 		// stack: target_ref target_len source_len target_data_pos source_data_pos
 | ||||||
|  | 		m_context << eth::Instruction::DUP4; | ||||||
|  | 		convertLengthToSize(_sourceType); | ||||||
|  | 		m_context << eth::Instruction::DUP4; | ||||||
|  | 		convertLengthToSize(_sourceType); | ||||||
|  | 		// stack: target_ref target_len source_len target_data_pos source_data_pos target_size source_size
 | ||||||
|  | 		// @todo we might be able to go without a third counter
 | ||||||
|  | 		m_context << u256(0); | ||||||
|  | 		// stack: target_ref target_len source_len target_data_pos source_data_pos target_size source_size counter
 | ||||||
|  | 		eth::AssemblyItem copyLoopStart = m_context.newTag(); | ||||||
|  | 		m_context << copyLoopStart; | ||||||
|  | 		// check for loop condition
 | ||||||
|  | 		m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 | ||||||
|  | 				   << eth::Instruction::GT << eth::Instruction::ISZERO; | ||||||
|  | 		eth::AssemblyItem copyLoopEnd = m_context.newTag(); | ||||||
|  | 		m_context.appendConditionalJumpTo(copyLoopEnd); | ||||||
|  | 		// copy
 | ||||||
|  | 		m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD; | ||||||
|  | 		StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true); | ||||||
|  | 		m_context << eth::dupInstruction(5 + sourceBaseType->getSizeOnStack()) | ||||||
|  | 			<< eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()) << eth::Instruction::ADD; | ||||||
|  | 		StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); | ||||||
|  | 		// increment
 | ||||||
|  | 		m_context << targetBaseType->getStorageSize() << eth::Instruction::ADD; | ||||||
|  | 		m_context.appendJumpTo(copyLoopStart); | ||||||
|  | 		m_context << copyLoopEnd; | ||||||
|  | 
 | ||||||
|  | 		// zero-out leftovers in target
 | ||||||
|  | 		// stack: target_ref target_len source_len target_data_pos source_data_pos target_size source_size counter
 | ||||||
|  | 		// add counter to target_data_pos
 | ||||||
|  | 		m_context << eth::Instruction::DUP5 << eth::Instruction::ADD | ||||||
|  | 				  << eth::Instruction::SWAP5 << eth::Instruction::POP; | ||||||
|  | 		// stack: target_ref target_len target_data_pos_updated target_data_pos source_data_pos target_size source_size
 | ||||||
|  | 		// add size to target_data_pos to get target_data_end
 | ||||||
|  | 		m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD | ||||||
|  | 				  << eth::Instruction::SWAP4 | ||||||
|  | 				  << eth::Instruction::POP  << eth::Instruction::POP << eth::Instruction::POP; | ||||||
|  | 		// stack: target_ref target_data_end target_data_pos_updated
 | ||||||
|  | 		clearStorageLoop(*targetBaseType); | ||||||
|  | 		m_context << eth::Instruction::POP; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	default: | ||||||
|  | 		solAssert(false, "Given byte array location not implemented."); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::clearArray(ArrayType const& _type) const | ||||||
|  | { | ||||||
|  | 	solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); | ||||||
|  | 	if (_type.isDynamicallySized()) | ||||||
|  | 		clearDynamicArray(_type); | ||||||
|  | 	else if (_type.getLength() == 0) | ||||||
|  | 		m_context << eth::Instruction::POP; | ||||||
|  | 	else if (_type.getLength() < 5) // unroll loop for small arrays @todo choose a good value
 | ||||||
|  | 	{ | ||||||
|  | 		for (unsigned i = 1; i < _type.getLength(); ++i) | ||||||
|  | 		{ | ||||||
|  | 			StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false); | ||||||
|  | 			m_context << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD; | ||||||
|  | 		} | ||||||
|  | 		StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		m_context | ||||||
|  | 			<< eth::Instruction::DUP1 << u256(_type.getLength()) | ||||||
|  | 			<< u256(_type.getBaseType()->getStorageSize()) | ||||||
|  | 			<< eth::Instruction::MUL << eth::Instruction::ADD << eth::Instruction::SWAP1; | ||||||
|  | 		clearStorageLoop(*_type.getBaseType()); | ||||||
|  | 		m_context << eth::Instruction::POP; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::clearDynamicArray(ArrayType const& _type) const | ||||||
|  | { | ||||||
|  | 	solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); | ||||||
|  | 	solAssert(_type.isDynamicallySized(), ""); | ||||||
|  | 
 | ||||||
|  | 	// fetch length
 | ||||||
|  | 	m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; | ||||||
|  | 	// set length to zero
 | ||||||
|  | 	m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE; | ||||||
|  | 	// stack: ref old_length
 | ||||||
|  | 	convertLengthToSize(_type); | ||||||
|  | 	// compute data positions
 | ||||||
|  | 	m_context << eth::Instruction::SWAP1; | ||||||
|  | 	CompilerUtils(m_context).computeHashStatic(); | ||||||
|  | 	// stack: len data_pos (len is in slots for byte array and in items for other arrays)
 | ||||||
|  | 	m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD | ||||||
|  | 		<< eth::Instruction::SWAP1; | ||||||
|  | 	// stack: data_pos_end data_pos
 | ||||||
|  | 	if (_type.isByteArray()) | ||||||
|  | 		clearStorageLoop(IntegerType(256)); | ||||||
|  | 	else | ||||||
|  | 		clearStorageLoop(*_type.getBaseType()); | ||||||
|  | 	// cleanup
 | ||||||
|  | 	m_context << eth::Instruction::POP; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const | ||||||
|  | { | ||||||
|  | 	solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); | ||||||
|  | 	solAssert(_type.isDynamicallySized(), ""); | ||||||
|  | 
 | ||||||
|  | 	eth::AssemblyItem resizeEnd = m_context.newTag(); | ||||||
|  | 
 | ||||||
|  | 	// stack: ref new_length
 | ||||||
|  | 	// fetch old length
 | ||||||
|  | 	m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD; | ||||||
|  | 	// stack: ref new_length old_length
 | ||||||
|  | 	// store new length
 | ||||||
|  | 	m_context << eth::Instruction::DUP2 << eth::Instruction::DUP4 << eth::Instruction::SSTORE; | ||||||
|  | 	// skip if size is not reduced
 | ||||||
|  | 	m_context << eth::Instruction::DUP2 << eth::Instruction::DUP2 | ||||||
|  | 		<< eth::Instruction::ISZERO << eth::Instruction::GT; | ||||||
|  | 	m_context.appendConditionalJumpTo(resizeEnd); | ||||||
|  | 
 | ||||||
|  | 	// size reduced, clear the end of the array
 | ||||||
|  | 	// stack: ref new_length old_length
 | ||||||
|  | 	convertLengthToSize(_type); | ||||||
|  | 	m_context << eth::Instruction::DUP2; | ||||||
|  | 	convertLengthToSize(_type); | ||||||
|  | 	// stack: ref new_length old_size new_size
 | ||||||
|  | 	// compute data positions
 | ||||||
|  | 	m_context << eth::Instruction::DUP4; | ||||||
|  | 	CompilerUtils(m_context).computeHashStatic(); | ||||||
|  | 	// stack: ref new_length old_size new_size data_pos
 | ||||||
|  | 	m_context << eth::Instruction::SWAP2 << eth::Instruction::DUP3 << eth::Instruction::ADD; | ||||||
|  | 	// stack: ref new_length data_pos new_size delete_end
 | ||||||
|  | 	m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD; | ||||||
|  | 	// stack: ref new_length delete_end delete_start
 | ||||||
|  | 	if (_type.isByteArray()) | ||||||
|  | 		clearStorageLoop(IntegerType(256)); | ||||||
|  | 	else | ||||||
|  | 		clearStorageLoop(*_type.getBaseType()); | ||||||
|  | 
 | ||||||
|  | 	m_context << resizeEnd; | ||||||
|  | 	// cleanup
 | ||||||
|  | 	m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::clearStorageLoop(Type const& _type) const | ||||||
|  | { | ||||||
|  | 	// stack: end_pos pos
 | ||||||
|  | 	eth::AssemblyItem loopStart = m_context.newTag(); | ||||||
|  | 	m_context << loopStart; | ||||||
|  | 	// check for loop condition
 | ||||||
|  | 	m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 | ||||||
|  | 			   << eth::Instruction::GT << eth::Instruction::ISZERO; | ||||||
|  | 	eth::AssemblyItem zeroLoopEnd = m_context.newTag(); | ||||||
|  | 	m_context.appendConditionalJumpTo(zeroLoopEnd); | ||||||
|  | 	// delete
 | ||||||
|  | 	StorageItem(m_context, _type).setToZero(SourceLocation(), false); | ||||||
|  | 	// increment
 | ||||||
|  | 	m_context << u256(1) << eth::Instruction::ADD; | ||||||
|  | 	m_context.appendJumpTo(loopStart); | ||||||
|  | 	// cleanup
 | ||||||
|  | 	m_context << zeroLoopEnd; | ||||||
|  | 	m_context << eth::Instruction::POP; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType) const | ||||||
|  | { | ||||||
|  | 	if (_arrayType.isByteArray()) | ||||||
|  | 		m_context << u256(31) << eth::Instruction::ADD | ||||||
|  | 			<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; | ||||||
|  | 	else if (_arrayType.getBaseType()->getStorageSize() > 1) | ||||||
|  | 		m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const | ||||||
|  | { | ||||||
|  | 	if (_arrayType.isDynamicallySized()) | ||||||
|  | 		m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; | ||||||
|  | 	else | ||||||
|  | 		m_context << _arrayType.getLength(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										78
									
								
								ArrayUtils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								ArrayUtils.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of cpp-ethereum. | ||||||
|  | 
 | ||||||
|  | 	cpp-ethereum is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	cpp-ethereum is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * @author Christian <c@ethdev.com> | ||||||
|  |  * @date 2015 | ||||||
|  |  * Code generation utils that handle arrays. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | namespace dev | ||||||
|  | { | ||||||
|  | namespace solidity | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class CompilerContext; | ||||||
|  | class Type; | ||||||
|  | class ArrayType; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Class that provides code generation for handling arrays. | ||||||
|  |  */ | ||||||
|  | class ArrayUtils | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	ArrayUtils(CompilerContext& _context): m_context(_context) {} | ||||||
|  | 
 | ||||||
|  | 	/// Copies an array to an array in storage. The arrays can be of different types only if
 | ||||||
|  | 	/// their storage representation is the same.
 | ||||||
|  | 	/// Stack pre: [source_reference] target_reference
 | ||||||
|  | 	/// Stack post: target_reference
 | ||||||
|  | 	void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const; | ||||||
|  | 	/// Clears the given dynamic or static array.
 | ||||||
|  | 	/// Stack pre: reference
 | ||||||
|  | 	/// Stack post:
 | ||||||
|  | 	void clearArray(ArrayType const& _type) const; | ||||||
|  | 	/// Clears the length and data elements of the array referenced on the stack.
 | ||||||
|  | 	/// Stack pre: reference
 | ||||||
|  | 	/// Stack post:
 | ||||||
|  | 	void clearDynamicArray(ArrayType const& _type) const; | ||||||
|  | 	/// Changes the size of a dynamic array and clears the tail if it is shortened.
 | ||||||
|  | 	/// Stack pre: reference new_length
 | ||||||
|  | 	/// Stack post:
 | ||||||
|  | 	void resizeDynamicArray(ArrayType const& _type) const; | ||||||
|  | 	/// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
 | ||||||
|  | 	/// Stack pre: end_ref start_ref
 | ||||||
|  | 	/// Stack post: end_ref
 | ||||||
|  | 	void clearStorageLoop(Type const& _type) const; | ||||||
|  | 	/// Converts length to size (multiplies by size on stack), rounds up for byte arrays.
 | ||||||
|  | 	/// Stack pre: length
 | ||||||
|  | 	/// Stack post: size
 | ||||||
|  | 	void convertLengthToSize(ArrayType const& _arrayType) const; | ||||||
|  | 	/// Retrieves the length (number of elements) of the array ref on the stack. This also
 | ||||||
|  | 	/// works for statically-sized arrays.
 | ||||||
|  | 	/// Stack pre: reference
 | ||||||
|  | 	/// Stack post: reference length
 | ||||||
|  | 	void retrieveLength(ArrayType const& _arrayType) const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	CompilerContext& m_context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										124
									
								
								Compiler.cpp
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								Compiler.cpp
									
									
									
									
									
								
							| @ -58,7 +58,10 @@ void Compiler::compileContract(ContractDefinition const& _contract, | |||||||
| 	while (!functions.empty()) | 	while (!functions.empty()) | ||||||
| 	{ | 	{ | ||||||
| 		for (Declaration const* function: functions) | 		for (Declaration const* function: functions) | ||||||
|  | 		{ | ||||||
|  | 			m_context.setStackOffset(0); | ||||||
| 			function->accept(*this); | 			function->accept(*this); | ||||||
|  | 		} | ||||||
| 		functions = m_context.getFunctionsWithoutCode(); | 		functions = m_context.getFunctionsWithoutCode(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -79,37 +82,38 @@ void Compiler::initializeContext(ContractDefinition const& _contract, | |||||||
| 
 | 
 | ||||||
| void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) | void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) | ||||||
| { | { | ||||||
| 	// arguments for base constructors, filled in derived-to-base order
 |  | ||||||
| 	map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments; |  | ||||||
| 
 |  | ||||||
| 	// Determine the arguments that are used for the base constructors.
 | 	// Determine the arguments that are used for the base constructors.
 | ||||||
| 	std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts(); | 	std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts(); | ||||||
| 	for (ContractDefinition const* contract: bases) | 	for (ContractDefinition const* contract: bases) | ||||||
|  | 	{ | ||||||
|  | 		if (FunctionDefinition const* constructor = contract->getConstructor()) | ||||||
|  | 			for (auto const& modifier: constructor->getModifiers()) | ||||||
|  | 			{ | ||||||
|  | 				auto baseContract = dynamic_cast<ContractDefinition const*>( | ||||||
|  | 					modifier->getName()->getReferencedDeclaration()); | ||||||
|  | 				if (baseContract) | ||||||
|  | 					if (m_baseArguments.count(baseContract->getConstructor()) == 0) | ||||||
|  | 						m_baseArguments[baseContract->getConstructor()] = &modifier->getArguments(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 		for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts()) | 		for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts()) | ||||||
| 		{ | 		{ | ||||||
| 			ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( | 			ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( | ||||||
| 						base->getName()->getReferencedDeclaration()); | 						base->getName()->getReferencedDeclaration()); | ||||||
| 			solAssert(baseContract, ""); | 			solAssert(baseContract, ""); | ||||||
| 			if (baseArguments.count(baseContract) == 0) |  | ||||||
| 				baseArguments[baseContract] = &base->getArguments(); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 	// Call constructors in base-to-derived order.
 | 			if (m_baseArguments.count(baseContract->getConstructor()) == 0) | ||||||
| 	// The Constructor for the most derived contract is called later.
 | 				m_baseArguments[baseContract->getConstructor()] = &base->getArguments(); | ||||||
| 	for (unsigned i = 1; i < bases.size(); i++) | 		} | ||||||
| 	{ |  | ||||||
| 		ContractDefinition const* base = bases[bases.size() - i]; |  | ||||||
| 		solAssert(base, ""); |  | ||||||
| 		initializeStateVariables(*base); |  | ||||||
| 		FunctionDefinition const* baseConstructor = base->getConstructor(); |  | ||||||
| 		if (!baseConstructor) |  | ||||||
| 			continue; |  | ||||||
| 		solAssert(baseArguments[base], ""); |  | ||||||
| 		appendBaseConstructorCall(*baseConstructor, *baseArguments[base]); |  | ||||||
| 	} | 	} | ||||||
| 	initializeStateVariables(_contract); | 	// Initialization of state variables in base-to-derived order.
 | ||||||
| 	if (_contract.getConstructor()) | 	for (ContractDefinition const* contract: boost::adaptors::reverse(bases)) | ||||||
| 		appendConstructorCall(*_contract.getConstructor()); | 		initializeStateVariables(*contract); | ||||||
|  | 
 | ||||||
|  | 	if (FunctionDefinition const* constructor = _contract.getConstructor()) | ||||||
|  | 		appendConstructor(*constructor); | ||||||
|  | 	else if (auto c = m_context.getNextConstructor(_contract)) | ||||||
|  | 		appendBaseConstructor(*c); | ||||||
| 
 | 
 | ||||||
| 	eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly()); | 	eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly()); | ||||||
| 	// stack contains sub size
 | 	// stack contains sub size
 | ||||||
| @ -126,22 +130,23 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor, | void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) | ||||||
| 										 vector<ASTPointer<Expression>> const& _arguments) |  | ||||||
| { | { | ||||||
| 	CompilerContext::LocationSetter locationSetter(m_context, &_constructor); | 	CompilerContext::LocationSetter locationSetter(m_context, &_constructor); | ||||||
| 	FunctionType constructorType(_constructor); | 	FunctionType constructorType(_constructor); | ||||||
| 	eth::AssemblyItem returnLabel = m_context.pushNewTag(); | 	if (!constructorType.getParameterTypes().empty()) | ||||||
| 	for (unsigned i = 0; i < _arguments.size(); ++i) | 	{ | ||||||
| 		compileExpression(*_arguments[i], constructorType.getParameterTypes()[i]); | 		std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor]; | ||||||
| 	m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); | 		solAssert(arguments, ""); | ||||||
| 	m_context << returnLabel; | 		for (unsigned i = 0; i < arguments->size(); ++i) | ||||||
|  | 			compileExpression(*(arguments->at(i)), constructorType.getParameterTypes()[i]); | ||||||
|  | 	} | ||||||
|  | 	_constructor.accept(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) | void Compiler::appendConstructor(FunctionDefinition const& _constructor) | ||||||
| { | { | ||||||
| 	CompilerContext::LocationSetter locationSetter(m_context, &_constructor); | 	CompilerContext::LocationSetter locationSetter(m_context, &_constructor); | ||||||
| 	eth::AssemblyItem returnTag = m_context.pushNewTag(); |  | ||||||
| 	// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
 | 	// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
 | ||||||
| 	unsigned argumentSize = 0; | 	unsigned argumentSize = 0; | ||||||
| 	for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters()) | 	for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters()) | ||||||
| @ -155,8 +160,7 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) | |||||||
| 		m_context << eth::Instruction::CODECOPY; | 		m_context << eth::Instruction::CODECOPY; | ||||||
| 		appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); | 		appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); | ||||||
| 	} | 	} | ||||||
| 	m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); | 	_constructor.accept(*this); | ||||||
| 	m_context << returnTag; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Compiler::appendFunctionSelector(ContractDefinition const& _contract) | void Compiler::appendFunctionSelector(ContractDefinition const& _contract) | ||||||
| @ -296,6 +300,29 @@ bool Compiler::visit(FunctionDefinition const& _function) | |||||||
| 	// although note that this reduces the size of the visible stack
 | 	// although note that this reduces the size of the visible stack
 | ||||||
| 
 | 
 | ||||||
| 	m_context.startFunction(_function); | 	m_context.startFunction(_function); | ||||||
|  | 
 | ||||||
|  | 	// stack upon entry: [return address] [arg0] [arg1] ... [argn]
 | ||||||
|  | 	// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
 | ||||||
|  | 
 | ||||||
|  | 	unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters()); | ||||||
|  | 	if (!_function.isConstructor()) | ||||||
|  | 		// adding 1 for return address.
 | ||||||
|  | 		m_context.adjustStackOffset(parametersSize + 1); | ||||||
|  | 	for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters()) | ||||||
|  | 	{ | ||||||
|  | 		m_context.addVariable(*variable, parametersSize); | ||||||
|  | 		parametersSize -= variable->getType()->getSizeOnStack(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters()) | ||||||
|  | 		m_context.addAndInitializeVariable(*variable); | ||||||
|  | 	for (VariableDeclaration const* localVariable: _function.getLocalVariables()) | ||||||
|  | 		m_context.addAndInitializeVariable(*localVariable); | ||||||
|  | 
 | ||||||
|  | 	if (_function.isConstructor()) | ||||||
|  | 		if (auto c = m_context.getNextConstructor(dynamic_cast<ContractDefinition const&>(*_function.getScope()))) | ||||||
|  | 			appendBaseConstructor(*c); | ||||||
|  | 
 | ||||||
| 	m_returnTag = m_context.newTag(); | 	m_returnTag = m_context.newTag(); | ||||||
| 	m_breakTags.clear(); | 	m_breakTags.clear(); | ||||||
| 	m_continueTags.clear(); | 	m_continueTags.clear(); | ||||||
| @ -303,21 +330,6 @@ bool Compiler::visit(FunctionDefinition const& _function) | |||||||
| 	m_currentFunction = &_function; | 	m_currentFunction = &_function; | ||||||
| 	m_modifierDepth = 0; | 	m_modifierDepth = 0; | ||||||
| 
 | 
 | ||||||
| 	// stack upon entry: [return address] [arg0] [arg1] ... [argn]
 |  | ||||||
| 	// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
 |  | ||||||
| 
 |  | ||||||
| 	unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters()); |  | ||||||
| 	m_context.adjustStackOffset(parametersSize); |  | ||||||
| 	for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters()) |  | ||||||
| 	{ |  | ||||||
| 		m_context.addVariable(*variable, parametersSize); |  | ||||||
| 		parametersSize -= variable->getType()->getSizeOnStack(); |  | ||||||
| 	} |  | ||||||
| 	for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters()) |  | ||||||
| 		m_context.addAndInitializeVariable(*variable); |  | ||||||
| 	for (VariableDeclaration const* localVariable: _function.getLocalVariables()) |  | ||||||
| 		m_context.addAndInitializeVariable(*localVariable); |  | ||||||
| 
 |  | ||||||
| 	appendModifierOrFunctionCode(); | 	appendModifierOrFunctionCode(); | ||||||
| 
 | 
 | ||||||
| 	m_context << m_returnTag; | 	m_context << m_returnTag; | ||||||
| @ -352,8 +364,14 @@ bool Compiler::visit(FunctionDefinition const& _function) | |||||||
| 		} | 		} | ||||||
| 	//@todo assert that everything is in place now
 | 	//@todo assert that everything is in place now
 | ||||||
| 
 | 
 | ||||||
| 	m_context << eth::Instruction::JUMP; | 	for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters() + _function.getReturnParameters()) | ||||||
|  | 		m_context.removeVariable(*variable); | ||||||
|  | 	for (VariableDeclaration const* localVariable: _function.getLocalVariables()) | ||||||
|  | 		m_context.removeVariable(*localVariable); | ||||||
| 
 | 
 | ||||||
|  | 	m_context.adjustStackOffset(-c_returnValuesSize); | ||||||
|  | 	if (!_function.isConstructor()) | ||||||
|  | 		m_context << eth::Instruction::JUMP; | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -515,6 +533,16 @@ void Compiler::appendModifierOrFunctionCode() | |||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth]; | 		ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth]; | ||||||
|  | 
 | ||||||
|  | 		// constructor call should be excluded
 | ||||||
|  | 		if (dynamic_cast<ContractDefinition const*>(modifierInvocation->getName()->getReferencedDeclaration())) | ||||||
|  | 		{ | ||||||
|  | 			++m_modifierDepth; | ||||||
|  | 			appendModifierOrFunctionCode(); | ||||||
|  | 			--m_modifierDepth; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName()); | 		ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName()); | ||||||
| 		CompilerContext::LocationSetter locationSetter(m_context, &modifier); | 		CompilerContext::LocationSetter locationSetter(m_context, &modifier); | ||||||
| 		solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), ""); | 		solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), ""); | ||||||
|  | |||||||
| @ -55,9 +55,8 @@ private: | |||||||
| 	/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
 | 	/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
 | ||||||
| 	/// with a new and initialized context. Adds the constructor code.
 | 	/// with a new and initialized context. Adds the constructor code.
 | ||||||
| 	void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); | 	void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); | ||||||
| 	void appendBaseConstructorCall(FunctionDefinition const& _constructor, | 	void appendBaseConstructor(FunctionDefinition const& _constructor); | ||||||
| 								   std::vector<ASTPointer<Expression>> const& _arguments); | 	void appendConstructor(FunctionDefinition const& _constructor); | ||||||
| 	void appendConstructorCall(FunctionDefinition const& _constructor); |  | ||||||
| 	void appendFunctionSelector(ContractDefinition const& _contract); | 	void appendFunctionSelector(ContractDefinition const& _contract); | ||||||
| 	/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
 | 	/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
 | ||||||
| 	/// From memory if @a _fromMemory is true, otherwise from call data.
 | 	/// From memory if @a _fromMemory is true, otherwise from call data.
 | ||||||
| @ -94,6 +93,8 @@ private: | |||||||
| 	unsigned m_modifierDepth = 0; | 	unsigned m_modifierDepth = 0; | ||||||
| 	FunctionDefinition const* m_currentFunction; | 	FunctionDefinition const* m_currentFunction; | ||||||
| 	unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
 | 	unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
 | ||||||
|  | 	// arguments for base constructors, filled in derived-to-base order
 | ||||||
|  | 	std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -51,8 +51,6 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) | |||||||
| void CompilerContext::startFunction(Declaration const& _function) | void CompilerContext::startFunction(Declaration const& _function) | ||||||
| { | { | ||||||
| 	m_functionsWithCode.insert(&_function); | 	m_functionsWithCode.insert(&_function); | ||||||
| 	m_localVariables.clear(); |  | ||||||
| 	m_asm.setDeposit(0); |  | ||||||
| 	*this << getFunctionEntryLabel(_function); | 	*this << getFunctionEntryLabel(_function); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -63,6 +61,12 @@ void CompilerContext::addVariable(VariableDeclaration const& _declaration, | |||||||
| 	m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent; | 	m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void CompilerContext::removeVariable(VariableDeclaration const& _declaration) | ||||||
|  | { | ||||||
|  | 	solAssert(m_localVariables.count(&_declaration), ""); | ||||||
|  | 	m_localVariables.erase(&_declaration); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration) | void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration) | ||||||
| { | { | ||||||
| 	LocationSetter locationSetter(*this, &_declaration); | 	LocationSetter locationSetter(*this, &_declaration); | ||||||
| @ -110,11 +114,8 @@ eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefiniti | |||||||
| 
 | 
 | ||||||
| eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base) | eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base) | ||||||
| { | { | ||||||
| 	// search for first contract after _base
 | 	auto it = getSuperContract(_base); | ||||||
| 	solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); | 	for (; it != m_inheritanceHierarchy.end(); ++it) | ||||||
| 	auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_base); |  | ||||||
| 	solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy."); |  | ||||||
| 	for (++it; it != m_inheritanceHierarchy.end(); ++it) |  | ||||||
| 		for (ASTPointer<FunctionDefinition> const& function: (*it)->getDefinedFunctions()) | 		for (ASTPointer<FunctionDefinition> const& function: (*it)->getDefinedFunctions()) | ||||||
| 			if (!function->isConstructor() && function->getName() == _name) | 			if (!function->isConstructor() && function->getName() == _name) | ||||||
| 				return getFunctionEntryLabel(*function); | 				return getFunctionEntryLabel(*function); | ||||||
| @ -122,6 +123,16 @@ eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _nam | |||||||
| 	return m_asm.newTag(); // not reached
 | 	return m_asm.newTag(); // not reached
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | FunctionDefinition const* CompilerContext::getNextConstructor(ContractDefinition const& _contract) const | ||||||
|  | { | ||||||
|  | 	vector<ContractDefinition const*>::const_iterator it = getSuperContract(_contract); | ||||||
|  | 	for (; it != m_inheritanceHierarchy.end(); ++it) | ||||||
|  | 		if ((*it)->getConstructor()) | ||||||
|  | 			return (*it)->getConstructor(); | ||||||
|  | 
 | ||||||
|  | 	return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| set<Declaration const*> CompilerContext::getFunctionsWithoutCode() | set<Declaration const*> CompilerContext::getFunctionsWithoutCode() | ||||||
| { | { | ||||||
| 	set<Declaration const*> functions; | 	set<Declaration const*> functions; | ||||||
| @ -201,5 +212,13 @@ CompilerContext& CompilerContext::operator<<(bytes const& _data) | |||||||
| 	return *this; | 	return *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const | ||||||
|  | { | ||||||
|  | 	solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); | ||||||
|  | 	auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_contract); | ||||||
|  | 	solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy."); | ||||||
|  | 	return ++it; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -43,11 +43,13 @@ public: | |||||||
| 	void addMagicGlobal(MagicVariableDeclaration const& _declaration); | 	void addMagicGlobal(MagicVariableDeclaration const& _declaration); | ||||||
| 	void addStateVariable(VariableDeclaration const& _declaration); | 	void addStateVariable(VariableDeclaration const& _declaration); | ||||||
| 	void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); | 	void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); | ||||||
|  | 	void removeVariable(VariableDeclaration const& _declaration); | ||||||
| 	void addAndInitializeVariable(VariableDeclaration const& _declaration); | 	void addAndInitializeVariable(VariableDeclaration const& _declaration); | ||||||
| 
 | 
 | ||||||
| 	void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; } | 	void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; } | ||||||
| 	bytes const& getCompiledContract(ContractDefinition const& _contract) const; | 	bytes const& getCompiledContract(ContractDefinition const& _contract) const; | ||||||
| 
 | 
 | ||||||
|  | 	void setStackOffset(int _offset) { m_asm.setDeposit(_offset); } | ||||||
| 	void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } | 	void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } | ||||||
| 	unsigned getStackHeight() const { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); } | 	unsigned getStackHeight() const { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); } | ||||||
| 
 | 
 | ||||||
| @ -62,6 +64,8 @@ public: | |||||||
| 	/// @returns the entry label of function with the given name from the most derived class just
 | 	/// @returns the entry label of function with the given name from the most derived class just
 | ||||||
| 	/// above _base in the current inheritance hierarchy.
 | 	/// above _base in the current inheritance hierarchy.
 | ||||||
| 	eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base); | 	eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base); | ||||||
|  | 	FunctionDefinition const* getNextConstructor(ContractDefinition const& _contract) const; | ||||||
|  | 
 | ||||||
| 	/// @returns the set of functions for which we still need to generate code
 | 	/// @returns the set of functions for which we still need to generate code
 | ||||||
| 	std::set<Declaration const*> getFunctionsWithoutCode(); | 	std::set<Declaration const*> getFunctionsWithoutCode(); | ||||||
| 	/// Resets function specific members, inserts the function entry label and marks the function
 | 	/// Resets function specific members, inserts the function entry label and marks the function
 | ||||||
| @ -126,9 +130,11 @@ public: | |||||||
| 		LocationSetter(CompilerContext& _compilerContext, ASTNode const* _node): | 		LocationSetter(CompilerContext& _compilerContext, ASTNode const* _node): | ||||||
| 			ScopeGuard(std::bind(&CompilerContext::popVisitedNodes, _compilerContext)) { _compilerContext.pushVisitedNodes(_node); } | 			ScopeGuard(std::bind(&CompilerContext::popVisitedNodes, _compilerContext)) { _compilerContext.pushVisitedNodes(_node); } | ||||||
| 	}; | 	}; | ||||||
| 	eth::Assembly m_asm; |  | ||||||
| private: |  | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|  | 	std::vector<ContractDefinition const*>::const_iterator getSuperContract(const ContractDefinition &_contract) const; | ||||||
|  | 
 | ||||||
|  | 	eth::Assembly m_asm; | ||||||
| 	/// Magic global variables like msg, tx or this, distinguished by type.
 | 	/// Magic global variables like msg, tx or this, distinguished by type.
 | ||||||
| 	std::set<Declaration const*> m_magicGlobals; | 	std::set<Declaration const*> m_magicGlobals; | ||||||
| 	/// Other already compiled contracts to be used in contract creation calls.
 | 	/// Other already compiled contracts to be used in contract creation calls.
 | ||||||
|  | |||||||
| @ -155,6 +155,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) | |||||||
| 	return getBytecode(); | 	return getBytecode(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | eth::AssemblyItems const& CompilerStack::getAssemblyItems(string const& _contractName) const | ||||||
|  | { | ||||||
|  | 	return getContract(_contractName).compiler->getAssemblyItems(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | eth::AssemblyItems const& CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const | ||||||
|  | { | ||||||
|  | 	return getContract(_contractName).compiler->getRuntimeAssemblyItems(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bytes const& CompilerStack::getBytecode(string const& _contractName) const | bytes const& CompilerStack::getBytecode(string const& _contractName) const | ||||||
| { | { | ||||||
| 	return getContract(_contractName).bytecode; | 	return getContract(_contractName).bytecode; | ||||||
|  | |||||||
| @ -26,12 +26,22 @@ | |||||||
| #include <ostream> | #include <ostream> | ||||||
| #include <string> | #include <string> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <vector> | ||||||
| #include <boost/noncopyable.hpp> | #include <boost/noncopyable.hpp> | ||||||
| #include <libdevcore/Common.h> | #include <libdevcore/Common.h> | ||||||
| #include <libdevcore/FixedHash.h> | #include <libdevcore/FixedHash.h> | ||||||
| 
 | 
 | ||||||
| namespace dev { | namespace dev | ||||||
| namespace solidity { | { | ||||||
|  | 
 | ||||||
|  | namespace eth | ||||||
|  | { | ||||||
|  | class AssemblyItem; | ||||||
|  | using AssemblyItems = std::vector<AssemblyItem>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace solidity | ||||||
|  | { | ||||||
| 
 | 
 | ||||||
| // forward declarations
 | // forward declarations
 | ||||||
| class Scanner; | class Scanner; | ||||||
| @ -85,6 +95,10 @@ public: | |||||||
| 	bytes const& getBytecode(std::string const& _contractName = "") const; | 	bytes const& getBytecode(std::string const& _contractName = "") const; | ||||||
| 	/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
 | 	/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
 | ||||||
| 	bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; | 	bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; | ||||||
|  | 	/// @returns normal contract assembly items
 | ||||||
|  | 	eth::AssemblyItems const& getAssemblyItems(std::string const& _contractName = "") const; | ||||||
|  | 	/// @returns runtime contract assembly items
 | ||||||
|  | 	eth::AssemblyItems const& getRuntimeAssemblyItems(std::string const& _contractName = "") const; | ||||||
| 	/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
 | 	/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
 | ||||||
| 	dev::h256 getContractCodeHash(std::string const& _contractName = "") const; | 	dev::h256 getContractCodeHash(std::string const& _contractName = "") const; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -164,134 +164,6 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari | |||||||
| 	m_context << u256(length) << u256(0) << eth::Instruction::SHA3; | 	m_context << u256(length) << u256(0) << eth::Instruction::SHA3; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CompilerUtils::copyByteArrayToStorage( |  | ||||||
| 	ArrayType const& _targetType, ArrayType const& _sourceType) const |  | ||||||
| { |  | ||||||
| 	// stack layout: [source_ref] target_ref (top)
 |  | ||||||
| 	// need to leave target_ref on the stack at the end
 |  | ||||||
| 	solAssert(_targetType.getLocation() == ArrayType::Location::Storage, ""); |  | ||||||
| 	solAssert(_targetType.isByteArray(), "Non byte arrays not yet implemented here."); |  | ||||||
| 	solAssert(_sourceType.isByteArray(), "Non byte arrays not yet implemented here."); |  | ||||||
| 
 |  | ||||||
| 	switch (_sourceType.getLocation()) |  | ||||||
| 	{ |  | ||||||
| 	case ArrayType::Location::CallData: |  | ||||||
| 	{ |  | ||||||
| 		// This also assumes that after "length" we only have zeros, i.e. it cannot be used to
 |  | ||||||
| 		// slice a byte array from calldata.
 |  | ||||||
| 
 |  | ||||||
| 		// stack: source_offset source_len target_ref
 |  | ||||||
| 		// fetch old length and convert to words
 |  | ||||||
| 		m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; |  | ||||||
| 		m_context << u256(31) << eth::Instruction::ADD |  | ||||||
| 				  << u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; |  | ||||||
| 		// stack here: source_offset source_len target_ref target_length_words
 |  | ||||||
| 		// actual array data is stored at SHA3(storage_offset)
 |  | ||||||
| 		m_context << eth::Instruction::DUP2; |  | ||||||
| 		CompilerUtils(m_context).computeHashStatic(); |  | ||||||
| 		// compute target_data_end
 |  | ||||||
| 		m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD |  | ||||||
| 				  << eth::Instruction::SWAP1; |  | ||||||
| 		// stack here: source_offset source_len target_ref target_data_end target_data_ref
 |  | ||||||
| 		// store length (in bytes)
 |  | ||||||
| 		m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::DUP5 |  | ||||||
| 			<< eth::Instruction::SSTORE; |  | ||||||
| 		// jump to end if length is zero
 |  | ||||||
| 		m_context << eth::Instruction::ISZERO; |  | ||||||
| 		eth::AssemblyItem copyLoopEnd = m_context.newTag(); |  | ||||||
| 		m_context.appendConditionalJumpTo(copyLoopEnd); |  | ||||||
| 		// store start offset
 |  | ||||||
| 		m_context << eth::Instruction::DUP5; |  | ||||||
| 		// stack now: source_offset source_len target_ref target_data_end target_data_ref calldata_offset
 |  | ||||||
| 		eth::AssemblyItem copyLoopStart = m_context.newTag(); |  | ||||||
| 		m_context << copyLoopStart |  | ||||||
| 				  // copy from calldata and store
 |  | ||||||
| 				  << eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD |  | ||||||
| 				  << eth::Instruction::DUP3 << eth::Instruction::SSTORE |  | ||||||
| 				  // increment target_data_ref by 1
 |  | ||||||
| 				  << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD |  | ||||||
| 				  // increment calldata_offset by 32
 |  | ||||||
| 				  << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD |  | ||||||
| 				  // check for loop condition
 |  | ||||||
| 				  << eth::Instruction::DUP1 << eth::Instruction::DUP6 << eth::Instruction::GT; |  | ||||||
| 		m_context.appendConditionalJumpTo(copyLoopStart); |  | ||||||
| 		m_context << eth::Instruction::POP; |  | ||||||
| 		m_context << copyLoopEnd; |  | ||||||
| 
 |  | ||||||
| 		// now clear leftover bytes of the old value
 |  | ||||||
| 		// stack now: source_offset source_len target_ref target_data_end target_data_ref
 |  | ||||||
| 		clearStorageLoop(); |  | ||||||
| 		// stack now: source_offset source_len target_ref target_data_end
 |  | ||||||
| 
 |  | ||||||
| 		m_context << eth::Instruction::POP << eth::Instruction::SWAP2 |  | ||||||
| 			<< eth::Instruction::POP << eth::Instruction::POP; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| 	case ArrayType::Location::Storage: |  | ||||||
| 	{ |  | ||||||
| 		// this copies source to target and also clears target if it was larger
 |  | ||||||
| 
 |  | ||||||
| 		// stack: source_ref target_ref
 |  | ||||||
| 		// store target_ref
 |  | ||||||
| 		m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; |  | ||||||
| 		// fetch lengthes
 |  | ||||||
| 		m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP2 |  | ||||||
| 				  << eth::Instruction::DUP1 << eth::Instruction::SLOAD; |  | ||||||
| 		// stack: target_ref target_len_bytes target_ref source_ref source_len_bytes
 |  | ||||||
| 		// store new target length
 |  | ||||||
| 		m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE; |  | ||||||
| 		// compute hashes (data positions)
 |  | ||||||
| 		m_context << eth::Instruction::SWAP2; |  | ||||||
| 		CompilerUtils(m_context).computeHashStatic(); |  | ||||||
| 		m_context << eth::Instruction::SWAP1; |  | ||||||
| 		CompilerUtils(m_context).computeHashStatic(); |  | ||||||
| 		// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos
 |  | ||||||
| 		// convert lengthes from bytes to storage slots
 |  | ||||||
| 		m_context << u256(31) << u256(32) << eth::Instruction::DUP1 << eth::Instruction::DUP3 |  | ||||||
| 				  << eth::Instruction::DUP8 << eth::Instruction::ADD << eth::Instruction::DIV |  | ||||||
| 				  << eth::Instruction::SWAP2 |  | ||||||
| 				  << eth::Instruction::DUP6 << eth::Instruction::ADD << eth::Instruction::DIV; |  | ||||||
| 		// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len
 |  | ||||||
| 		// @todo we might be able to go without a third counter
 |  | ||||||
| 		m_context << u256(0); |  | ||||||
| 		// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
 |  | ||||||
| 		eth::AssemblyItem copyLoopStart = m_context.newTag(); |  | ||||||
| 		m_context << copyLoopStart; |  | ||||||
| 		// check for loop condition
 |  | ||||||
| 		m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 |  | ||||||
| 				   << eth::Instruction::GT << eth::Instruction::ISZERO; |  | ||||||
| 		eth::AssemblyItem copyLoopEnd = m_context.newTag(); |  | ||||||
| 		m_context.appendConditionalJumpTo(copyLoopEnd); |  | ||||||
| 		// copy
 |  | ||||||
| 		m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD |  | ||||||
| 				  << eth::Instruction::SLOAD |  | ||||||
| 				  << eth::Instruction::DUP6 << eth::Instruction::DUP3 << eth::Instruction::ADD |  | ||||||
| 				  << eth::Instruction::SSTORE; |  | ||||||
| 		// increment
 |  | ||||||
| 		m_context << u256(1) << eth::Instruction::ADD; |  | ||||||
| 		m_context.appendJumpTo(copyLoopStart); |  | ||||||
| 		m_context << copyLoopEnd; |  | ||||||
| 
 |  | ||||||
| 		// zero-out leftovers in target
 |  | ||||||
| 		// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
 |  | ||||||
| 		// add counter to target_data_pos
 |  | ||||||
| 		m_context << eth::Instruction::DUP5 << eth::Instruction::ADD |  | ||||||
| 				  << eth::Instruction::SWAP5 << eth::Instruction::POP; |  | ||||||
| 		// stack: target_ref target_len_bytes target_data_pos_updated target_data_pos source_data_pos target_len source_len
 |  | ||||||
| 		// add length to target_data_pos to get target_data_end
 |  | ||||||
| 		m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD |  | ||||||
| 				  << eth::Instruction::SWAP4 |  | ||||||
| 				  << eth::Instruction::POP  << eth::Instruction::POP << eth::Instruction::POP; |  | ||||||
| 		// stack: target_ref target_data_end target_data_pos_updated
 |  | ||||||
| 		clearStorageLoop(); |  | ||||||
| 		m_context << eth::Instruction::POP; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| 	default: |  | ||||||
| 		solAssert(false, "Given byte array location not implemented."); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) | unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) | ||||||
| { | { | ||||||
| 	unsigned _encodedSize = _type.getCalldataEncodedSize(); | 	unsigned _encodedSize = _type.getCalldataEncodedSize(); | ||||||
| @ -316,28 +188,6 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda | |||||||
| 	return numBytes; | 	return numBytes; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CompilerUtils::clearByteArray(ArrayType const& _type) const |  | ||||||
| { |  | ||||||
| 	solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); |  | ||||||
| 	solAssert(_type.isByteArray(), "Non byte arrays not yet implemented here."); |  | ||||||
| 
 |  | ||||||
| 	// fetch length
 |  | ||||||
| 	m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; |  | ||||||
| 	// set length to zero
 |  | ||||||
| 	m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE; |  | ||||||
| 	// convert length from bytes to storage slots
 |  | ||||||
| 	m_context << u256(31) << eth::Instruction::ADD |  | ||||||
| 			  << u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; |  | ||||||
| 	// compute data positions
 |  | ||||||
| 	m_context << eth::Instruction::SWAP1; |  | ||||||
| 	CompilerUtils(m_context).computeHashStatic(); |  | ||||||
| 	// stack: len data_pos
 |  | ||||||
| 	m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD |  | ||||||
| 			  << eth::Instruction::SWAP1; |  | ||||||
| 	clearStorageLoop(); |  | ||||||
| 	// cleanup
 |  | ||||||
| 	m_context << eth::Instruction::POP; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const | unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const | ||||||
| { | { | ||||||
| @ -356,25 +206,5 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBou | |||||||
| 	return numBytes; | 	return numBytes; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CompilerUtils::clearStorageLoop() const |  | ||||||
| { |  | ||||||
| 	// stack: end_pos pos
 |  | ||||||
| 	eth::AssemblyItem loopStart = m_context.newTag(); |  | ||||||
| 	m_context << loopStart; |  | ||||||
| 	// check for loop condition
 |  | ||||||
| 	m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 |  | ||||||
| 			   << eth::Instruction::GT << eth::Instruction::ISZERO; |  | ||||||
| 	eth::AssemblyItem zeroLoopEnd = m_context.newTag(); |  | ||||||
| 	m_context.appendConditionalJumpTo(zeroLoopEnd); |  | ||||||
| 	// zero out
 |  | ||||||
| 	m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE; |  | ||||||
| 	// increment
 |  | ||||||
| 	m_context << u256(1) << eth::Instruction::ADD; |  | ||||||
| 	m_context.appendJumpTo(loopStart); |  | ||||||
| 	// cleanup
 |  | ||||||
| 	m_context << zeroLoopEnd; |  | ||||||
| 	m_context << eth::Instruction::POP; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -79,15 +79,6 @@ public: | |||||||
| 	/// @note Only works for types of fixed size.
 | 	/// @note Only works for types of fixed size.
 | ||||||
| 	void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false); | 	void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false); | ||||||
| 
 | 
 | ||||||
| 	/// Copies a byte array to a byte array in storage.
 |  | ||||||
| 	/// Stack pre: [source_reference] target_reference
 |  | ||||||
| 	/// Stack post: target_reference
 |  | ||||||
| 	void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const; |  | ||||||
| 	/// Clears the length and data elements of the byte array referenced on the stack.
 |  | ||||||
| 	/// Stack pre: reference
 |  | ||||||
| 	/// Stack post:
 |  | ||||||
| 	void clearByteArray(ArrayType const& _type) const; |  | ||||||
| 
 |  | ||||||
| 	/// Bytes we need to the start of call data.
 | 	/// Bytes we need to the start of call data.
 | ||||||
| 	///  - The size in bytes of the function (hash) identifier.
 | 	///  - The size in bytes of the function (hash) identifier.
 | ||||||
| 	static const unsigned int dataStartOffset; | 	static const unsigned int dataStartOffset; | ||||||
| @ -97,10 +88,6 @@ private: | |||||||
| 	unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const; | 	unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const; | ||||||
| 	/// Loads type from memory assuming memory offset is on stack top.
 | 	/// Loads type from memory assuming memory offset is on stack top.
 | ||||||
| 	unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries); | 	unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries); | ||||||
| 	/// Appends a loop that clears a sequence of storage slots (excluding end).
 |  | ||||||
| 	/// Stack pre: end_ref start_ref
 |  | ||||||
| 	/// Stack post: end_ref
 |  | ||||||
| 	void clearStorageLoop() const; |  | ||||||
| 
 | 
 | ||||||
| 	CompilerContext& m_context; | 	CompilerContext& m_context; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -93,7 +93,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& | |||||||
| 			m_context << eth::Instruction::DUP1 | 			m_context << eth::Instruction::DUP1 | ||||||
| 					  << structType->getStorageOffsetOfMember(names[i]) | 					  << structType->getStorageOffsetOfMember(names[i]) | ||||||
| 					  << eth::Instruction::ADD; | 					  << eth::Instruction::ADD; | ||||||
| 			StorageItem(m_context, types[i]).retrieveValue(SourceLocation(), true); | 			StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true); | ||||||
| 			solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); | 			solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); | ||||||
| 			m_context << eth::Instruction::SWAP1; | 			m_context << eth::Instruction::SWAP1; | ||||||
| 			retSizeOnStack += types[i]->getSizeOnStack(); | 			retSizeOnStack += types[i]->getSizeOnStack(); | ||||||
| @ -104,7 +104,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& | |||||||
| 	{ | 	{ | ||||||
| 		// simple value
 | 		// simple value
 | ||||||
| 		solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); | 		solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); | ||||||
| 		StorageItem(m_context, returnType).retrieveValue(SourceLocation(), true); | 		StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); | ||||||
| 		retSizeOnStack = returnType->getSizeOnStack(); | 		retSizeOnStack = returnType->getSizeOnStack(); | ||||||
| 	} | 	} | ||||||
| 	solAssert(retSizeOnStack <= 15, "Stack too deep."); | 	solAssert(retSizeOnStack <= 15, "Stack too deep."); | ||||||
| @ -680,7 +680,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) | |||||||
| 				m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; | 				m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; | ||||||
| 				break; | 				break; | ||||||
| 			case ArrayType::Location::Storage: | 			case ArrayType::Location::Storage: | ||||||
| 				setLValueToStorageItem(_memberAccess); | 				setLValue<StorageArrayLength>(_memberAccess, type); | ||||||
| 				break; | 				break; | ||||||
| 			default: | 			default: | ||||||
| 				solAssert(false, "Unsupported array location."); | 				solAssert(false, "Unsupported array location."); | ||||||
| @ -1047,7 +1047,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio | |||||||
| 
 | 
 | ||||||
| void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) | void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) | ||||||
| { | { | ||||||
| 	setLValue<StorageItem>(_expression, _expression.getType()); | 	setLValue<StorageItem>(_expression, *_expression.getType()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										122
									
								
								LValue.cpp
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								LValue.cpp
									
									
									
									
									
								
							| @ -32,15 +32,14 @@ using namespace solidity; | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration): | StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration): | ||||||
| 	LValue(_compilerContext, _declaration.getType()), | 	LValue(_compilerContext, *_declaration.getType()), | ||||||
| 	m_baseStackOffset(m_context.getBaseStackOffsetOfVariable(_declaration)), | 	m_baseStackOffset(m_context.getBaseStackOffsetOfVariable(_declaration)), | ||||||
| 	m_size(m_dataType->getSizeOnStack()) | 	m_size(m_dataType.getSizeOnStack()) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StackVariable::retrieveValue(SourceLocation const& _location, bool _remove) const | void StackVariable::retrieveValue(SourceLocation const& _location, bool) const | ||||||
| { | { | ||||||
| 	(void)_remove; |  | ||||||
| 	unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); | 	unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); | ||||||
| 	if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
 | 	if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
 | ||||||
| 		BOOST_THROW_EXCEPTION(CompilerError() | 		BOOST_THROW_EXCEPTION(CompilerError() | ||||||
| @ -49,9 +48,8 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool _remove) | |||||||
| 		m_context << eth::dupInstruction(stackPos + 1); | 		m_context << eth::dupInstruction(stackPos + 1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StackVariable::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const | void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const | ||||||
| { | { | ||||||
| 	(void)_sourceType; |  | ||||||
| 	unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; | 	unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; | ||||||
| 	if (stackDiff > 16) | 	if (stackDiff > 16) | ||||||
| 		BOOST_THROW_EXCEPTION(CompilerError() | 		BOOST_THROW_EXCEPTION(CompilerError() | ||||||
| @ -63,7 +61,7 @@ void StackVariable::storeValue(Type const& _sourceType, SourceLocation const& _l | |||||||
| 		retrieveValue(_location); | 		retrieveValue(_location); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StackVariable::setToZero(SourceLocation const& _location) const | void StackVariable::setToZero(SourceLocation const& _location, bool) const | ||||||
| { | { | ||||||
| 	unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset); | 	unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset); | ||||||
| 	if (stackDiff > 16) | 	if (stackDiff > 16) | ||||||
| @ -77,20 +75,20 @@ void StackVariable::setToZero(SourceLocation const& _location) const | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration): | StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration): | ||||||
| 	StorageItem(_compilerContext, _declaration.getType()) | 	StorageItem(_compilerContext, *_declaration.getType()) | ||||||
| { | { | ||||||
| 	m_context << m_context.getStorageLocationOfVariable(_declaration); | 	m_context << m_context.getStorageLocationOfVariable(_declaration); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StorageItem::StorageItem(CompilerContext& _compilerContext, TypePointer const& _type): | StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type): | ||||||
| 	LValue(_compilerContext, _type) | 	LValue(_compilerContext, _type) | ||||||
| { | { | ||||||
| 	if (m_dataType->isValueType()) | 	if (m_dataType.isValueType()) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(m_dataType->getStorageSize() == m_dataType->getSizeOnStack(), ""); | 		solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), ""); | ||||||
| 		solAssert(m_dataType->getStorageSize() <= numeric_limits<unsigned>::max(), | 		solAssert(m_dataType.getStorageSize() <= numeric_limits<unsigned>::max(), | ||||||
| 			"The storage size of " + m_dataType->toString() + " should fit in an unsigned"); | 			"The storage size of " + m_dataType.toString() + " should fit in an unsigned"); | ||||||
| 		m_size = unsigned(m_dataType->getStorageSize()); | 		m_size = unsigned(m_dataType.getStorageSize()); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 		m_size = 0; // unused
 | 		m_size = 0; // unused
 | ||||||
| @ -98,7 +96,7 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, TypePointer const& _ | |||||||
| 
 | 
 | ||||||
| void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const | void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const | ||||||
| { | { | ||||||
| 	if (!m_dataType->isValueType()) | 	if (!m_dataType.isValueType()) | ||||||
| 		return; // no distinction between value and reference for non-value types
 | 		return; // no distinction between value and reference for non-value types
 | ||||||
| 	if (!_remove) | 	if (!_remove) | ||||||
| 		m_context << eth::Instruction::DUP1; | 		m_context << eth::Instruction::DUP1; | ||||||
| @ -118,7 +116,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const | |||||||
| void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const | void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const | ||||||
| { | { | ||||||
| 	// stack layout: value value ... value target_ref
 | 	// stack layout: value value ... value target_ref
 | ||||||
| 	if (m_dataType->isValueType()) | 	if (m_dataType.isValueType()) | ||||||
| 	{ | 	{ | ||||||
| 		if (!_move) // copy values
 | 		if (!_move) // copy values
 | ||||||
| 		{ | 		{ | ||||||
| @ -143,20 +141,20 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc | |||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(_sourceType.getCategory() == m_dataType->getCategory(), | 		solAssert(_sourceType.getCategory() == m_dataType.getCategory(), | ||||||
| 			"Wrong type conversation for assignment."); | 			"Wrong type conversation for assignment."); | ||||||
| 		if (m_dataType->getCategory() == Type::Category::Array) | 		if (m_dataType.getCategory() == Type::Category::Array) | ||||||
| 		{ | 		{ | ||||||
| 			CompilerUtils(m_context).copyByteArrayToStorage( | 			ArrayUtils(m_context).copyArrayToStorage( | ||||||
| 						dynamic_cast<ArrayType const&>(*m_dataType), | 						dynamic_cast<ArrayType const&>(m_dataType), | ||||||
| 						dynamic_cast<ArrayType const&>(_sourceType)); | 						dynamic_cast<ArrayType const&>(_sourceType)); | ||||||
| 			if (_move) | 			if (_move) | ||||||
| 				m_context << eth::Instruction::POP; | 				m_context << eth::Instruction::POP; | ||||||
| 		} | 		} | ||||||
| 		else if (m_dataType->getCategory() == Type::Category::Struct) | 		else if (m_dataType.getCategory() == Type::Category::Struct) | ||||||
| 		{ | 		{ | ||||||
| 			// stack layout: source_ref target_ref
 | 			// stack layout: source_ref target_ref
 | ||||||
| 			auto const& structType = dynamic_cast<StructType const&>(*m_dataType); | 			auto const& structType = dynamic_cast<StructType const&>(m_dataType); | ||||||
| 			solAssert(structType == _sourceType, "Struct assignment with conversion."); | 			solAssert(structType == _sourceType, "Struct assignment with conversion."); | ||||||
| 			for (auto const& member: structType.getMembers()) | 			for (auto const& member: structType.getMembers()) | ||||||
| 			{ | 			{ | ||||||
| @ -167,12 +165,12 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc | |||||||
| 				m_context << structType.getStorageOffsetOfMember(member.first) | 				m_context << structType.getStorageOffsetOfMember(member.first) | ||||||
| 					<< eth::Instruction::DUP3 << eth::Instruction::DUP2 << eth::Instruction::ADD; | 					<< eth::Instruction::DUP3 << eth::Instruction::DUP2 << eth::Instruction::ADD; | ||||||
| 				// stack: source_ref target_ref member_offset source_member_ref
 | 				// stack: source_ref target_ref member_offset source_member_ref
 | ||||||
| 				StorageItem(m_context, memberType).retrieveValue(_location, true); | 				StorageItem(m_context, *memberType).retrieveValue(_location, true); | ||||||
| 				// stack: source_ref target_ref member_offset source_value...
 | 				// stack: source_ref target_ref member_offset source_value...
 | ||||||
| 				m_context << eth::dupInstruction(2 + memberType->getSizeOnStack()) | 				m_context << eth::dupInstruction(2 + memberType->getSizeOnStack()) | ||||||
| 					<< eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD; | 					<< eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD; | ||||||
| 				// stack: source_ref target_ref member_offset source_value... target_member_ref
 | 				// stack: source_ref target_ref member_offset source_value... target_member_ref
 | ||||||
| 				StorageItem(m_context, memberType).storeValue(*memberType, _location, true); | 				StorageItem(m_context, *memberType).storeValue(*memberType, _location, true); | ||||||
| 				m_context << eth::Instruction::POP; | 				m_context << eth::Instruction::POP; | ||||||
| 			} | 			} | ||||||
| 			if (_move) | 			if (_move) | ||||||
| @ -187,16 +185,18 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const | ||||||
| void StorageItem::setToZero(SourceLocation const& _location) const |  | ||||||
| { | { | ||||||
| 	(void)_location; | 	if (m_dataType.getCategory() == Type::Category::Array) | ||||||
| 	if (m_dataType->getCategory() == Type::Category::Array) | 	{ | ||||||
| 		CompilerUtils(m_context).clearByteArray(dynamic_cast<ArrayType const&>(*m_dataType)); | 		if (!_removeReference) | ||||||
| 	else if (m_dataType->getCategory() == Type::Category::Struct) | 			m_context << eth::Instruction::DUP1; | ||||||
|  | 		ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(m_dataType)); | ||||||
|  | 	} | ||||||
|  | 	else if (m_dataType.getCategory() == Type::Category::Struct) | ||||||
| 	{ | 	{ | ||||||
| 		// stack layout: ref
 | 		// stack layout: ref
 | ||||||
| 		auto const& structType = dynamic_cast<StructType const&>(*m_dataType); | 		auto const& structType = dynamic_cast<StructType const&>(m_dataType); | ||||||
| 		for (auto const& member: structType.getMembers()) | 		for (auto const& member: structType.getMembers()) | ||||||
| 		{ | 		{ | ||||||
| 			// zero each member that is not a mapping
 | 			// zero each member that is not a mapping
 | ||||||
| @ -205,19 +205,61 @@ void StorageItem::setToZero(SourceLocation const& _location) const | |||||||
| 				continue; | 				continue; | ||||||
| 			m_context << structType.getStorageOffsetOfMember(member.first) | 			m_context << structType.getStorageOffsetOfMember(member.first) | ||||||
| 				<< eth::Instruction::DUP2 << eth::Instruction::ADD; | 				<< eth::Instruction::DUP2 << eth::Instruction::ADD; | ||||||
| 			StorageItem(m_context, memberType).setToZero(); | 			StorageItem(m_context, *memberType).setToZero(); | ||||||
| 		} | 		} | ||||||
| 		m_context << eth::Instruction::POP; | 		if (_removeReference) | ||||||
|  | 			m_context << eth::Instruction::POP; | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		if (m_size == 0) | 		if (m_size == 0 && _removeReference) | ||||||
| 			m_context << eth::Instruction::POP; | 			m_context << eth::Instruction::POP; | ||||||
| 		for (unsigned i = 0; i < m_size; ++i) | 		else if (m_size == 1) | ||||||
| 			if (i + 1 >= m_size) | 			m_context | ||||||
| 				m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; | 				<< u256(0) << (_removeReference ? eth::Instruction::SWAP1 : eth::Instruction::DUP2) | ||||||
| 			else | 				<< eth::Instruction::SSTORE; | ||||||
| 				m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE | 		else | ||||||
| 					<< u256(1) << eth::Instruction::ADD; | 		{ | ||||||
|  | 			if (!_removeReference) | ||||||
|  | 				m_context << eth::Instruction::DUP1; | ||||||
|  | 			for (unsigned i = 0; i < m_size; ++i) | ||||||
|  | 				if (i + 1 >= m_size) | ||||||
|  | 					m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; | ||||||
|  | 				else | ||||||
|  | 					m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE | ||||||
|  | 						<< u256(1) << eth::Instruction::ADD; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const ArrayType& _arrayType): | ||||||
|  | 	LValue(_compilerContext, *_arrayType.getMemberType("length")), | ||||||
|  | 	m_arrayType(_arrayType) | ||||||
|  | { | ||||||
|  | 	solAssert(m_arrayType.isDynamicallySized(), ""); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const | ||||||
|  | { | ||||||
|  | 	if (!_remove) | ||||||
|  | 		m_context << eth::Instruction::DUP1; | ||||||
|  | 	m_context << eth::Instruction::SLOAD; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _move) const | ||||||
|  | { | ||||||
|  | 	if (_move) | ||||||
|  | 		m_context << eth::Instruction::SWAP1; | ||||||
|  | 	else | ||||||
|  | 		m_context << eth::Instruction::DUP2; | ||||||
|  | 	ArrayUtils(m_context).resizeDynamicArray(m_arrayType); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const | ||||||
|  | { | ||||||
|  | 	if (!_removeReference) | ||||||
|  | 		m_context << eth::Instruction::DUP1; | ||||||
|  | 	ArrayUtils(m_context).clearDynamicArray(m_arrayType); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | |||||||
							
								
								
									
										44
									
								
								LValue.h
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								LValue.h
									
									
									
									
									
								
							| @ -24,6 +24,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <libevmcore/SourceLocation.h> | #include <libevmcore/SourceLocation.h> | ||||||
|  | #include <libsolidity/ArrayUtils.h> | ||||||
| 
 | 
 | ||||||
| namespace dev | namespace dev | ||||||
| { | { | ||||||
| @ -32,6 +33,7 @@ namespace solidity | |||||||
| 
 | 
 | ||||||
| class Declaration; | class Declaration; | ||||||
| class Type; | class Type; | ||||||
|  | class ArrayType; | ||||||
| class CompilerContext; | class CompilerContext; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -40,7 +42,7 @@ class CompilerContext; | |||||||
| class LValue | class LValue | ||||||
| { | { | ||||||
| protected: | protected: | ||||||
| 	LValue(CompilerContext& _compilerContext, std::shared_ptr<Type const> const& _dataType): | 	LValue(CompilerContext& _compilerContext, Type const& _dataType): | ||||||
| 		m_context(_compilerContext), m_dataType(_dataType) {} | 		m_context(_compilerContext), m_dataType(_dataType) {} | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| @ -56,13 +58,14 @@ public: | |||||||
| 	/// Stack post: if !_move: value_of(lvalue_ref)
 | 	/// Stack post: if !_move: value_of(lvalue_ref)
 | ||||||
| 	virtual void storeValue(Type const& _sourceType, | 	virtual void storeValue(Type const& _sourceType, | ||||||
| 		SourceLocation const& _location = SourceLocation(), bool _move = false) const = 0; | 		SourceLocation const& _location = SourceLocation(), bool _move = false) const = 0; | ||||||
| 	/// Stores zero in the lvalue.
 | 	/// Stores zero in the lvalue. Removes the reference from the stack if @a _removeReference is true.
 | ||||||
| 	/// @a _location is the source location of the requested operation
 | 	/// @a _location is the source location of the requested operation
 | ||||||
| 	virtual void setToZero(SourceLocation const& _location = SourceLocation()) const = 0; | 	virtual void setToZero( | ||||||
|  | 		SourceLocation const& _location = SourceLocation(), bool _removeReference = true) const = 0; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 	CompilerContext& m_context; | 	CompilerContext& m_context; | ||||||
| 	std::shared_ptr<Type const> m_dataType; | 	Type const& m_dataType; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -71,13 +74,14 @@ protected: | |||||||
| class StackVariable: public LValue | class StackVariable: public LValue | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	explicit StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration); | 	StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration); | ||||||
| 
 | 
 | ||||||
| 	virtual bool storesReferenceOnStack() const { return false; } | 	virtual bool storesReferenceOnStack() const { return false; } | ||||||
| 	virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; | 	virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; | ||||||
| 	virtual void storeValue(Type const& _sourceType, | 	virtual void storeValue(Type const& _sourceType, | ||||||
| 		SourceLocation const& _location = SourceLocation(), bool _move = false) const override; | 		SourceLocation const& _location = SourceLocation(), bool _move = false) const override; | ||||||
| 	virtual void setToZero(SourceLocation const& _location = SourceLocation()) const override; | 	virtual void setToZero( | ||||||
|  | 		SourceLocation const& _location = SourceLocation(), bool _removeReference = true) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	/// Base stack offset (@see CompilerContext::getBaseStackOffsetOfVariable) of the local variable.
 | 	/// Base stack offset (@see CompilerContext::getBaseStackOffsetOfVariable) of the local variable.
 | ||||||
| @ -93,14 +97,15 @@ class StorageItem: public LValue | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	/// Constructs the LValue and pushes the location of @a _declaration onto the stack.
 | 	/// Constructs the LValue and pushes the location of @a _declaration onto the stack.
 | ||||||
| 	explicit StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration); | 	StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration); | ||||||
| 	/// Constructs the LValue and assumes that the storage reference is already on the stack.
 | 	/// Constructs the LValue and assumes that the storage reference is already on the stack.
 | ||||||
| 	explicit StorageItem(CompilerContext& _compilerContext, std::shared_ptr<Type const> const& _type); | 	StorageItem(CompilerContext& _compilerContext, Type const& _type); | ||||||
| 	virtual bool storesReferenceOnStack() const { return true; } | 	virtual bool storesReferenceOnStack() const { return true; } | ||||||
| 	virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; | 	virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; | ||||||
| 	virtual void storeValue(Type const& _sourceType, | 	virtual void storeValue(Type const& _sourceType, | ||||||
| 		SourceLocation const& _location = SourceLocation(), bool _move = false) const override; | 		SourceLocation const& _location = SourceLocation(), bool _move = false) const override; | ||||||
| 	virtual void setToZero(SourceLocation const& _location = SourceLocation()) const override; | 	virtual void setToZero( | ||||||
|  | 		SourceLocation const& _location = SourceLocation(), bool _removeReference = true) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	/// Number of stack elements occupied by the value (not the reference).
 | 	/// Number of stack elements occupied by the value (not the reference).
 | ||||||
| @ -108,5 +113,26 @@ private: | |||||||
| 	unsigned m_size; | 	unsigned m_size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reference to the "length" member of a dynamically-sized array. This is an LValue with special | ||||||
|  |  * semantics since assignments to it might reduce its length and thus arrays members have to be | ||||||
|  |  * deleted. | ||||||
|  |  */ | ||||||
|  | class StorageArrayLength: public LValue | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	/// Constructs the LValue, assumes that the reference to the array head is already on the stack.
 | ||||||
|  | 	StorageArrayLength(CompilerContext& _compilerContext, ArrayType const& _arrayType); | ||||||
|  | 	virtual bool storesReferenceOnStack() const { return true; } | ||||||
|  | 	virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; | ||||||
|  | 	virtual void storeValue(Type const& _sourceType, | ||||||
|  | 		SourceLocation const& _location = SourceLocation(), bool _move = false) const override; | ||||||
|  | 	virtual void setToZero( | ||||||
|  | 		SourceLocation const& _location = SourceLocation(), bool _removeReference = true) const override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	ArrayType const& m_arrayType; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -44,8 +44,14 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, | |||||||
| 	tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); | 	tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); | ||||||
| 	if (startLine == endLine) | 	if (startLine == endLine) | ||||||
| 	{ | 	{ | ||||||
| 		_stream << _scanner.getLineAtPosition(_location.start) << endl | 		string line = _scanner.getLineAtPosition(_location.start); | ||||||
| 				<< string(startColumn, ' ') << "^"; | 		_stream << line << endl; | ||||||
|  | 		std::for_each(line.cbegin(), line.cbegin() + startColumn, | ||||||
|  | 			[&_stream](char const& ch) | ||||||
|  | 			{ | ||||||
|  | 				_stream << (ch == '\t' ? '\t' : ' '); | ||||||
|  | 			}); | ||||||
|  | 		_stream << "^"; | ||||||
| 		if (endColumn > startColumn + 2) | 		if (endColumn > startColumn + 2) | ||||||
| 			_stream << string(endColumn - startColumn - 2, '-'); | 			_stream << string(endColumn - startColumn - 2, '-'); | ||||||
| 		if (endColumn > startColumn + 1) | 		if (endColumn > startColumn + 1) | ||||||
|  | |||||||
							
								
								
									
										29
									
								
								Types.cpp
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								Types.cpp
									
									
									
									
									
								
							| @ -537,7 +537,19 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const | |||||||
| 
 | 
 | ||||||
| bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const | bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const | ||||||
| { | { | ||||||
| 	return _convertTo.getCategory() == getCategory(); | 	if (_convertTo.getCategory() != getCategory()) | ||||||
|  | 		return false; | ||||||
|  | 	auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo); | ||||||
|  | 	// let us not allow assignment to memory arrays for now
 | ||||||
|  | 	if (convertTo.getLocation() != Location::Storage) | ||||||
|  | 		return false; | ||||||
|  | 	if (convertTo.isByteArray() != isByteArray()) | ||||||
|  | 		return false; | ||||||
|  | 	if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) | ||||||
|  | 		return false; | ||||||
|  | 	if (convertTo.isDynamicallySized()) | ||||||
|  | 		return true; | ||||||
|  | 	return !isDynamicallySized() && convertTo.getLength() >= getLength(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const | TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const | ||||||
| @ -552,7 +564,10 @@ bool ArrayType::operator==(Type const& _other) const | |||||||
| 	if (_other.getCategory() != getCategory()) | 	if (_other.getCategory() != getCategory()) | ||||||
| 		return false; | 		return false; | ||||||
| 	ArrayType const& other = dynamic_cast<ArrayType const&>(_other); | 	ArrayType const& other = dynamic_cast<ArrayType const&>(_other); | ||||||
| 	return other.m_location == m_location; | 	if (other.m_location != m_location || other.isByteArray() != isByteArray() || | ||||||
|  | 			other.isDynamicallySized() != isDynamicallySized()) | ||||||
|  | 		return false; | ||||||
|  | 	return isDynamicallySized() || getLength()  == other.getLength(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u256 ArrayType::getStorageSize() const | u256 ArrayType::getStorageSize() const | ||||||
| @ -628,8 +643,7 @@ MemberList const& ContractType::getMembers() const | |||||||
| 		{ | 		{ | ||||||
| 			for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) | 			for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) | ||||||
| 				for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions()) | 				for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions()) | ||||||
| 					if (!function->isConstructor() && !function->getName().empty()&& | 					if (function->isVisibleInDerivedContracts()) | ||||||
| 							function->isVisibleInDerivedContracts()) |  | ||||||
| 						members.push_back(make_pair(function->getName(), make_shared<FunctionType>(*function, true))); | 						members.push_back(make_pair(function->getName(), make_shared<FunctionType>(*function, true))); | ||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| @ -1024,10 +1038,9 @@ MemberList const& TypeType::getMembers() const | |||||||
| 			vector<ContractDefinition const*> currentBases = m_currentContract->getLinearizedBaseContracts(); | 			vector<ContractDefinition const*> currentBases = m_currentContract->getLinearizedBaseContracts(); | ||||||
| 			if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) | 			if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) | ||||||
| 				// We are accessing the type of a base contract, so add all public and protected
 | 				// We are accessing the type of a base contract, so add all public and protected
 | ||||||
| 				// functions. Note that this does not add inherited functions on purpose.
 | 				// members. Note that this does not add inherited functions on purpose.
 | ||||||
| 				for (ASTPointer<FunctionDefinition> const& f: contract.getDefinedFunctions()) | 				for (Declaration const* decl: contract.getInheritableMembers()) | ||||||
| 					if (!f->isConstructor() && !f->getName().empty() && f->isVisibleInDerivedContracts()) | 					members.push_back(make_pair(decl->getName(), decl->getType())); | ||||||
| 						members.push_back(make_pair(f->getName(), make_shared<FunctionType>(*f))); |  | ||||||
| 		} | 		} | ||||||
| 		else if (m_actualType->getCategory() == Category::Enum) | 		else if (m_actualType->getCategory() == Category::Enum) | ||||||
| 		{ | 		{ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user