<commit_before>/*
    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 <<EMAIL>>
 * @date 2014
 * Solidity data types
 */

#include <libsolidity/Types.h>
#include <limits>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/Utils.h>
#include <libsolidity/AST.h>

using namespace std;

namespace dev
{
namespace solidity
{

void StorageOffsets::computeOffsets(TypePointers const& _types)
{
	bigint slotOffset = 0;
	unsigned byteOffset = 0;
	map<size_t, pair<u256, unsigned>> offsets;
	for (size_t i = 0; i < _types.size(); ++i)
	{
		TypePointer const& type = _types[i];
		if (!type->canBeStored())
			continue;
		if (byteOffset + type->getStorageBytes() > 32)
		{
			// would overflow, go to next slot
			++slotOffset;
			byteOffset = 0;
		}
		if (slotOffset >= bigint(1) << 256)
			BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
		offsets[i] = make_pair(u256(slotOffset), byteOffset);
		solAssert(type->getStorageSize() >= 1, "Invalid storage size.");
		if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32)
			byteOffset += type->getStorageBytes();
		else
		{
			slotOffset += type->getStorageSize();
			byteOffset = 0;
		}
	}
	if (byteOffset > 0)
		++slotOffset;
	if (slotOffset >= bigint(1) << 256)
		BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
	m_storageSize = u256(slotOffset);
	swap(m_offsets, offsets);
}

pair<u256, unsigned> const* StorageOffsets::getOffset(size_t _index) const
{
	if (m_offsets.count(_index))
		return &m_offsets.at(_index);
	else
		return nullptr;
}

MemberList& MemberList::operator=(MemberList&& _other)
{
	m_memberTypes = std::move(_other.m_memberTypes);
	m_storageOffsets = std::move(_other.m_storageOffsets);
	return *this;
}

std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const& _name) const
{
	if (!m_storageOffsets)
	{
		TypePointers memberTypes;
		memberTypes.reserve(m_memberTypes.size());
		for (auto const& nameAndType: m_memberTypes)
			memberTypes.push_back(nameAndType.second);
		m_storageOffsets.reset(new StorageOffsets());
		m_storageOffsets->computeOffsets(memberTypes);
	}
	for (size_t index = 0; index < m_memberTypes.size(); ++index)
		if (m_memberTypes[index].first == _name)
			return m_storageOffsets->getOffset(index);
	return nullptr;
}

u256 const& MemberList::getStorageSize() const
{
	// trigger lazy computation
	getMemberStorageOffset("");
	return m_storageOffsets->getStorageSize();
}

TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
	char const* tokenCstr = Token::toString(_typeToken);
	solAssert(Token::isElementaryTypeName(_typeToken),
		"Expected an elementary type name but got " + ((tokenCstr) ? std::string(Token::toString(_typeToken)) : ""));

	if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32)
	{
		int offset = _typeToken - Token::Int;
		int bytes = offset % 33;
		if (bytes == 0 && _typeToken != Token::Bytes0)
			bytes = 32;
		int modifier = offset / 33;
		switch(modifier)
		{
		case 0:
			return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Signed);
		case 1:
			return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Unsigned);
		case 2:
			return make_shared<FixedBytesType>(bytes);
		default:
			solAssert(false, "Unexpected modifier value. Should never happen");
			return TypePointer();
		}
	}
	else if (_typeToken == Token::Byte)
		return make_shared<FixedBytesType>(1);
	else if (_typeToken == Token::Address)
		return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
	else if (_typeToken == Token::Bool)
		return make_shared<BoolType>();
	else if (_typeToken == Token::Bytes)
		return make_shared<ArrayType>(ArrayType::Location::Storage);
	else
		BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
																		 std::string(Token::toString(_typeToken)) + " to type."));
}

TypePointer Type::fromElementaryTypeName(string const& _name)
{
	return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}

TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
	Declaration const* declaration = _typeName.getReferencedDeclaration();
	if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
		return make_shared<StructType>(*structDef);
	else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
		return make_shared<EnumType>(*enumDef);
	else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
		return make_shared<FunctionType>(*function);
	else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
		return make_shared<ContractType>(*contract);
	return TypePointer();
}

TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
{
	TypePointer keyType = _keyType.toType();
	if (!keyType)
		BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
	TypePointer valueType = _valueType.toType();
	if (!valueType)
		BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
	return make_shared<MappingType>(keyType, valueType);
}

TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
{
	TypePointer baseType = _baseTypeName.toType();
	if (!baseType)
		BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
	if (_length)
	{
		if (!_length->getType())
			_length->checkTypeRequirements();
		auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
		if (!length)
			BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
		return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
	}
	else
		return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
}

TypePointer Type::forLiteral(Literal const& _literal)
{
	switch (_literal.getToken())
	{
	case Token::TrueLiteral:
	case Token::FalseLiteral:
		return make_shared<BoolType>();
	case Token::Number:
		return make_shared<IntegerConstantType>(_literal);
	case Token::StringLiteral:
		//@todo put larger strings into dynamic strings<commit_msg>Disallowed special case of bytes0 arrays.<commit_after>/*
    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 <<EMAIL>>
 * @date 2014
 * Solidity data types
 */

#include <libsolidity/Types.h>
#include <limits>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/Utils.h>
#include <libsolidity/AST.h>

using namespace std;

namespace dev
{
namespace solidity
{

void StorageOffsets::computeOffsets(TypePointers const& _types)
{
	bigint slotOffset = 0;
	unsigned byteOffset = 0;
	map<size_t, pair<u256, unsigned>> offsets;
	for (size_t i = 0; i < _types.size(); ++i)
	{
		TypePointer const& type = _types[i];
		if (!type->canBeStored())
			continue;
		if (byteOffset + type->getStorageBytes() > 32)
		{
			// would overflow, go to next slot
			++slotOffset;
			byteOffset = 0;
		}
		if (slotOffset >= bigint(1) << 256)
			BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
		offsets[i] = make_pair(u256(slotOffset), byteOffset);
		solAssert(type->getStorageSize() >= 1, "Invalid storage size.");
		if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32)
			byteOffset += type->getStorageBytes();
		else
		{
			slotOffset += type->getStorageSize();
			byteOffset = 0;
		}
	}
	if (byteOffset > 0)
		++slotOffset;
	if (slotOffset >= bigint(1) << 256)
		BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
	m_storageSize = u256(slotOffset);
	swap(m_offsets, offsets);
}

pair<u256, unsigned> const* StorageOffsets::getOffset(size_t _index) const
{
	if (m_offsets.count(_index))
		return &m_offsets.at(_index);
	else
		return nullptr;
}

MemberList& MemberList::operator=(MemberList&& _other)
{
	m_memberTypes = std::move(_other.m_memberTypes);
	m_storageOffsets = std::move(_other.m_storageOffsets);
	return *this;
}

std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const& _name) const
{
	if (!m_storageOffsets)
	{
		TypePointers memberTypes;
		memberTypes.reserve(m_memberTypes.size());
		for (auto const& nameAndType: m_memberTypes)
			memberTypes.push_back(nameAndType.second);
		m_storageOffsets.reset(new StorageOffsets());
		m_storageOffsets->computeOffsets(memberTypes);
	}
	for (size_t index = 0; index < m_memberTypes.size(); ++index)
		if (m_memberTypes[index].first == _name)
			return m_storageOffsets->getOffset(index);
	return nullptr;
}

u256 const& MemberList::getStorageSize() const
{
	// trigger lazy computation
	getMemberStorageOffset("");
	return m_storageOffsets->getStorageSize();
}

TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
	char const* tokenCstr = Token::toString(_typeToken);
	solAssert(Token::isElementaryTypeName(_typeToken),
		"Expected an elementary type name but got " + ((tokenCstr) ? std::string(Token::toString(_typeToken)) : ""));

	if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32)
	{
		int offset = _typeToken - Token::Int;
		int bytes = offset % 33;
		if (bytes == 0 && _typeToken != Token::Bytes0)
			bytes = 32;
		int modifier = offset / 33;
		switch(modifier)
		{
		case 0:
			return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Signed);
		case 1:
			return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Unsigned);
		case 2:
			return make_shared<FixedBytesType>(bytes);
		default:
			solAssert(false, "Unexpected modifier value. Should never happen");
			return TypePointer();
		}
	}
	else if (_typeToken == Token::Byte)
		return make_shared<FixedBytesType>(1);
	else if (_typeToken == Token::Address)
		return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
	else if (_typeToken == Token::Bool)
		return make_shared<BoolType>();
	else if (_typeToken == Token::Bytes)
		return make_shared<ArrayType>(ArrayType::Location::Storage);
	else
		BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
																		 std::string(Token::toString(_typeToken)) + " to type."));
}

TypePointer Type::fromElementaryTypeName(string const& _name)
{
	return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}

TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
	Declaration const* declaration = _typeName.getReferencedDeclaration();
	if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
		return make_shared<StructType>(*structDef);
	else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
		return make_shared<EnumType>(*enumDef);
	else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
		return make_shared<FunctionType>(*function);
	else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
		return make_shared<ContractType>(*contract);
	return TypePointer();
}

TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
{
	TypePointer keyType = _keyType.toType();
	if (!keyType)
		BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
	TypePointer valueType = _valueType.toType();
	if (!valueType)
		BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
	return make_shared<MappingType>(keyType, valueType);
}

TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
{
	TypePointer baseType = _baseTypeName.toType();
	if (!baseType)
		BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
	if (baseType->getStorageBytes() == 0)
		BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array."));
	if (_length)
	{
		if (!_length->getType())
			_length->checkTypeRequirements();
		auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
		if (!length)
			BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
		return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
	}
	else
		return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
}

TypePointer Type::forLiteral(Literal const& _literal)
{
	switch (_literal.getToken())
	{
	case Token::TrueLiteral:
	case Token::FalseLiteral:
		return make_shared<BoolType>();
	case Token::Number:
		return make_shared<IntegerConstantType>(_literal);
	case Token::StringLiteral:
		//@todo put larger strings into dynamic strings
{'fraction_non_alphanumeric': 0.12387387387387387, 'fraction_numerical': 0.008727477477477477, 'mean_word_length': 5.759752616555661, 'pattern_counts': {'":': 0, '<': 107, '<?xml version=': 0, '>': 114, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4381769', 'n_tokens_mistral': 4736, 'n_tokens_neox': 4375, 'n_words': 1076}