/*
	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 .
*/
/**
 * Class that contains contextual information during IR generation.
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::frontend;
string IRGenerationContext::enqueueFunctionForCodeGeneration(FunctionDefinition const& _function)
{
	string name = functionName(_function);
	if (!m_functions.contains(name))
		m_functionGenerationQueue.insert(&_function);
	return name;
}
FunctionDefinition const* IRGenerationContext::dequeueFunctionForCodeGeneration()
{
	solAssert(!m_functionGenerationQueue.empty(), "");
	FunctionDefinition const* result = *m_functionGenerationQueue.begin();
	m_functionGenerationQueue.erase(m_functionGenerationQueue.begin());
	return result;
}
ContractDefinition const& IRGenerationContext::mostDerivedContract() const
{
	solAssert(m_mostDerivedContract, "Most derived contract requested but not set.");
	return *m_mostDerivedContract;
}
IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl)
{
	auto const& [it, didInsert] = m_localVariables.emplace(
		std::make_pair(&_varDecl, IRVariable{_varDecl})
	);
	solAssert(didInsert, "Local variable added multiple times.");
	return it->second;
}
IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const& _varDecl)
{
	solAssert(
		m_localVariables.count(&_varDecl),
		"Unknown variable: " + _varDecl.name()
	);
	return m_localVariables.at(&_varDecl);
}
void IRGenerationContext::addStateVariable(
	VariableDeclaration const& _declaration,
	u256 _storageOffset,
	unsigned _byteOffset
)
{
	m_stateVariables[&_declaration] = make_pair(move(_storageOffset), _byteOffset);
}
string IRGenerationContext::functionName(FunctionDefinition const& _function)
{
	// @TODO previously, we had to distinguish creation context and runtime context,
	// but since we do not work with jump positions anymore, this should not be a problem, right?
	return "fun_" + _function.name() + "_" + to_string(_function.id());
}
string IRGenerationContext::functionName(VariableDeclaration const& _varDecl)
{
	return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
}
string IRGenerationContext::creationObjectName(ContractDefinition const& _contract) const
{
	return _contract.name() + "_" + toString(_contract.id());
}
string IRGenerationContext::runtimeObjectName(ContractDefinition const& _contract) const
{
	return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
}
string IRGenerationContext::newYulVariable()
{
	return "_" + to_string(++m_varCounter);
}
string IRGenerationContext::trySuccessConditionVariable(Expression const& _expression) const
{
	// NB: The TypeChecker already ensured that the Expression is of type FunctionCall.
	solAssert(
		static_cast(_expression.annotation()).tryCall,
		"Parameter must be a FunctionCall with tryCall-annotation set."
	);
	return "trySuccessCondition_" + to_string(_expression.id());
}
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
{
	string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
	return m_functions.createFunction(funName, [&]() {
		Whiskers templ(R"(
			function (fun  )   {
				switch fun
				<#cases>
				case 
				{
					  ()
				}
				
				default { invalid() }
			}
		)");
		templ("functionName", funName);
		templ("comma", _in > 0 ? "," : "");
		YulUtilFunctions utils(m_evmVersion, m_revertStrings, m_functions);
		templ("in", suffixedVariableNameList("in_", 0, _in));
		templ("arrow", _out > 0 ? "->" : "");
		templ("assignment_op", _out > 0 ? ":=" : "");
		templ("out", suffixedVariableNameList("out_", 0, _out));
		// UNIMPLEMENTED: Internal library calls via pointers are not implemented yet.
		// We're not generating code for internal library functions here even though it's possible
		// to call them via pointers. Right now such calls end up triggering the `default` case in
		// the switch above.
		vector