<commit_before>#include "mapping.h"
#include "details/atom/classdef-table-view.h"
#include "details/ir/sequence.h"

using namespace Yuni;



namespace Nany
{
namespace Pass
{
namespace Mapping
{


	SequenceMapping::SequenceMapping(ClassdefTable& cdeftable, Mutex& mutex, IR::Sequence& sequence)
		: cdeftable(cdeftable)
		, mutex(mutex)
		, currentSequence(sequence)
		, localMetadataHandler(this, &retriveReportMetadata)

	{
		// reduce memory allocations
		lastPushedNamedParameters.reserve(8); // arbitrary
		lastPushedIndexedParameters.reserve(8);
	}


	void SequenceMapping::retriveReportMetadata(void* self, Logs::Level level, const AST::Node*, String& filename, uint32_t& line, uint32_t& offset)
	{
		auto& sb    = *(reinterpret_cast<SequenceMapping*>(self));
		sb.success &= Logs::isError(level);
		filename    = sb.currentFilename;
		line        = sb.currentLine;
		offset      = sb.currentOffset;
	}


	inline void SequenceMapping::pushNewFrame(Atom& atom)
	{
		std::unique_ptr<AtomStackFrame> next{atomStack.release()};
		atomStack = std::make_unique<AtomStackFrame>(atom, next);
	}


	void SequenceMapping::printError(const IR::Instruction& operands, AnyString msg)
	{
		// example: ICE: unknown opcode 'resolveAttribute': from 'ref %4 = resolve %3."()"'
		auto trace = ice();
		if (not msg.empty())
			trace << msg << ':';
		else
			trace << "invalid opcode ";

		trace << " '" << IR::ISA::print(currentSequence, operands) << '\'';
		success = false;
	}


	inline void AtomStackFrame::CaptureVariables::enabled(Atom* newatom)
	{
		atom = newatom;
		atom->flags += Atom::Flags::captureVariables;

		if (!newatom->candidatesForCapture)
		{
			typedef decltype(newatom->candidatesForCapture) Set;
			newatom->candidatesForCapture = std::make_unique<Set::element_type>();
		}
	}


	template<IR::ISA::Op O>
	inline void SequenceMapping::printError(const IR::ISA::Operand<O>& operands, AnyString msg)
	{
		printError(IR::Instruction::fromOpcode(operands), msg);
	}


	template<IR::ISA::Op O>
	inline bool SequenceMapping::checkForLVID(const IR::ISA::Operand<O>& operands, LVID lvid)
	{
		if (debugmode)
		{
			if (unlikely(lvid == 0 or not (lvid < atomStack->classdefs.size())))
			{
				printError(operands, String{"mapping: invalid lvid %"} << lvid << " (upper bound: %"
					<< atomStack->classdefs.size() << ')');
				return false;
			}
		}
		return true;
	}


	inline void SequenceMapping::resetClassdefOriginFromCurrentPosition(Classdef& cdef)
	{
		cdef.origins.filename = currentFilename;
		cdef.origins.line     = currentLine;
		cdef.origins.offset   = currentOffset;
	}


	void SequenceMapping::attachFuncCall(const IR::ISA::Operand<IR::ISA::Op::call>& operands)
	{
		if (unlikely(not checkForLVID(operands, operands.ptr2func)))
			return;
		if (unlikely(not checkForLVID(operands, operands.lvid)))
			return;

		auto& stackframe     = *atomStack;
		auto  atomid         = stackframe.atom.atomid;
		auto& clidFuncToCall = stackframe.classdefs[operands.ptr2func];
		auto& clidRetValue   = stackframe.classdefs[operands.lvid];

		// update all underlying types
		{
			CLID clid{atomid, 0};

			MutexLocker locker{mutex};

			resetClassdefOriginFromCurrentPosition(cdeftable.classdef(clidRetValue));

			auto& funcdef = cdeftable.addClassdefInterfaceSelf(clidFuncToCall);
			funcdef.resetReturnType(clidRetValue);
			funcdef.clid = clidFuncToCall;

			for (uint i = 0; i != lastPushedIndexedParameters.size(); ++i)
			{
				auto lvid = lastPushedIndexedParameters[i];
				clid.reclass(lvid);
				funcdef.appendParameter(clid);

				auto& followup = cdeftable.classdef(clid).followup;
				followup.pushedIndexedParams.push_back(std::make_pair(clidFuncToCall, i));
			}

			for (auto& pair: lastPushedNamedParameters)
			{
				clid.reclass(pair.second);
				funcdef.appendParameter(pair.first, clid);

				auto& followup = cdeftable.classdef(clid).followup;
				followup.pushedNamedParams.push_back(std::make_pair(clidFuncToCall, pair.first));
			}
		}
	}


	inline void SequenceMapping::visit(IR::ISA::Operand<IR::ISA::Op::blueprint>& operands)
	{
		if (unlikely(nullptr == atomStack))
			return printError(operands, "invalid stack for blueprint");

		auto kind = static_cast<IR::ISA::Blueprint>(operands.kind);
		switch (kind)
		{
			case IR::ISA::Blueprint::vardef:
			{
				// registering the blueprint into the outline...
				Atom& atom = atomStack->currentAtomNotUnit();
				AnyString varname = currentSequence.stringrefs[operands.name];
				if (unlikely(varname.empty()))
					return printError(operands, "invalid func name");
				if (unlikely(atom.type != Atom::Type::classdef))
					return printError(operands, "vardef: invalid parent atom");

				if (atomStack->capture.enabled())
					atomStack->capture.knownVars[varname] = atomStack->scope;

				MutexLocker locker{mutex};
				// create a new atom in the global type table
				auto* newVarAtom = cdeftable.atoms.createVardef(atom, varname);
				assert(newVarAtom != nullptr);

				cdeftable.registerAtom(newVarAtom);
				newVarAtom->returnType.clid.reclass(atom.atomid, operands.lvid);
				if (!firstAtomCreated)
					firstAtomCreated = newVarAtom;
				break;
			}

			case IR::ISA::Blueprint::param:
			case IR::ISA::Blueprint::paramself:
			case IR::ISA::Blueprint::gentypeparam:
			{
				auto& frame = *atomStack;

				// calculating the lvid for the current parameter
				// (+1 since %1 is the return value/type)
				uint paramLVID = (++frame.parameterIndex) + 1;

				if (unlikely(not checkForLVID(operands, paramLVID)))
					return;

				bool isGenTypeParam = (kind == IR::ISA::Blueprint::gentypeparam);
				if (unlikely(not isGenTypeParam and not frame.atom.isFunction()))
					return printError(operands, "parameter for non-function");

				CLID clid{frame.atom.atomid, paramLVID};
				AnyString name = currentSequence.stringrefs[operands.name];

				auto& parameters = (not isGenTypeParam)
					? frame.atom.parameters : frame.atom.tmplparams;
				parameters.append(clid, name);

				if (frame.capture.enabled())
					frame.capture.knownVars[name] = frame.scope;


				MutexLocker locker{mutex};
				// information about the parameter itself
				auto& cdef = cdeftable.classdef(clid);
				cdef.instance = not isGenTypeParam;
				cdef.qualifiers.ref = false; // should not be 'ref' by default, contrary to all other classdefs

				// making sure the classdef is 'any'
				assert(cdef.atom == nullptr and cdef.isAny());

				if (isGenTypeParam)
				{
					// if a generic type parameter, generating an implicit typedef
					Atom& atom = atomStack->currentAtomNotUnit();
					auto* newAliasAtom = cdeftable.atoms.createTypealias(atom, name);
					cdeftable.registerAtom(newAliasAtom);
					newAliasAtom->returnType.clid = cdef.clid; // type of the typedef
				}
				break;
			}

			case IR::ISA::Blueprint::funcdef:
			{
				// registering the blueprint into the outline...
				Atom& atom = atomStack->currentAtomNotUnit();
				AnyString funcname = currentSequence.stringrefs[operands.name];
				if (unlikely(funcname.empty()))
					return printError(operands, "invalid func name");

				// reset last lvid and parameters
				lastLVID = 0;
				lastPushedNamedParameters.clear();
				lastPushedIndexedParameters.clear();

				MutexLocker locker{mutex};
				// create a new atom in the global type table
				auto* newatom = cdeftable.atoms.createFuncdef(atom, funcname);
				assert(newatom != nullptr);
				newatom->opcodes.sequence  = &currentSequence;
				newatom->opcodes.offset   = currentSequence.offsetOf(operands);
				// create a pseudo classdef to easily retrieve the real atom from a clid
				cdeftable.registerAtom(newatom);

				operands.atomid = newatom->atomid;

				// return type
				newatom->returnType.clid.reclass(newatom->atomid, 1);

				// requires additional information
				needAtomDbgFileReport = true;
				needAtomDbgOffsetReport = true;
				pushNewFrame(*newatom);

				// capture unknown variables ?
				if (newatom->isClassMember() and newatom->parent->flags(Atom::Flags::captureVariables))
				{
					atomStack->capture.enabled(newatom->parent);
					newatom->flags += Atom::Flags::captureVariables;
				}

				if (!firstAtomCreated)
					firstAtomCreated = newatom;
				break;
			}

			case IR::ISA::Blueprint::classdef:
			{
				// registering the blueprint into the outline...
				Atom& atom = atomStack->currentAtomNotUnit();

				// reset last lvid and parameters<commit_msg>an operator outside a class is always global<commit_after>#include "mapping.h"
#include "details/atom/classdef-table-view.h"
#include "details/ir/sequence.h"

using namespace Yuni;



namespace Nany
{
namespace Pass
{
namespace Mapping
{


	SequenceMapping::SequenceMapping(ClassdefTable& cdeftable, Mutex& mutex, IR::Sequence& sequence)
		: cdeftable(cdeftable)
		, mutex(mutex)
		, currentSequence(sequence)
		, localMetadataHandler(this, &retriveReportMetadata)

	{
		// reduce memory allocations
		lastPushedNamedParameters.reserve(8); // arbitrary
		lastPushedIndexedParameters.reserve(8);
	}


	void SequenceMapping::retriveReportMetadata(void* self, Logs::Level level, const AST::Node*, String& filename, uint32_t& line, uint32_t& offset)
	{
		auto& sb    = *(reinterpret_cast<SequenceMapping*>(self));
		sb.success &= Logs::isError(level);
		filename    = sb.currentFilename;
		line        = sb.currentLine;
		offset      = sb.currentOffset;
	}


	inline void SequenceMapping::pushNewFrame(Atom& atom)
	{
		std::unique_ptr<AtomStackFrame> next{atomStack.release()};
		atomStack = std::make_unique<AtomStackFrame>(atom, next);
	}


	void SequenceMapping::printError(const IR::Instruction& operands, AnyString msg)
	{
		// example: ICE: unknown opcode 'resolveAttribute': from 'ref %4 = resolve %3."()"'
		auto trace = ice();
		if (not msg.empty())
			trace << msg << ':';
		else
			trace << "invalid opcode ";

		trace << " '" << IR::ISA::print(currentSequence, operands) << '\'';
		success = false;
	}


	inline void AtomStackFrame::CaptureVariables::enabled(Atom* newatom)
	{
		atom = newatom;
		atom->flags += Atom::Flags::captureVariables;

		if (!newatom->candidatesForCapture)
		{
			typedef decltype(newatom->candidatesForCapture) Set;
			newatom->candidatesForCapture = std::make_unique<Set::element_type>();
		}
	}


	template<IR::ISA::Op O>
	inline void SequenceMapping::printError(const IR::ISA::Operand<O>& operands, AnyString msg)
	{
		printError(IR::Instruction::fromOpcode(operands), msg);
	}


	template<IR::ISA::Op O>
	inline bool SequenceMapping::checkForLVID(const IR::ISA::Operand<O>& operands, LVID lvid)
	{
		if (debugmode)
		{
			if (unlikely(lvid == 0 or not (lvid < atomStack->classdefs.size())))
			{
				printError(operands, String{"mapping: invalid lvid %"} << lvid << " (upper bound: %"
					<< atomStack->classdefs.size() << ')');
				return false;
			}
		}
		return true;
	}


	inline void SequenceMapping::resetClassdefOriginFromCurrentPosition(Classdef& cdef)
	{
		cdef.origins.filename = currentFilename;
		cdef.origins.line     = currentLine;
		cdef.origins.offset   = currentOffset;
	}


	void SequenceMapping::attachFuncCall(const IR::ISA::Operand<IR::ISA::Op::call>& operands)
	{
		if (unlikely(not checkForLVID(operands, operands.ptr2func)))
			return;
		if (unlikely(not checkForLVID(operands, operands.lvid)))
			return;

		auto& stackframe     = *atomStack;
		auto  atomid         = stackframe.atom.atomid;
		auto& clidFuncToCall = stackframe.classdefs[operands.ptr2func];
		auto& clidRetValue   = stackframe.classdefs[operands.lvid];

		// update all underlying types
		{
			CLID clid{atomid, 0};

			MutexLocker locker{mutex};

			resetClassdefOriginFromCurrentPosition(cdeftable.classdef(clidRetValue));

			auto& funcdef = cdeftable.addClassdefInterfaceSelf(clidFuncToCall);
			funcdef.resetReturnType(clidRetValue);
			funcdef.clid = clidFuncToCall;

			for (uint i = 0; i != lastPushedIndexedParameters.size(); ++i)
			{
				auto lvid = lastPushedIndexedParameters[i];
				clid.reclass(lvid);
				funcdef.appendParameter(clid);

				auto& followup = cdeftable.classdef(clid).followup;
				followup.pushedIndexedParams.push_back(std::make_pair(clidFuncToCall, i));
			}

			for (auto& pair: lastPushedNamedParameters)
			{
				clid.reclass(pair.second);
				funcdef.appendParameter(pair.first, clid);

				auto& followup = cdeftable.classdef(clid).followup;
				followup.pushedNamedParams.push_back(std::make_pair(clidFuncToCall, pair.first));
			}
		}
	}


	inline void SequenceMapping::visit(IR::ISA::Operand<IR::ISA::Op::blueprint>& operands)
	{
		if (unlikely(nullptr == atomStack))
			return printError(operands, "invalid stack for blueprint");

		auto kind = static_cast<IR::ISA::Blueprint>(operands.kind);
		switch (kind)
		{
			case IR::ISA::Blueprint::vardef:
			{
				// registering the blueprint into the outline...
				Atom& atom = atomStack->currentAtomNotUnit();
				AnyString varname = currentSequence.stringrefs[operands.name];
				if (unlikely(varname.empty()))
					return printError(operands, "invalid func name");
				if (unlikely(atom.type != Atom::Type::classdef))
					return printError(operands, "vardef: invalid parent atom");

				if (atomStack->capture.enabled())
					atomStack->capture.knownVars[varname] = atomStack->scope;

				MutexLocker locker{mutex};
				// create a new atom in the global type table
				auto* newVarAtom = cdeftable.atoms.createVardef(atom, varname);
				assert(newVarAtom != nullptr);

				cdeftable.registerAtom(newVarAtom);
				newVarAtom->returnType.clid.reclass(atom.atomid, operands.lvid);
				if (!firstAtomCreated)
					firstAtomCreated = newVarAtom;
				break;
			}

			case IR::ISA::Blueprint::param:
			case IR::ISA::Blueprint::paramself:
			case IR::ISA::Blueprint::gentypeparam:
			{
				auto& frame = *atomStack;

				// calculating the lvid for the current parameter
				// (+1 since %1 is the return value/type)
				uint paramLVID = (++frame.parameterIndex) + 1;

				if (unlikely(not checkForLVID(operands, paramLVID)))
					return;

				bool isGenTypeParam = (kind == IR::ISA::Blueprint::gentypeparam);
				if (unlikely(not isGenTypeParam and not frame.atom.isFunction()))
					return printError(operands, "parameter for non-function");

				CLID clid{frame.atom.atomid, paramLVID};
				AnyString name = currentSequence.stringrefs[operands.name];

				auto& parameters = (not isGenTypeParam)
					? frame.atom.parameters : frame.atom.tmplparams;
				parameters.append(clid, name);

				if (frame.capture.enabled())
					frame.capture.knownVars[name] = frame.scope;


				MutexLocker locker{mutex};
				// information about the parameter itself
				auto& cdef = cdeftable.classdef(clid);
				cdef.instance = not isGenTypeParam;
				cdef.qualifiers.ref = false; // should not be 'ref' by default, contrary to all other classdefs

				// making sure the classdef is 'any'
				assert(cdef.atom == nullptr and cdef.isAny());

				if (isGenTypeParam)
				{
					// if a generic type parameter, generating an implicit typedef
					Atom& atom = atomStack->currentAtomNotUnit();
					auto* newAliasAtom = cdeftable.atoms.createTypealias(atom, name);
					cdeftable.registerAtom(newAliasAtom);
					newAliasAtom->returnType.clid = cdef.clid; // type of the typedef
				}
				break;
			}

			case IR::ISA::Blueprint::funcdef:
			{
				// registering the blueprint into the outline...
				Atom& atom = atomStack->currentAtomNotUnit();
				AnyString funcname = currentSequence.stringrefs[operands.name];
				if (unlikely(funcname.empty()))
					return printError(operands, "invalid func name");

				// reset last lvid and parameters
				lastLVID = 0;
				lastPushedNamedParameters.clear();
				lastPushedIndexedParameters.clear();

				bool isGlobalOperator = (atom.type == Atom::Type::namespacedef)
					and funcname[0] == '^' and (not funcname.startsWith("^view^"));

				auto& parentAtom = (not isGlobalOperator)
					? atom
					: cdeftable.atoms.root;

				MutexLocker locker{mutex};
				// create a new atom in the global type table
				auto* newatom = cdeftable.atoms.createFuncdef(parentAtom, funcname);
				assert(newatom != nullptr);
				newatom->opcodes.sequence  = &currentSequence;
				newatom->opcodes.offset   = currentSequence.offsetOf(operands);
				// create a pseudo classdef to easily retrieve the real atom from a clid
				cdeftable.registerAtom(newatom);

				operands.atomid = newatom->atomid;

				// return type
				newatom->returnType.clid.reclass(newatom->atomid, 1);
				// scope resolution
				if (parentAtom.atomid != atom.atomid)
					newatom->scopeForNameResolution = &atom;

				// requires additional information
				needAtomDbgFileReport = true;
				needAtomDbgOffsetReport = true;
				pushNewFrame(*newatom);

				// capture unknown variables ?
				if (not isGlobalOperator)
				{
					if (newatom->isClassMember() and newatom->parent->flags(Atom::Flags::captureVariables))
					{
						atomStack->capture.enabled(newatom->parent);
						newatom->flags += Atom::Flags::captureVariables;
					}
				}

				if (!firstAtomCreated)
					firstAtomCreated = newatom;
				break;
			}

			case IR::ISA::Blueprint::classdef:
			{
				// registering the blueprint into the outline...
				Atom& atom = atomStack->currentAtomNotUnit();

				// reset last lvid and parameters
{'fraction_non_alphanumeric': 0.12784883720930232, 'fraction_numerical': 0.0021511627906976743, 'mean_word_length': 4.072544971984665, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 78, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '4555819', 'n_tokens_mistral': 6608, 'n_tokens_neox': 5533, 'n_words': 1129}