/*
	This file is part of solidity.
	solidity 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.
	solidity 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 solidity.  If not, see .
*/
#include 
#include 
#include 
#include 
using namespace std;
using namespace dev;
using namespace solidity;
BoolType const TypeProvider::m_boolean{};
InaccessibleDynamicType const TypeProvider::m_inaccessibleDynamic{};
/// The string and bytes unique_ptrs are initialized when they are first used because
/// they rely on `byte` being available which we cannot guarantee in the static init context.
unique_ptr TypeProvider::m_bytesStorage;
unique_ptr TypeProvider::m_bytesMemory;
unique_ptr TypeProvider::m_stringStorage;
unique_ptr TypeProvider::m_stringMemory;
TupleType const TypeProvider::m_emptyTuple{};
AddressType const TypeProvider::m_payableAddress{StateMutability::Payable};
AddressType const TypeProvider::m_address{StateMutability::NonPayable};
array, 32> const TypeProvider::m_intM{{
	{make_unique(8 * 1, IntegerType::Modifier::Signed)},
	{make_unique(8 * 2, IntegerType::Modifier::Signed)},
	{make_unique(8 * 3, IntegerType::Modifier::Signed)},
	{make_unique(8 * 4, IntegerType::Modifier::Signed)},
	{make_unique(8 * 5, IntegerType::Modifier::Signed)},
	{make_unique(8 * 6, IntegerType::Modifier::Signed)},
	{make_unique(8 * 7, IntegerType::Modifier::Signed)},
	{make_unique(8 * 8, IntegerType::Modifier::Signed)},
	{make_unique(8 * 9, IntegerType::Modifier::Signed)},
	{make_unique(8 * 10, IntegerType::Modifier::Signed)},
	{make_unique(8 * 11, IntegerType::Modifier::Signed)},
	{make_unique(8 * 12, IntegerType::Modifier::Signed)},
	{make_unique(8 * 13, IntegerType::Modifier::Signed)},
	{make_unique(8 * 14, IntegerType::Modifier::Signed)},
	{make_unique(8 * 15, IntegerType::Modifier::Signed)},
	{make_unique(8 * 16, IntegerType::Modifier::Signed)},
	{make_unique(8 * 17, IntegerType::Modifier::Signed)},
	{make_unique(8 * 18, IntegerType::Modifier::Signed)},
	{make_unique(8 * 19, IntegerType::Modifier::Signed)},
	{make_unique(8 * 20, IntegerType::Modifier::Signed)},
	{make_unique(8 * 21, IntegerType::Modifier::Signed)},
	{make_unique(8 * 22, IntegerType::Modifier::Signed)},
	{make_unique(8 * 23, IntegerType::Modifier::Signed)},
	{make_unique(8 * 24, IntegerType::Modifier::Signed)},
	{make_unique(8 * 25, IntegerType::Modifier::Signed)},
	{make_unique(8 * 26, IntegerType::Modifier::Signed)},
	{make_unique(8 * 27, IntegerType::Modifier::Signed)},
	{make_unique(8 * 28, IntegerType::Modifier::Signed)},
	{make_unique(8 * 29, IntegerType::Modifier::Signed)},
	{make_unique(8 * 30, IntegerType::Modifier::Signed)},
	{make_unique(8 * 31, IntegerType::Modifier::Signed)},
	{make_unique(8 * 32, IntegerType::Modifier::Signed)}
}};
array, 32> const TypeProvider::m_uintM{{
	{make_unique(8 * 1, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 2, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 3, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 4, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 5, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 6, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 7, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 8, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 9, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 10, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 11, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 12, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 13, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 14, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 15, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 16, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 17, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 18, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 19, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 20, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 21, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 22, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 23, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 24, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 25, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 26, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 27, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 28, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 29, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 30, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 31, IntegerType::Modifier::Unsigned)},
	{make_unique(8 * 32, IntegerType::Modifier::Unsigned)}
}};
array, 32> const TypeProvider::m_bytesM{{
	{make_unique(1)},
	{make_unique(2)},
	{make_unique(3)},
	{make_unique(4)},
	{make_unique(5)},
	{make_unique(6)},
	{make_unique(7)},
	{make_unique(8)},
	{make_unique(9)},
	{make_unique(10)},
	{make_unique(11)},
	{make_unique(12)},
	{make_unique(13)},
	{make_unique(14)},
	{make_unique(15)},
	{make_unique(16)},
	{make_unique(17)},
	{make_unique(18)},
	{make_unique(19)},
	{make_unique(20)},
	{make_unique(21)},
	{make_unique(22)},
	{make_unique(23)},
	{make_unique(24)},
	{make_unique(25)},
	{make_unique(26)},
	{make_unique(27)},
	{make_unique(28)},
	{make_unique(29)},
	{make_unique(30)},
	{make_unique(31)},
	{make_unique(32)}
}};
array, 4> const TypeProvider::m_magics{{
	{make_unique(MagicType::Kind::Block)},
	{make_unique(MagicType::Kind::Message)},
	{make_unique(MagicType::Kind::Transaction)},
	{make_unique(MagicType::Kind::ABI)}
	// MetaType is stored separately
}};
inline void clearCache(Type const& type)
{
	type.clearCache();
}
template 
inline void clearCache(unique_ptr const& type)
{
	// Some lazy-initialized types might not exist yet.
	if (type)
		type->clearCache();
}
template 
inline void clearCaches(Container& container)
{
	for (auto const& e: container)
		clearCache(e);
}
void TypeProvider::reset()
{
	clearCache(m_boolean);
	clearCache(m_inaccessibleDynamic);
	clearCache(m_bytesStorage);
	clearCache(m_bytesMemory);
	clearCache(m_stringStorage);
	clearCache(m_stringMemory);
	clearCache(m_emptyTuple);
	clearCache(m_payableAddress);
	clearCache(m_address);
	clearCaches(instance().m_intM);
	clearCaches(instance().m_uintM);
	clearCaches(instance().m_bytesM);
	clearCaches(instance().m_magics);
	instance().m_generalTypes.clear();
	instance().m_stringLiteralTypes.clear();
	instance().m_ufixedMxN.clear();
	instance().m_fixedMxN.clear();
}
template 
inline T const* TypeProvider::createAndGet(Args&& ... _args)
{
	instance().m_generalTypes.emplace_back(make_unique(std::forward(_args)...));
	return static_cast(instance().m_generalTypes.back().get());
}
Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
{
	solAssert(
		TokenTraits::isElementaryTypeName(_type.token()),
		"Expected an elementary type name but got " + _type.toString()
	);
	unsigned const m = _type.firstNumber();
	unsigned const n = _type.secondNumber();
	switch (_type.token())
	{
	case Token::IntM:
		return integer(m, IntegerType::Modifier::Signed);
	case Token::UIntM:
		return integer(m, IntegerType::Modifier::Unsigned);
	case Token::Byte:
		return byte();
	case Token::BytesM:
		return fixedBytes(m);
	case Token::FixedMxN:
		return fixedPoint(m, n, FixedPointType::Modifier::Signed);
	case Token::UFixedMxN:
		return fixedPoint(m, n, FixedPointType::Modifier::Unsigned);
	case Token::Int:
		return integer(256, IntegerType::Modifier::Signed);
	case Token::UInt:
		return integer(256, IntegerType::Modifier::Unsigned);
	case Token::Fixed:
		return fixedPoint(128, 18, FixedPointType::Modifier::Signed);
	case Token::UFixed:
		return fixedPoint(128, 18, FixedPointType::Modifier::Unsigned);
	case Token::Address:
		return address();
	case Token::Bool:
		return boolean();
	case Token::Bytes:
		return bytesStorage();
	case Token::String:
		return stringStorage();
	default:
		solAssert(
			false,
			"Unable to convert elementary typename " + _type.toString() + " to type."
		);
	}
}
TypePointer TypeProvider::fromElementaryTypeName(string const& _name)
{
	vector nameParts;
	boost::split(nameParts, _name, boost::is_any_of(" "));
	solAssert(nameParts.size() == 1 || nameParts.size() == 2, "Cannot parse elementary type: " + _name);
	Token token;
	unsigned short firstNum, secondNum;
	tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(nameParts[0]);
	auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum));
	if (auto* ref = dynamic_cast(t))
	{
		DataLocation location = DataLocation::Storage;
		if (nameParts.size() == 2)
		{
			if (nameParts[1] == "storage")
				location = DataLocation::Storage;
			else if (nameParts[1] == "calldata")
				location = DataLocation::CallData;
			else if (nameParts[1] == "memory")
				location = DataLocation::Memory;
			else
				solAssert(false, "Unknown data location: " + nameParts[1]);
		}
		return withLocation(ref, location, true);
	}
	else if (t->category() == Type::Category::Address)
	{
		if (nameParts.size() == 2)
		{
			if (nameParts[1] == "payable")
				return payableAddress();
			else
				solAssert(false, "Invalid state mutability for address type: " + nameParts[1]);
		}
		return address();
	}
	else
	{
		solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types");
		return t;
	}
}
ArrayType const* TypeProvider::bytesStorage()
{
	if (!m_bytesStorage)
		m_bytesStorage = make_unique(DataLocation::Storage, false);
	return m_bytesStorage.get();
}
ArrayType const* TypeProvider::bytesMemory()
{
	if (!m_bytesMemory)
		m_bytesMemory = make_unique(DataLocation::Memory, false);
	return m_bytesMemory.get();
}
ArrayType const* TypeProvider::stringStorage()
{
	if (!m_stringStorage)
		m_stringStorage = make_unique(DataLocation::Storage, true);
	return m_stringStorage.get();
}
ArrayType const* TypeProvider::stringMemory()
{
	if (!m_stringMemory)
		m_stringMemory = make_unique(DataLocation::Memory, true);
	return m_stringMemory.get();
}
TypePointer TypeProvider::forLiteral(Literal const& _literal)
{
	switch (_literal.token())
	{
	case Token::TrueLiteral:
	case Token::FalseLiteral:
		return boolean();
	case Token::Number:
		return rationalNumber(_literal);
	case Token::StringLiteral:
		return stringLiteral(_literal.value());
	default:
		return nullptr;
	}
}
RationalNumberType const* TypeProvider::rationalNumber(Literal const& _literal)
{
	solAssert(_literal.token() == Token::Number, "");
	std::tuple validLiteral = RationalNumberType::isValidLiteral(_literal);
	if (std::get<0>(validLiteral))
	{
		TypePointer compatibleBytesType = nullptr;
		if (_literal.isHexNumber())
		{
			size_t const digitCount = _literal.valueWithoutUnderscores().length() - 2;
			if (digitCount % 2 == 0 && (digitCount / 2) <= 32)
				compatibleBytesType = fixedBytes(digitCount / 2);
		}
		return rationalNumber(std::get<1>(validLiteral), compatibleBytesType);
	}
	return nullptr;
}
StringLiteralType const* TypeProvider::stringLiteral(string const& literal)
{
	auto i = instance().m_stringLiteralTypes.find(literal);
	if (i != instance().m_stringLiteralTypes.end())
		return i->second.get();
	else
		return instance().m_stringLiteralTypes.emplace(literal, make_unique(literal)).first->second.get();
}
FixedPointType const* TypeProvider::fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier)
{
	auto& map = _modifier == FixedPointType::Modifier::Unsigned ? instance().m_ufixedMxN : instance().m_fixedMxN;
	auto i = map.find(make_pair(m, n));
	if (i != map.end())
		return i->second.get();
	return map.emplace(
		make_pair(m, n),
		make_unique(m, n, _modifier)
	).first->second.get();
}
TupleType const* TypeProvider::tuple(vector members)
{
	if (members.empty())
		return &m_emptyTuple;
	return createAndGet(move(members));
}
ReferenceType const* TypeProvider::withLocation(ReferenceType const* _type, DataLocation _location, bool _isPointer)
{
	if (_type->location() == _location && _type->isPointer() == _isPointer)
		return _type;
	instance().m_generalTypes.emplace_back(_type->copyForLocation(_location, _isPointer));
	return static_cast(instance().m_generalTypes.back().get());
}
FunctionType const* TypeProvider::function(FunctionDefinition const& _function, bool _isInternal)
{
	return createAndGet(_function, _isInternal);
}
FunctionType const* TypeProvider::function(VariableDeclaration const& _varDecl)
{
	return createAndGet(_varDecl);
}
FunctionType const* TypeProvider::function(EventDefinition const& _def)
{
	return createAndGet(_def);
}
FunctionType const* TypeProvider::function(FunctionTypeName const& _typeName)
{
	return createAndGet(_typeName);
}
FunctionType const* TypeProvider::function(
	strings const& _parameterTypes,
	strings const& _returnParameterTypes,
	FunctionType::Kind _kind,
	bool _arbitraryParameters,
	StateMutability _stateMutability
)
{
	return createAndGet(
		_parameterTypes, _returnParameterTypes,
		_kind, _arbitraryParameters, _stateMutability
	);
}
FunctionType const* TypeProvider::function(
	TypePointers const& _parameterTypes,
	TypePointers const& _returnParameterTypes,
	strings _parameterNames,
	strings _returnParameterNames,
	FunctionType::Kind _kind,
	bool _arbitraryParameters,
	StateMutability _stateMutability,
	Declaration const* _declaration,
	bool _gasSet,
	bool _valueSet,
	bool _bound
)
{
	return createAndGet(
		_parameterTypes,
		_returnParameterTypes,
		_parameterNames,
		_returnParameterNames,
		_kind,
		_arbitraryParameters,
		_stateMutability,
		_declaration,
		_gasSet,
		_valueSet,
		_bound
	);
}
RationalNumberType const* TypeProvider::rationalNumber(rational const& _value, Type const* _compatibleBytesType)
{
	return createAndGet(_value, _compatibleBytesType);
}
ArrayType const* TypeProvider::array(DataLocation _location, bool _isString)
{
	if (_isString)
	{
		if (_location == DataLocation::Storage)
			return stringStorage();
		if (_location == DataLocation::Memory)
			return stringMemory();
	}
	else
	{
		if (_location == DataLocation::Storage)
			return bytesStorage();
		if (_location == DataLocation::Memory)
			return bytesMemory();
	}
	return createAndGet(_location, _isString);
}
ArrayType const* TypeProvider::array(DataLocation _location, Type const* _baseType)
{
	return createAndGet(_location, _baseType);
}
ArrayType const* TypeProvider::array(DataLocation _location, Type const* _baseType, u256 const& _length)
{
	return createAndGet(_location, _baseType, _length);
}
ContractType const* TypeProvider::contract(ContractDefinition const& _contractDef, bool _isSuper)
{
	return createAndGet(_contractDef, _isSuper);
}
EnumType const* TypeProvider::enumType(EnumDefinition const& _enumDef)
{
	return createAndGet(_enumDef);
}
ModuleType const* TypeProvider::module(SourceUnit const& _source)
{
	return createAndGet(_source);
}
TypeType const* TypeProvider::typeType(Type const* _actualType)
{
	return createAndGet(_actualType);
}
StructType const* TypeProvider::structType(StructDefinition const& _struct, DataLocation _location)
{
	return createAndGet(_struct, _location);
}
ModifierType const* TypeProvider::modifier(ModifierDefinition const& _def)
{
	return createAndGet(_def);
}
MagicType const* TypeProvider::magic(MagicType::Kind _kind)
{
	solAssert(_kind != MagicType::Kind::MetaType, "MetaType is handled separately");
	return m_magics.at(static_cast(_kind)).get();
}
MagicType const* TypeProvider::meta(Type const* _type)
{
	solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
	return createAndGet(_type);
}
MappingType const* TypeProvider::mapping(Type const* _keyType, Type const* _valueType)
{
	return createAndGet(_keyType, _valueType);
}