#include "BfMangler.h"
#include "BfDemangler.h"
#include "BfCompiler.h"
#pragma warning(disable:4996)

USING_NS_BF;

int BfGNUMangler::ParseSubIdx(StringImpl& name, int strIdx)
{
	const char* charPtr = name.c_str() + strIdx + 1;
	if (*charPtr == '_')
		return 0;
	int idx = atoi(charPtr) + 1;
	return idx;
}

void BfGNUMangler::AddSubIdx(StringImpl& name, int subIdx)
{
	name += 'S';
	if (subIdx != 0)
	{
		int showIdx = subIdx - 1;
		if (showIdx > 36)
		{
			name += '0' + (showIdx / 36);
			showIdx %= 36;
		}
		if (showIdx < 10)
			name += '0' + showIdx;
		else
			name += 'A' + (showIdx - 10);
	}
	name += '_';
}

void BfGNUMangler::AddSizedString(StringImpl& name, const StringImpl& addStr)
{
	char str[16];
	itoa((int)addStr.length(), str, 10);
	name += str;
	name += addStr;
}

BfTypeCode BfGNUMangler::GetPrimTypeAt(MangleContext& mangleContext, StringImpl& name, int strIdx)
{
	char startChar = name[strIdx];

	auto module = mangleContext.mModule;
	switch (startChar)
	{
	case 'v': return BfTypeCode_None;
	case 'b': return BfTypeCode_Boolean;
	case 'a': return BfTypeCode_Int8;
	case 'h': return BfTypeCode_UInt8;
	case 's': return BfTypeCode_Int16;
	case 't': return BfTypeCode_UInt16;
	case 'i': return BfTypeCode_Int32;
	case 'j': return BfTypeCode_UInt32;
    case 'l': return BfTypeCode_Int64;
    case 'm': return BfTypeCode_UInt64;
    case 'x': return BfTypeCode_Int64;
    case 'y': return BfTypeCode_UInt64;
	case 'u':
		if (name[strIdx + 1] == '3')
			return BfTypeCode_IntPtr;
		if (name[strIdx + 1] == '4')
			return BfTypeCode_UIntPtr;
		break;
	case 'c': return BfTypeCode_Char8;
	case 'D':
		if (name[strIdx + 1] == 'i')
			return BfTypeCode_Char32;
		else if (name[strIdx + 1] == 's')
			return BfTypeCode_Char16;
		break;
	case 'f': return BfTypeCode_Float;
	case 'd': return BfTypeCode_Double;
	}
	return (BfTypeCode)-1;
}

void BfGNUMangler::AddPrefix(MangleContext& mangleContext, StringImpl& name, int startIdx, const char* prefix)
{
	int subIdx;
	char startChar = name[startIdx];
	if (startChar == 'S')
	{
		subIdx = ParseSubIdx(name, startIdx);
		for (int matchIdx = subIdx + 1; matchIdx < (int)mangleContext.mSubstituteList.size(); matchIdx++)
		{
			auto& entry = mangleContext.mSubstituteList[matchIdx];
			if ((entry.mKind == NameSubstitute::Kind_Prefix) && (entry.mExtendsIdx == subIdx) && (entry.mPrefix == prefix))
			{
				BF_ASSERT(name.EndsWith('_'));
				name.RemoveToEnd(startIdx);
				AddSubIdx(name, matchIdx);
				return;
			}
		}
	}
	else
	{
		auto typeCode = GetPrimTypeAt(mangleContext, name, startIdx);
		if (typeCode != (BfTypeCode)-1)
		{
			for (int matchIdx = 0; matchIdx < (int)mangleContext.mSubstituteList.size(); matchIdx++)
			{
				auto& entry = mangleContext.mSubstituteList[matchIdx];
				if ((entry.mKind == NameSubstitute::Kind_PrimitivePrefix) && (entry.mExtendsTypeCode == typeCode) && (entry.mPrefix == prefix))
				{
					name.RemoveToEnd(startIdx);
					AddSubIdx(name, matchIdx);
					return;
				}
			}

			NameSubstitute nameSub;
			nameSub.mKind = NameSubstitute::Kind_PrimitivePrefix;
			nameSub.mExtendsTypeCode = typeCode;
			nameSub.mPrefix = prefix;
			mangleContext.mSubstituteList.push_back(nameSub);

			name.Insert(startIdx, prefix);
			return;
		}
		else
		{
			// Applies to last-added one
			subIdx = (int)mangleContext.mSubstituteList.size() - 1;
            BF_ASSERT(isdigit(startChar) || (startChar == 'A') || (startChar == 'N') || (startChar == 'P') || (startChar == 'R') || (startChar == 'U'));
		}
	}

	NameSubstitute nameSub;
	nameSub.mKind = NameSubstitute::Kind_Prefix;
	nameSub.mExtendsIdx = subIdx;
	nameSub.mPrefix = prefix;
	mangleContext.mSubstituteList.push_back(nameSub);

	name.Insert(startIdx, prefix);
}

void BfGNUMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl& name, const NameSubstitute& newNameSub)
{
	int curMatchIdx = -1;
	bool matchFailed = false;
	FindOrCreateNameSub(mangleContext, name, newNameSub, curMatchIdx, matchFailed);
	if (!matchFailed)
		AddSubIdx(name, curMatchIdx);
}

void BfGNUMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl& name, const NameSubstitute& newNameSub, int& curMatchIdx, bool& matchFailed)
{
	int parentIdx = curMatchIdx;
	if (!matchFailed)
	{
		curMatchIdx++;
		for ( ; curMatchIdx < (int)mangleContext.mSubstituteList.size(); curMatchIdx++)
		{
			auto& entry = mangleContext.mSubstituteList[curMatchIdx];
			if ((entry.mExtendsIdx == parentIdx) && (entry.mKind == newNameSub.mKind) && (entry.mParam == newNameSub.mParam))
			{
				return;
			}
		}

		matchFailed = true;
		if (newNameSub.mKind != BfGNUMangler::NameSubstitute::Kind_GenericParam)
			name += "N";
		if (parentIdx != -1)
			AddSubIdx(name, parentIdx);
	}

	if (newNameSub.mKind == NameSubstitute::Kind_NamespaceAtom)
	{
		AddSizedString(name, newNameSub.mAtom->mString.mPtr);
	}
	else if (newNameSub.mKind == NameSubstitute::Kind_TypeInstName)
	{
		auto typeDef = newNameSub.mTypeInst->mTypeDef;
		// Mixing this in could create linking errors since the references to the "correct" version won't necessarily be recompiled
		//  when we remove the "incorrect" version.  I believe this is no longer needed since our compilation model has changed
		/*if (typeDef->mDupDetectedRevision != -1)
		{
			char str[64];
			sprintf(str, "_%p", typeDef);
			name += str;
		}*/
		if ((typeDef->mIsDelegate) && (newNameSub.mTypeInst->IsClosure()))
		{
			auto closureType = (BfClosureType*)newNameSub.mTypeInst;
			if (!closureType->mCreatedTypeDef)
				name += closureType->mNameAdd;
		}
		AddSizedString(name, typeDef->mName->mString.mPtr);
	}
	else if (newNameSub.mKind == BfGNUMangler::NameSubstitute::Kind_TypeGenericArgs)
	{
		int genericParamStart = 0;
		if (newNameSub.mTypeInst->mTypeDef->mOuterType != NULL)
			genericParamStart = (int)newNameSub.mTypeInst->mTypeDef->mOuterType->mGenericParamDefs.size();

		name += "I";

		auto typeDef = newNameSub.mTypeInst->mTypeDef;

		BfType* checkType = newNameSub.mTypeInst;
		if (checkType->IsClosure())
		{
			checkType = ((BfClosureType*)checkType)->mSrcDelegate;
		}

		BF_ASSERT(checkType->IsGenericTypeInstance());
		BfTypeInstance* genericTypeInstance = (BfTypeInstance*)checkType;

		for (int genericParamIdx = genericParamStart; genericParamIdx < (int) typeDef->mGenericParamDefs.size(); genericParamIdx++)
		{
			auto genericParam = genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments[genericParamIdx];

			Mangle(mangleContext, name, genericParam);
		}

		name += "E";
	}
	else if (newNameSub.mKind == BfGNUMangler::NameSubstitute::Kind_GenericParam)
	{
		auto genericParamType = (BfGenericParamType*)newNameSub.mType;
		char str[16];
		if (genericParamType->mGenericParamIdx < 10)
			name += "3";
		else
			name += "4";
		if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
			name += "`M";
		else
			name += "`T";
		itoa(genericParamType->mGenericParamIdx, str, 10);
		name += str;
	}

	curMatchIdx = (int)mangleContext.mSubstituteList.size();
	mangleContext.mSubstituteList.push_back(newNameSub);

	auto& nameSubRef = mangleContext.mSubstituteList.back();
	nameSubRef.mExtendsIdx = parentIdx;
}

void BfGNUMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl& name, BfTypeInstance* typeInst, int& curMatchIdx, bool& matchFailed)
{
	auto typeDef = typeInst->mTypeDef;
	if (typeDef->IsGlobalsContainer())
		return;

	int numOuterGenericParams = 0;
	if (typeDef->mOuterType != NULL)
	{
		numOuterGenericParams = (int)typeDef->mOuterType->mGenericParamDefs.size();
		auto useModule = typeInst->mModule;
		if (useModule == NULL)
			useModule = mangleContext.mModule;
		auto outerType = useModule->GetOuterType(typeInst);
		if (outerType != NULL)
			FindOrCreateNameSub(mangleContext, name, outerType, curMatchIdx, matchFailed);
		else
			useModule->Fail("Failed to mangle name in BfGNUMangler::FindOrCreateNameSub");
	}

	FindOrCreateNameSub(mangleContext, name, NameSubstitute(NameSubstitute::Kind_TypeInstName, typeInst), curMatchIdx, matchFailed);

	if (typeDef->mGenericParamDefs.size() != numOuterGenericParams)
	{
		FindOrCreateNameSub(mangleContext, name, NameSubstitute(NameSubstitute::Kind_TypeGenericArgs, typeInst), curMatchIdx, matchFailed);
	}
}

void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name, BfTypeInstance* typeInst, BfTypeInstance* postfixTypeInstance, bool* isEndOpen)
{
	static int sCallCount = 0;
	sCallCount++;

	if (typeInst->IsTuple())
	{
		auto tupleType = (BfTypeInstance*)typeInst;
		name += "N7__TUPLEI";
		mangleContext.mSubstituteList.push_back(NameSubstitute(BfGNUMangler::NameSubstitute::Kind_None, NULL)); // Insert entry for '__TUPLE'
		for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
		{
			BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx];
			BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
			String fieldName = fieldDef->mName;
			if ((fieldName[0] < '0') || (fieldName[0] > '9'))
				name += StrFormat("U%d`%s", fieldName.length() + 1, fieldName.c_str());
			Mangle(mangleContext, name, fieldInstance->mResolvedType, postfixTypeInstance);
		}
		name += "E";
		mangleContext.mSubstituteList.push_back(NameSubstitute(BfGNUMangler::NameSubstitute::Kind_None, NULL)); // Insert entry for '__TUPLE<T>'
		if (isEndOpen != NULL)
			*isEndOpen = true;
		else
			name += "E";
		return;
	}
	else if ((typeInst->IsDelegateFromTypeRef()) || (typeInst->IsFunctionFromTypeRef()))
	{
		BF_ASSERT(typeInst->mTypeDef->mMethods[0]->mName == "Invoke");
		auto delegateInfo = typeInst->GetDelegateInfo();
		auto methodDef = typeInst->mTypeDef->mMethods[0];

		if (typeInst->IsDelegate())
			name += "N8delegateI";
		else
			name += "N8functionI";
		SizedArray<BfType*, 8> typeVec;
		typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mReturnTypeRef)->mType);
		if (methodDef->mIsMutating)
			name += "_mut_";
		if (delegateInfo->mCallingConvention == BfCallingConvention_Cdecl)
			name += "_cdecl_";
		else if (delegateInfo->mCallingConvention == BfCallingConvention_Stdcall)
			name += "_stdcall_";
		else if (delegateInfo->mCallingConvention == BfCallingConvention_Fastcall)
			name += "_fastcall_";
		for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++)
		{
			name += "_";
			name += methodDef->mParams[paramIdx]->mName;
			if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_VarArgs)
			{
				name += "__varargs";
				continue;
			}
			if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_Params)
				name += "_params_";
			typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mParams[paramIdx]->mTypeRef)->mType);
		}
		for (auto type : typeVec)
			Mangle(mangleContext, name, type, postfixTypeInstance);
		mangleContext.mSubstituteList.push_back(NameSubstitute(BfGNUMangler::NameSubstitute::Kind_None, NULL)); // Insert entry for 'Delegate'
		name += "E";
		mangleContext.mSubstituteList.push_back(NameSubstitute(BfGNUMangler::NameSubstitute::Kind_None, NULL)); // Insert entry for 'Delegate<T>'
		if (isEndOpen != NULL)
			*isEndOpen = true;
		else
			name += "E";
		name += "E";
	}
	else if (typeInst->IsBoxed())
	{
		auto boxedType = (BfBoxedType*)typeInst;
		name += "N3BoxI";
		mangleContext.mSubstituteList.push_back(NameSubstitute(BfGNUMangler::NameSubstitute::Kind_None, NULL)); // Insert entry for 'Box'
		Mangle(mangleContext, name, boxedType->GetModifiedElementType(), postfixTypeInstance);
		name += "E";
		mangleContext.mSubstituteList.push_back(NameSubstitute(BfGNUMangler::NameSubstitute::Kind_None, NULL)); // Insert entry for 'Box<T>'
		if (isEndOpen != NULL)
			*isEndOpen = true;
		else
			name += "E";
		return;
	}

	int curMatchIdx = -1;
	bool matchFailed = false;
	for (int typeIdx = 0; typeIdx < 2; typeIdx++)
	{
		BfTypeInstance* useTypeInst = typeInst;
		if (typeIdx == 1)
		{
			if (postfixTypeInstance == NULL)
				break;
			useTypeInst = postfixTypeInstance;
		}

		auto typeDef = useTypeInst->mTypeDef;

		if (!mangleContext.mCPPMangle)
			FindOrCreateNameSub(mangleContext, name, NameSubstitute(NameSubstitute::Kind_NamespaceAtom, typeInst->mModule->mSystem->mBfAtom), curMatchIdx, matchFailed);

		for (int i = 0; i < typeDef->mNamespace.mSize; i++)
			FindOrCreateNameSub(mangleContext, name, NameSubstitute(NameSubstitute::Kind_NamespaceAtom, typeDef->mNamespace.mParts[i]), curMatchIdx, matchFailed);

		FindOrCreateNameSub(mangleContext, name, useTypeInst, curMatchIdx, matchFailed);
	}
	if (isEndOpen != NULL)
	{
		if (!matchFailed)
		{
			if (curMatchIdx != -1)
			{
				name += "N";
				AddSubIdx(name, curMatchIdx);
				*isEndOpen = true;
			}
			else
				*isEndOpen = false;
		}
		else
			*isEndOpen = true;
		return;
	}

	if (matchFailed)
	{
		name += "E";
	}
	else
	{
		AddSubIdx(name, curMatchIdx);
	}
}

void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType* type, BfType* postfixType, bool isConst)
{
	static int sCallCount = 0;
	sCallCount++;

	if (type->IsPrimitiveType())
	{
		auto primType = (BfPrimitiveType*)type;

		switch (primType->mTypeDef->mTypeCode)
		{
		case BfTypeCode_NullPtr:
			{
				auto pointerType = (BfPointerType*)type;
				int startIdx = (int)name.length();
				name += "v";
				AddPrefix(mangleContext, name, startIdx, "P");
			}
			return;
		case BfTypeCode_Dot:
			name += "U3dot";
			return;
		case BfTypeCode_None:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
				name += "v";
			else
				name += "U4void";
			return;
		case BfTypeCode_Self:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
				name += "U8concrete";
			else
				name += "U4self";
			return;
		case BfTypeCode_Boolean:
			name += "b"; return;
		case BfTypeCode_Int8:
			name += "a"; return;
		case BfTypeCode_UInt8:
			name += "h"; return;
		case BfTypeCode_Int16:
			name += "s"; return;
		case BfTypeCode_UInt16:
			name += "t"; return;
		case BfTypeCode_Int32:
			name += "i"; return;
		case BfTypeCode_UInt32:
			name += "j"; return;
        case BfTypeCode_Int64:
			if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8))
				name += "l";
			else
				name += "x";
			return;
        case BfTypeCode_UInt64:
			if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8))
				name += "m";
			else
				name += "y";
			return;
		case BfTypeCode_UIntPtr:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
			{
				if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8))
					name += (primType->mSize == 8) ? "m" : "j";
				else
					name += (primType->mSize == 8) ? "y" : "j";
				return;
			}
			name += "u4uint";
			return;
		case BfTypeCode_IntPtr:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
			{
				if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8))
					name += (primType->mSize == 8) ? "l" : "i";
				else
					name += (primType->mSize == 8) ? "x" : "i";
				return;
			}
			name += "u3int";
			return;
		case BfTypeCode_Char8:
			name += "c"; return;
		case BfTypeCode_Char16:
			name += "Ds"; return;
		case BfTypeCode_Char32:
			name += "Di"; return;
		case BfTypeCode_Float:
			name += "f"; return;
		case BfTypeCode_Double:
			name += "d"; return;

		case BfTypeCode_Var:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
				name += "v";
			else
				name += "U3var";
			return;
		case BfTypeCode_Let:
			name += "U3let"; return;
		case BfTypeCode_IntUnknown:
			name += "U4iunk"; return;
		case BfTypeCode_UIntUnknown:
			name += "U4uunk"; return;
		default: break;
		}

		name += "?"; return;
		//name += Mangle(primType->mTypeDef, NULL, addName, NULL, substituteList);
	}
	else if (type->IsTypeInstance())
	{
		BfTypeInstance* postfixTypeInst = NULL;
		if (postfixType != NULL)
			postfixTypeInst = postfixType->ToTypeInstance();

		auto typeInstance = (BfTypeInstance*)type;

		int startIdx = (int)name.length();
		MangleTypeInst(mangleContext, name, typeInstance, postfixTypeInst);
		if ((type->IsObjectOrInterface()) && (mangleContext.mPrefixObjectPointer))
			AddPrefix(mangleContext, name, startIdx, "P");
	}
	else if (type->IsGenericParam())
	{
		FindOrCreateNameSub(mangleContext, name, NameSubstitute(NameSubstitute::Kind_GenericParam, type));
	}
	else if (type->IsPointer())
	{
		auto pointerType = (BfPointerType*)type;
		int startIdx = (int)name.length();
		Mangle(mangleContext, name, pointerType->mElementType);
		AddPrefix(mangleContext, name, startIdx, "P");
		return;
	}
	else if (type->IsRef())
	{
		BfRefType* refType = (BfRefType*)type;
		if (refType->mRefKind == BfRefType::RefKind_In)
		{
			isConst = true;
		}
		else if ((refType->mRefKind == BfRefType::RefKind_Mut) && (!mangleContext.mCCompat))
		{
			name += "U3mut";
			Mangle(mangleContext, name, refType->mElementType);
			return;
		}
		else if ((refType->mRefKind == BfRefType::RefKind_Out) && (!mangleContext.mCCompat))
		{
			name += "U3out";
			Mangle(mangleContext, name, refType->mElementType);
			return;
		}
		int startIdx = (int)name.length();
		Mangle(mangleContext, name, refType->mElementType);
		AddPrefix(mangleContext, name, startIdx, isConst ? "RK" : "R");
		return;
	}
	else if (type->IsModifiedTypeType())
	{
		BfModifiedTypeType* retTypeType = (BfModifiedTypeType*)type;
		if (retTypeType->mModifiedKind == BfToken_RetType)
			name += "U7rettype";
		else if (retTypeType->mModifiedKind == BfToken_AllocType)
			name += "U5alloc";
		else if (retTypeType->mModifiedKind == BfToken_Nullable)
			name += "U8nullable";
		else if (retTypeType->mModifiedKind == BfToken_Params)
			name += "U6params";
		else
			BF_FATAL("Unhandled");
		Mangle(mangleContext, name, retTypeType->mElementType);
		return;
	}
	else if (type->IsConcreteInterfaceType())
	{
		BfConcreteInterfaceType* concreteInterfaceType = (BfConcreteInterfaceType*)type;
		name += "U8concrete";
		Mangle(mangleContext, name, concreteInterfaceType->mInterface);
		return;
	}
	else if (type->IsSizedArray())
	{
		if (type->IsUnknownSizedArrayType())
		{
			BfUnknownSizedArrayType* arrayType = (BfUnknownSizedArrayType*)type;
			name += "A_";
			Mangle(mangleContext, name, arrayType->mElementType);
			name += "_";
			Mangle(mangleContext, name, arrayType->mElementCountSource);
			return;
		}
		else
		{
			BfSizedArrayType* arrayType = (BfSizedArrayType*)type;
			name += StrFormat("A%d_", arrayType->mElementCount);
			Mangle(mangleContext, name, arrayType->mElementType);
			return;
		}
	}
	else if (type->IsMethodRef())
	{
		auto methodRefType = (BfMethodRefType*)type;
		String mrefName = "mref_";
		String mangleName;
		BfMethodInstance* methodInstance = methodRefType->mMethodRef;
		if (methodInstance == NULL)
		{
			BF_ASSERT(!methodRefType->mMangledMethodName.IsEmpty());
			mangleName = methodRefType->mMangledMethodName;
		}
		else
		{
			if (methodInstance->mIsAutocompleteMethod)
				name += "AUTOCOMPLETE";

			auto module = methodInstance->GetOwner()->mModule;
			if (module->mCompiler->mIsResolveOnly)
			{
				// There are cases where we will reprocess a method in ResolveOnly for things like
				//  GetSymbolReferences, so we will have duplicate live local methodInstances in those cases
				mrefName += HashEncode64((uint64)methodRefType->mMethodRef);
			}
			else
			{
				mangleName = Mangle(methodInstance);
				methodRefType->mMangledMethodName = mangleName;
			}
		}

		if (!mangleName.IsEmpty())
		{
			Val128 val128 = Hash128(mangleName.c_str(), (int)mangleName.length());
			mrefName += HashEncode128(val128);
		}

		//TODO: Make a 'U' name?
		name += mrefName;
	}
	else if (type->IsConstExprValue())
	{
		BfConstExprValueType* constExprValueType = (BfConstExprValueType*)type;
		int64 val = constExprValueType->mValue.mInt64;

		if ((!constExprValueType->mType->IsPrimitiveType()) ||
			(((BfPrimitiveType*)constExprValueType->mType)->mTypeDef->mTypeCode != BfTypeCode_IntPtr))
		{
			Mangle(mangleContext, name, constExprValueType->mType);
		}

		name += "$0";

		if (val < 0)
		{
			name += "?";
			val = -val;
		}
		if ((val >= 1) && (val < 10))
		{
			name += (char)('0' + val - 1);
		}
		else
		{
			char str[64];
			char* strP = str + 63;
			*strP = 0;

			while (val > 0)
			{
				*(--strP) = (char)((val % 0x10) + 'A');
				val /= 0x10;
			}

			name += strP;
			name += '`';
		}

		if (constExprValueType->mValue.mTypeCode == BfTypeCode_Let)
			name += "Undef";
	}
	else
	{
		BF_FATAL("Not handled");
	}
}

String BfGNUMangler::Mangle(BfType* type, BfModule* module)
{
	StringT<256> name;
	name += "_ZTS";
	MangleContext mangleContext;
	mangleContext.mModule = module;
	Mangle(mangleContext, name, type);
	return name;
}

String BfGNUMangler::Mangle(BfMethodInstance* methodInst)
{
	StringT<256> name;

	if ((methodInst->mMethodDef->mCLink) && (!methodInst->mMangleWithIdx))
	{
		return methodInst->mMethodDef->mName;
	}

	auto methodDef = methodInst->mMethodDef;
	auto methodDeclaration = BfNodeDynCastExact<BfMethodDeclaration>(methodDef->mMethodDeclaration);
	auto typeInst = methodInst->GetOwner();
	auto typeDef = typeInst->mTypeDef;

	MangleContext mangleContext;
	mangleContext.mModule = methodInst->GetOwner()->mModule;
	if (methodInst->mCallingConvention != BfCallingConvention_Unspecified)
		mangleContext.mCCompat = true;
	bool isCMangle = false;
	HandleCustomAttributes(methodInst->GetCustomAttributes(), typeInst->mConstHolder, mangleContext.mModule, name, isCMangle, mangleContext.mCPPMangle);
	if (isCMangle)
		name += methodInst->mMethodDef->mName;
	if (!name.IsEmpty())
		return name;

	bool mangledMethodIdx = false;
	bool prefixLen = false;

	bool isNameOpen = false;
	name += "_Z";
	MangleTypeInst(mangleContext, name, methodInst->mMethodInstanceGroup->mOwner, methodInst->GetExplicitInterface(), &isNameOpen);

	if (methodInst->GetForeignType() != NULL)
	{
		// This won't demangle correctly.  TODO: Do this 'correctly'
		MangleTypeInst(mangleContext, name, methodInst->GetForeignType());
	}

	mangleContext.mPrefixObjectPointer = true;
	StringT<128> methodName = methodInst->mMethodDef->mName;
	for (int i = 0; i < (int)methodName.length(); i++)
	{
		if (methodName[i] == '@')
			methodName[i] = '$';
	}
	if ((!mangleContext.mCPPMangle) && (!methodDef->mIsMutating) && (!methodDef->mIsStatic) && (methodInst->GetOwner()->IsValueType()))
		methodName += "__im";

	if (methodInst->mMethodDef->mIsOperator)
	{
		auto operatorDef = (BfOperatorDef*)methodInst->mMethodDef;
		if (operatorDef->mOperatorDeclaration->mIsConvOperator)
		{
			methodName = "cv";
			Mangle(mangleContext, methodName, methodInst->mReturnType);
		}
		else
		{
			switch (operatorDef->mOperatorDeclaration->mBinOp)
			{
			case BfBinaryOp_Add:
				methodName = "pl";
				break;
			case BfBinaryOp_Subtract:
				methodName = "mi";
				break;
			case BfBinaryOp_Multiply:
				methodName = "ml";
				break;
			case BfBinaryOp_OverflowAdd:
				methodName = "opl";
				break;
			case BfBinaryOp_OverflowSubtract:
				methodName = "omi";
				break;
			case BfBinaryOp_OverflowMultiply:
				methodName = "oml";
				break;
			case BfBinaryOp_Divide:
				methodName = "dv";
				break;
			case BfBinaryOp_Modulus:
				methodName = "rm";
				break;
			case BfBinaryOp_BitwiseAnd:
				methodName = "an";
				break;
			case BfBinaryOp_BitwiseOr:
				methodName = "or";
				break;
			case BfBinaryOp_ExclusiveOr:
				methodName = "eo";
				break;
			case BfBinaryOp_LeftShift:
				methodName = "ls";
				break;
			case BfBinaryOp_RightShift:
				methodName = "rs";
				break;
			case BfBinaryOp_Equality:
				methodName = "eq";
				break;
			case BfBinaryOp_InEquality:
				methodName = "ne";
				break;
			case BfBinaryOp_GreaterThan:
				methodName = "gt";
				break;
			case BfBinaryOp_LessThan:
				methodName = "lt";
				break;
			case BfBinaryOp_GreaterThanOrEqual:
				methodName = "ge";
				break;
			case BfBinaryOp_LessThanOrEqual:
				methodName = "le";
				break;
			case BfBinaryOp_ConditionalAnd:
				methodName = "aa";
				break;
			case BfBinaryOp_ConditionalOr:
				methodName = "oo";
				break;
			case BfBinaryOp_NullCoalesce:
				methodName = "2nc";
				break;
			case BfBinaryOp_Is:
				methodName = "2is";
				break;
			case BfBinaryOp_As:
				methodName = "2as";
				break;
			default: break;
			}

			switch (operatorDef->mOperatorDeclaration->mUnaryOp)
			{
			case BfUnaryOp_AddressOf:
				methodName = "ad";
				break;
			case BfUnaryOp_Dereference:
				methodName = "de";
				break;
			case BfUnaryOp_Negate:
				methodName = "ng";
				break;
			case BfUnaryOp_Not:
				methodName = "nt";
				break;
			case BfUnaryOp_Positive:
				methodName = "ps";
				break;
			case BfUnaryOp_InvertBits:
				methodName = "co";
				break;
			case BfUnaryOp_Increment:
				methodName = "pp";
				break;
			case BfUnaryOp_Decrement:
				methodName = "mm";
				break;
			case BfUnaryOp_PostIncrement:
				methodName = "pp";
				break;
			case BfUnaryOp_PostDecrement:
				methodName = "mm";
				break;
			case BfUnaryOp_Ref:
				methodName = "3ref";
				break;
			case BfUnaryOp_Mut:
				methodName = "3mut";
				break;
			case BfUnaryOp_Out:
				methodName = "3out";
				break;
			default: break;
			}
		}
	}
	else if (methodInst->mMethodDef->mMethodType == BfMethodType_Ctor)
	{
		if (methodInst->mMethodDef->mIsStatic)
		{
			methodName = "__BfStaticCtor";
			prefixLen = true;
		}
		else
			methodName = "C1";
	}
	else if (methodInst->mMethodDef->mMethodType == BfMethodType_Dtor)
	{
		if (methodInst->mMethodDef->mIsStatic)
		{
			methodName = "__BfStaticDtor";
			prefixLen = true;
		}
		else
			methodName = "D1";
	}
	else
	{
		if (methodInst->mMangleWithIdx)
		{
			methodName += StrFormat("`%d", methodInst->mMethodDef->mIdx);
			mangledMethodIdx = true;
		}

		prefixLen = true;
	}

	if (!mangleContext.mCPPMangle)
	{
		if (methodDef->mCheckedKind == BfCheckedKind_Checked)
			name += "`CHK";
		else if (methodDef->mCheckedKind == BfCheckedKind_Unchecked)
			name += "`UCHK";

		if (methodDef->mHasComptime)
			name += "`COMPTIME";
	}

	if (((methodInst->GetOwner()->mTypeDef->IsGlobalsContainer()) &&
		 ((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_Dtor) || (methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC))) ||
		((methodInst->mMethodDef->mDeclaringType->mPartialIdx != -1) && (methodInst->mMethodDef->mDeclaringType->IsExtension()) &&
		 (!methodInst->mIsForeignMethodDef) && (!methodInst->mMethodDef->mIsExtern) &&
		 ((!methodInst->mMethodDef->mIsOverride) || (methodDef->mName == BF_METHODNAME_MARKMEMBERS) || (methodDef->mMethodType == BfMethodType_Dtor))))
	{
		auto declType = methodInst->mMethodDef->mDeclaringType;
		BF_ASSERT(methodInst->GetOwner()->mTypeDef->mIsCombinedPartial);
		auto declProject = declType->mProject;
		bool addProjectName = (declProject != typeInst->mTypeDef->mProject);
		bool addIndex = true;

		if (typeInst->mTypeDef->IsGlobalsContainer())
		{
			addProjectName = true;

			if ((methodInst->mCallingConvention == BfCallingConvention_Cdecl) ||
				(methodInst->mCallingConvention == BfCallingConvention_Stdcall))
			{
				addProjectName = false;
				addIndex = false;
			}
		}

		if (addProjectName)
		{
			name += declProject->mName;
			name += "$";
		}
		if (addIndex)
			name += StrFormat("%d$", declType->mPartialIdx);
	}

	if ((methodInst->mMangleWithIdx) && (!mangledMethodIdx))
	{
		methodName += StrFormat("`%d", methodInst->mMethodDef->mIdx);
	}

	//

	if ((prefixLen) && (methodInst->mMethodInstanceGroup->mOwner->mTypeDef->IsGlobalsContainer()) && (methodInst->mMethodDef->mMethodDeclaration == NULL))
	{
		methodName += '`';
		methodName += methodInst->mMethodInstanceGroup->mOwner->mTypeDef->mProject->mName;
	}

	if (prefixLen)
		AddSizedString(name, methodName);
	else
		name += methodName;

	if (methodInst->GetNumGenericArguments() != 0)
	{
		auto& methodGenericArguments = methodInst->mMethodInfoEx->mMethodGenericArguments;
		NameSubstitute nameSub(NameSubstitute::Kind_MethodName, methodInst);
		nameSub.mExtendsIdx = (int)mangleContext.mSubstituteList.size() - 1;
		mangleContext.mSubstituteList.push_back(nameSub);

		name += 'I';
		for (auto genericArg : methodGenericArguments)
			Mangle(mangleContext, name, genericArg);
		name += 'E';
	}
	else if (methodInst->mMethodDef->mGenericParams.size() != 0)
	{
		NameSubstitute nameSub(NameSubstitute::Kind_MethodName, methodInst);
		nameSub.mExtendsIdx = (int)mangleContext.mSubstituteList.size() - 1;
		mangleContext.mSubstituteList.push_back(nameSub);

		name += 'I';
		for (auto genericArg : methodInst->mMethodDef->mGenericParams)
			name += 'v';
		name += 'E';
	}

	if (isNameOpen)
		name += 'E';

	if (methodInst->mMethodDef->mGenericParams.size() != 0)
		Mangle(mangleContext, name, methodInst->mReturnType);

	mangleContext.mInArgs = true;

	bool doExplicitThis = (!methodDef->mIsStatic) && (typeInst->IsTypedPrimitive());

	if (doExplicitThis) // Explicit "_this"
		Mangle(mangleContext, name, typeInst->GetUnderlyingType());

	for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++)
	{
		BfType* paramType = methodInst->GetParamType(paramIdx);

		bool isConst = false;
		if ((methodDeclaration != NULL) && (paramIdx < methodDeclaration->mParams.mSize))
		{
			auto paramDecl = methodDeclaration->mParams[paramIdx];
			HandleParamCustomAttributes(paramDecl->mAttributes, false, isConst);
		}

		if (!mangleContext.mCPPMangle)
		{
			auto paramKind = methodInst->GetParamKind(paramIdx);
			if (paramKind == BfParamKind_Params)
				name += "U6params";
			else if (paramKind == BfParamKind_DelegateParam)
				name += "U5param";
		}

		if ((paramType->IsVoid()) && (methodInst->GetParamCount() == 1))
		{
			// Avoid collision between 'Method()' and 'Method(void)'
			name += "U4void";
		}
		else
			Mangle(mangleContext, name, paramType, NULL, isConst);
	}
	if ((methodInst->GetParamCount() == 0) && (!doExplicitThis))
		name += 'v';

	return name;
}

String BfGNUMangler::MangleMethodName(BfTypeInstance* type, const StringImpl& methodName)
{
	MangleContext mangleContext;
	mangleContext.mModule = type->mModule;
	StringT<256> name = "_Z";

	auto typeInst = type->ToTypeInstance();
	BF_ASSERT(typeInst != NULL);

	bool isNameOpen;
	MangleTypeInst(mangleContext, name, typeInst, NULL, &isNameOpen);
	AddSizedString(name, methodName);
	if (isNameOpen)
		name += "E";
	name += "v";
	return name;
}

String BfGNUMangler::MangleStaticFieldName(BfTypeInstance* type, const StringImpl& fieldName)
{
	MangleContext mangleContext;
	mangleContext.mModule = type->mModule;

	auto typeInst = type->ToTypeInstance();
	auto typeDef = typeInst->mTypeDef;
	if ((typeDef->IsGlobalsContainer()) && (typeDef->mNamespace.IsEmpty()))
		return fieldName;

	StringT<256> name = "_Z";
	bool isNameOpen;
	MangleTypeInst(mangleContext, name, typeInst, NULL, &isNameOpen);
	AddSizedString(name, fieldName);
	if (isNameOpen)
		name += "E";
	return name;
}

String BfGNUMangler::Mangle(BfFieldInstance* fieldInstance)
{
	StringT<256> name;
	MangleContext mangleContext;
	mangleContext.mModule = fieldInstance->mOwner->mModule;

	bool isCMangle = false;
	HandleCustomAttributes(fieldInstance->mCustomAttributes, fieldInstance->mOwner->mConstHolder, mangleContext.mModule, name, isCMangle, mangleContext.mCPPMangle);
	if (isCMangle)
		name += fieldInstance->GetFieldDef()->mName;
	if (!name.IsEmpty())
		return name;

	auto typeInst = fieldInstance->mOwner->ToTypeInstance();
	auto typeDef = typeInst->mTypeDef;
	if ((typeDef->IsGlobalsContainer()) && (typeDef->mNamespace.IsEmpty()))
		return fieldInstance->GetFieldDef()->mName;

	name += "_Z";
	bool isNameOpen;
	MangleTypeInst(mangleContext, name, typeInst, NULL, &isNameOpen);
	AddSizedString(name, fieldInstance->GetFieldDef()->mName);
	if (isNameOpen)
		name += "E";
	return name;
}

//////////////////////////////////////////////////////////////////////////

BfModule* BfMSMangler::MangleContext::GetUnreifiedModule()
{
	if (mModule == NULL)
		return NULL;
	return mModule->mContext->mUnreifiedModule;
}

//////////////////////////////////////////////////////////////////////////

void BfMSMangler::AddGenericArgs(MangleContext& mangleContext, StringImpl& name, const SizedArrayImpl<BfType*>& genericArgs, int numOuterGenericParams)
{
	if (numOuterGenericParams == (int)genericArgs.size())
		return;

	auto prevSubList = mangleContext.mSubstituteList;
	auto prevSubTypeList = mangleContext.mSubstituteTypeList;
	mangleContext.mSubstituteList.clear();

	for (int genericIdx = numOuterGenericParams; genericIdx < (int)genericArgs.size(); genericIdx++)
		Mangle(mangleContext, name, genericArgs[genericIdx]);

	mangleContext.mSubstituteList = prevSubList;
	mangleContext.mSubstituteTypeList = prevSubTypeList;
}

void BfMSMangler::AddStr(MangleContext& mangleContext, StringImpl& name, const StringImpl& str)
{
	if ((int)mangleContext.mSubstituteList.size() < 10)
	{
		// Add NULL entry... shouldn't match anything
		mangleContext.mSubstituteList.push_back(NameSubstitute(NameSubstitute::Kind_None, NULL));
	}

	name += str;
	name += '@';
}

void BfMSMangler::AddSubIdx(StringImpl& name, int strIdx)
{
	//TODO: BF_ASSERT(strIdx < 10);
	char str[16];
	itoa(strIdx, str, 10);
	name += str;
}

void BfMSMangler::AddTypeStart(MangleContext& mangleContext, StringImpl& name, BfType* type)
{
	if (mangleContext.mIsSafeMangle)
	{
		name += "V"; // Mangle everything as a struct so we don't need to require the type to be populated
		return;
	}

	if (!type->IsDeclared())
	{
		if (mangleContext.mModule != NULL)
		{
			auto unreifiedModule = mangleContext.GetUnreifiedModule();
			if (unreifiedModule != NULL)
				unreifiedModule->PopulateType(type, BfPopulateType_Declaration);
		}
		else
		{
			BF_FATAL("No module");
		}
	}

	if ((type->IsEnum()) && (type->IsTypedPrimitive()))
	{
		auto unreifiedModule = mangleContext.GetUnreifiedModule();
		if (type->mDefineState >= BfTypeDefineState_Defined)
		{
			BF_ASSERT(type->mSize >= 0);
		}

		// The enum size is supposed to be encoded, but VC always uses '4'
		//name += "W";
		//name += ('0' + type->mSize);
		name += "W4";
		return;
	}

	name += type->IsStruct() ? "U" : "V";
}

bool BfMSMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl& name, const NameSubstitute& newNameSub)
{
	for (int curMatchIdx = 0; curMatchIdx < (int)mangleContext.mSubstituteList.size(); curMatchIdx++)
	{
		auto& entry = mangleContext.mSubstituteList[curMatchIdx];
		if ((entry.mKind == newNameSub.mKind) && (entry.mParam == newNameSub.mParam) && (entry.mExtendsTypeId == newNameSub.mExtendsTypeId))
		{
			AddSubIdx(name, curMatchIdx);
			return true;
		}
	}

	if (newNameSub.mKind == NameSubstitute::Kind_NamespaceAtom)
	{
		name += newNameSub.mAtom->mString;
		name += '@';
	}
	else if (newNameSub.mKind == NameSubstitute::Kind_TypeInstName)
	{
		if (mangleContext.mWantsGroupStart)
		{
			AddTypeStart(mangleContext, name, newNameSub.mTypeInst);
			mangleContext.mWantsGroupStart = false;
		}

		if (newNameSub.mTypeInst->IsTuple())
		{
			auto tupleType = (BfTypeInstance*)newNameSub.mType;
			name += "?$__TUPLE";
			SizedArray<BfType*, 8> typeVec;
			for (auto& fieldInst : tupleType->mFieldInstances)
			{
				BfFieldDef* fieldDef = fieldInst.GetFieldDef();
				String fieldName = fieldDef->mName;
				if ((fieldName[0] < '0') || (fieldName[0] > '9'))
					name += StrFormat("_%s", fieldName.c_str());
				typeVec.push_back(fieldInst.mResolvedType);
			}
			name += '@';
			if (!typeVec.empty())
				AddGenericArgs(mangleContext, name, typeVec);
			name += '@';
		}
		else if ((newNameSub.mTypeInst->IsDelegateFromTypeRef()) || (newNameSub.mTypeInst->IsFunctionFromTypeRef()))
		{
			BF_ASSERT(newNameSub.mTypeInst->mTypeDef->mMethods[0]->mName == "Invoke");

			auto delegateInfo = newNameSub.mTypeInst->GetDelegateInfo();

			auto methodDef = newNameSub.mTypeInst->mTypeDef->mMethods[0];
			if (newNameSub.mTypeInst->IsDelegate())
				name += "?$delegate";
			else
				name += "?$function";
			if (methodDef->mIsMutating)
				name += "_mut_";
			if (delegateInfo->mCallingConvention == BfCallingConvention_Cdecl)
				name += "_cdecl_";
			else if (delegateInfo->mCallingConvention == BfCallingConvention_Stdcall)
				name += "_stdcall_";
			else if (delegateInfo->mCallingConvention == BfCallingConvention_Fastcall)
				name += "_fastcall_";

			SizedArray<BfType*, 8> typeVec;
			typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mReturnTypeRef)->mType);

			for (int paramIdx = 0; paramIdx < (int)methodDef->mParams.size(); paramIdx++)
			{
				name += "_";
				name += methodDef->mParams[paramIdx]->mName;
				if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_VarArgs)
				{
					name += "__varargs";
					continue;
				}
				if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_Params)
					name += "_params_";
				typeVec.push_back(BfNodeDynCast<BfDirectTypeReference>(methodDef->mParams[paramIdx]->mTypeRef)->mType);
			}
			name += '@';
			if (!typeVec.empty())
				AddGenericArgs(mangleContext, name, typeVec);
			name += '@';
		}
		else if (newNameSub.mTypeInst->IsBoxed())
		{
			auto boxedType = (BfBoxedType*)newNameSub.mTypeInst;
			name += "?$Box@";
			SizedArray<BfType*, 8> typeVec;
			typeVec.push_back(boxedType->GetModifiedElementType());
			AddGenericArgs(mangleContext, name, typeVec);
			name += '@';
		}
		else
		{
			auto typeDef = newNameSub.mTypeInst->mTypeDef;

			BfTypeInstance* genericTypeInst = NULL;
			if (newNameSub.mTypeInst->IsGenericTypeInstance())
				genericTypeInst = (BfTypeInstance*)newNameSub.mTypeInst;

			int numOuterGenericParams = 0;
			if ((!mangleContext.mIsSafeMangle) && (typeDef->mOuterType != NULL))
			{
				numOuterGenericParams = (int)typeDef->mOuterType->mGenericParamDefs.size();
			}

			if (genericTypeInst != NULL)
			{
				// If we don't have our own generic params then don't treat us as a generic
				if ((int)typeDef->mGenericParamDefs.size() == numOuterGenericParams)
					genericTypeInst = NULL;
			}

			if (genericTypeInst != NULL)
			{
				name += "?$";
				mangleContext.mWantsGroupStart = false;
			}

// 			name += *typeDef->mName->mString;
// 			if ((typeDef->mIsDelegate) && (newNameSub.mTypeInst->IsClosure()))
// 			{
// 				auto closureType = (BfClosureType*)newNameSub.mTypeInst;
// 				if (!closureType->mCreatedTypeDef)
// 					name += closureType->mNameAdd;
// 			}

			if (newNameSub.mTypeInst->IsClosure())
			{
				auto closureType = (BfClosureType*)newNameSub.mTypeInst;
				name += closureType->mSrcDelegate->mTypeDef->mName->mString;
				name += closureType->mNameAdd;
			}
			else
			{
				name += typeDef->mName->mString;
			}

			name += '@';

			if (genericTypeInst != NULL)
			{
				AddGenericArgs(mangleContext, name, genericTypeInst->mGenericTypeInfo->mTypeGenericArguments, numOuterGenericParams);
				name += '@';
			}
		}
	}
	else if (newNameSub.mKind == BfGNUMangler::NameSubstitute::Kind_GenericParam)
	{
		name += "U"; // Struct
		auto genericParamType = (BfGenericParamType*)newNameSub.mType;
		if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
			name += "_M";
		else
			name += "_T";
		char str[16];
		itoa(genericParamType->mGenericParamIdx, str, 10);
		name += str;
		name += '@';
		name += '@';
	}

	mangleContext.mSubstituteList.push_back(newNameSub);

	return false;
}

void BfMSMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfTypeInstance* typeInstance, bool isAlreadyStarted, bool isOuterType)
{
	BfTypeInstance* genericTypeInst = NULL;
	if (typeInstance->IsGenericTypeInstance())
	{
		genericTypeInst = (BfTypeInstance*)typeInstance;
	}

	auto typeDef = typeInstance->mTypeDef;
	bool hasNamespace = typeDef->mNamespace.mSize != 0;
	if (!isAlreadyStarted)
	{
		if ((hasNamespace) || (typeDef->mOuterType != NULL))
		{
			AddTypeStart(mangleContext, name, typeInstance);
		}
		else
		{
			if (typeDef->IsGlobalsContainer())
				return;
			mangleContext.mWantsGroupStart = true;
		}
	}

	if (!typeDef->IsGlobalsContainer())
		FindOrCreateNameSub(mangleContext, name, NameSubstitute(BfMangler::NameSubstitute::Kind_TypeInstName, typeInstance));
	mangleContext.mWantsGroupStart = false;

	auto useModule = typeInstance->mModule;
	if (useModule == NULL)
		useModule = mangleContext.mModule;

	if ((typeDef->mOuterType != NULL) && (!typeInstance->IsBoxed()))
	{
		if (mangleContext.mIsSafeMangle)
		{
			auto outerType = typeDef->mOuterType;
			while (outerType != NULL)
			{
				name += ":";
				name += outerType->mFullName.ToString();
				outerType = outerType->mOuterType;
			}
		}
		else
		{
			auto unreifiedModule = useModule->mContext->mUnreifiedModule;
			auto outerType = unreifiedModule->GetOuterType(typeInstance);
			if (outerType != NULL)
				Mangle(mangleContext, name, outerType, true, true);
			else
				useModule->Fail("Failed to mangle name in BfMSMangler::Mangle");
		}
	}

	if (!isOuterType)
	{
		if (!typeInstance->IsBoxed())
		{
			for (int namespaceIdx = (int)typeDef->mNamespace.mSize - 1; namespaceIdx >= 0; namespaceIdx--)
			{
				auto namePart = typeDef->mNamespace.mParts[namespaceIdx];
				FindOrCreateNameSub(mangleContext, name, NameSubstitute(BfMangler::NameSubstitute::Kind_NamespaceAtom, namePart));
			}

			if (!mangleContext.mCPPMangle)
				FindOrCreateNameSub(mangleContext, name, NameSubstitute(BfMangler::NameSubstitute::Kind_NamespaceAtom, useModule->mSystem->mBfAtom));
		}

		name += '@';
	}
}

void BfMSMangler::MangleConst(MangleContext & mangleContext, StringImpl & name, int64 val)
{
	name += "$0";
	if (val < 0)
	{
		name += "?";
		val = -val;
	}
	if ((val > 0) && (val <= 10))
	{
		name += (char)('0' + val - 1);
	}
	else
	{
		char str[64];
		char* strP = str + 63;
		*strP = 0;

		while (val > 0)
		{
			*(--strP) = (char)((val % 0x10) + 'A');
			val /= 0x10;
		}

		name += strP;
		name += '@';
	}
}

void BfMSMangler::AddPrefix(MangleContext& mangleContext, StringImpl& name, int startIdx, const char* prefix)
{
	int subIdx;
	char startChar = name[startIdx];
	if ((startChar >= '0') && (startChar <= '9'))
	{
		//subIdx = ParseSubIdx(name, startIdx);
		subIdx = startChar - '0';
		for (int matchIdx = subIdx + 1; matchIdx < (int)mangleContext.mSubstituteList.size(); matchIdx++)
		{
			auto& entry = mangleContext.mSubstituteList[matchIdx];
			if ((entry.mKind == NameSubstitute::Kind_Prefix) && (entry.mExtendsIdx == subIdx) && (entry.mPrefix == prefix))
			{
				BF_ASSERT(name.EndsWith('_'));
				name.RemoveToEnd(startIdx);
				AddSubIdx(name, matchIdx);
				return;
			}
		}
	}
	else
	{
		//auto typeCode = GetPrimTypeAt(mangleContext, name, startIdx);
		auto typeCode = BfTypeCode_None;
		if (typeCode != (BfTypeCode)-1)
		{
			for (int matchIdx = 0; matchIdx < (int)mangleContext.mSubstituteList.size(); matchIdx++)
			{
				auto& entry = mangleContext.mSubstituteList[matchIdx];
				if ((entry.mKind == NameSubstitute::Kind_PrimitivePrefix) && (entry.mExtendsTypeCode == typeCode) && (entry.mPrefix == prefix))
				{
					name.RemoveToEnd(startIdx);
					AddSubIdx(name, matchIdx);
					return;
				}
			}

			NameSubstitute nameSub;
			nameSub.mKind = NameSubstitute::Kind_PrimitivePrefix;
			nameSub.mExtendsTypeCode = typeCode;
			nameSub.mPrefix = prefix;
			mangleContext.mSubstituteList.push_back(nameSub);

			name.Insert(startIdx, prefix);
			return;
		}
		else
		{
			// Applies to last-added one
			subIdx = (int)mangleContext.mSubstituteList.size() - 1;
			BF_ASSERT(isdigit(startChar) || (startChar == 'N') || (startChar == 'P') || (startChar == 'R'));
		}
	}

	NameSubstitute nameSub;
	nameSub.mKind = NameSubstitute::Kind_Prefix;
	nameSub.mExtendsIdx = subIdx;
	nameSub.mPrefix = prefix;
	mangleContext.mSubstituteList.push_back(nameSub);

	name.Insert(startIdx, prefix);
}

void BfMSMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType* type, bool useTypeList, bool isConst)
{
	bool isLongPrim = false;

	if (type->IsPrimitiveType())
	{
		auto primType = (BfPrimitiveType*)type;

		switch (primType->mTypeDef->mTypeCode)
		{
		case BfTypeCode_NullPtr:
			{
				name += "P";
				if (mangleContext.mIs64Bit)
					name += "E";
				name += "AV";
				name += "v";
			}
			return;
		case BfTypeCode_None:
			name += "X"; return;
		case BfTypeCode_Int8:
			name += "C"; return;
		case BfTypeCode_UInt8:
			name += "E"; return;
		case BfTypeCode_Int16:
			name += "F"; return;
		case BfTypeCode_UInt16:
			name += "G"; return;
		case BfTypeCode_Int32:
			name += "H"; return;
		case BfTypeCode_UInt32:
			name += "I"; return;
		case BfTypeCode_Char8:
			name += "D"; return;
		case BfTypeCode_Char16:
			name += "_S"; return; //char16_t
		case BfTypeCode_Float:
			name += "M"; return;
		case BfTypeCode_Double:
			name += "N"; return;
		case BfTypeCode_Int64:
		case BfTypeCode_UInt64:
		case BfTypeCode_Boolean:
		case BfTypeCode_Char32:
			isLongPrim = true;
			break;
		case BfTypeCode_IntPtr:
			if ((primType->mSize == 4) && (mangleContext.mCCompat))
			{
				name += "H";
				return;
			}
			isLongPrim = true;
			break;
		case BfTypeCode_UIntPtr:
			if ((primType->mSize == 4) && (mangleContext.mCCompat))
			{
				name += "I";
				return;
			}
			isLongPrim = true;
			break;

		case BfTypeCode_Dot:
			name += "Tdot@@"; return;
		case BfTypeCode_Var:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
				name += "X";
			else
				name += "Tvar@@";
			return;
		case BfTypeCode_Self:
			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
				name += "X";
			else
				name += "Tself@@";
			return;
		case BfTypeCode_Let:
			name += "Tlet@@"; return;
		case BfTypeCode_IntUnknown:
			name += "Tiunk@@"; return;
		case BfTypeCode_UIntUnknown:
			name += "Tuunk@@"; return;

		default:
			name += "?"; return;
		}
	}

	if (useTypeList)
	{
		for (int checkIdx = 0; checkIdx < (int)mangleContext.mSubstituteTypeList.size(); checkIdx++)
		{
			if (mangleContext.mSubstituteTypeList[checkIdx] == type)
			{
				name += ('0' + checkIdx);
				return;
			}
		}
	}

	if (isLongPrim)
	{
		auto primType = (BfPrimitiveType*)type;
		switch (primType->mTypeDef->mTypeCode)
		{
		case BfTypeCode_Boolean:
			name += "_N"; break;
		case BfTypeCode_Int64:
			name += "_J"; break;
		case BfTypeCode_UInt64:
			name += "_K"; break;
		case BfTypeCode_UIntPtr:
			if (primType->mSize == 4)
				name += "_I";
 			else if (mangleContext.mCCompat)
				name += "_K";
 			else
 				name += "Tuint@@";
			break;
		case BfTypeCode_IntPtr:
			if (primType->mSize == 4)
				name += "_H";
			else if (mangleContext.mCCompat)
				name += "_J";
			else
				name += "Tint@@";
			break;
		case BfTypeCode_Char32:
			name += "_U"; break;
		default: break;
		}
	}
	else if (type->IsTypeInstance())
	{
		auto typeInstance = (BfTypeInstance*)type;
		auto typeDef = typeInstance->mTypeDef;

		if (type->IsObjectOrInterface())
		{
			name += "P";
			if (mangleContext.mIs64Bit)
				name += "E";
			name += "A";
		}
		else if (((type->IsGenericTypeInstance()) || (type->IsComposite()) || (type->IsEnum())) && (mangleContext.mInRet))
			name += "?A";

		Mangle(mangleContext, name, typeInstance, false);
	}
	else if (type->IsGenericParam())
	{
		FindOrCreateNameSub(mangleContext, name, NameSubstitute(BfMangler::NameSubstitute::Kind_GenericParam, type));
	}
	else if (type->IsPointer())
	{
		auto pointerType = (BfPointerType*)type;

		const char* strAdd = mangleContext.mIs64Bit ? "PEA" : "PA";

		if (mangleContext.mIs64Bit)
			name += "PE";
		else
			name += "P";
		if (isConst)
			name += "B";
		else
			name += "A";
		Mangle(mangleContext, name, pointerType->mElementType);
	}
	else if (type->IsRef())
	{
		auto refType = (BfRefType*)type;
		name += "A";
		if (mangleContext.mIs64Bit)
			name += "E";
		if ((isConst) || (refType->mRefKind == BfRefType::RefKind_In))
			name += "B";
		else
			name += "A";
		if (refType->mRefKind == BfRefType::RefKind_Mut)
			name += "mut$";
		else if (refType->mRefKind == BfRefType::RefKind_Out)
			name += "out$";
		Mangle(mangleContext, name, refType->mElementType);
	}
	else if (type->IsModifiedTypeType())
	{
		auto retType = (BfModifiedTypeType*)type;
		if (retType->mModifiedKind == BfToken_RetType)
			name += "rettype$";
		else if (retType->mModifiedKind == BfToken_AllocType)
			name += "alloc$";
		else if (retType->mModifiedKind == BfToken_Nullable)
			name += "nullable$";
		else if (retType->mModifiedKind == BfToken_Params)
			name += "params$";
		else
			BF_FATAL("Unhandled");
		Mangle(mangleContext, name, retType->mElementType);
	}
	else if (type->IsConcreteInterfaceType())
	{
		auto concreteType = (BfConcreteInterfaceType*)type;
		name += "concrete$";
		Mangle(mangleContext, name, concreteType->mInterface);
	}
	else if (type->IsSizedArray())
	{
		if (type->IsUnknownSizedArrayType())
		{
			auto arrType = (BfUnknownSizedArrayType*)type;
			name += StrFormat("arr_$", arrType->mSize);
			Mangle(mangleContext, name, arrType->mElementType);
			name += "$";
			Mangle(mangleContext, name, arrType->mElementCountSource);
		}
		else
		{
			// We can't use MS mangling of "_O" because it isn't size-specific
			auto arrType = (BfSizedArrayType*)type;
			//name += StrFormat("arr_%d$", arrType->mSize);
			//Mangle(mangleContext, name, arrType->mElementType);

			name += "?$_ARRAY@";
			Mangle(mangleContext, name, arrType->mElementType);
			MangleConst(mangleContext, name, arrType->mElementCount);
			name += '@';
		}
	}
	else if (type->IsMethodRef())
	{
		auto methodRefType = (BfMethodRefType*)type;
		name += "Tmref_";

		StringT<128> mangleName;
		BfMethodInstance* methodInstance = methodRefType->mMethodRef;
		if (methodInstance == NULL)
		{
			BF_ASSERT(!methodRefType->mMangledMethodName.IsEmpty());
			mangleName = methodRefType->mMangledMethodName;
		}
		else
		{
			if (methodInstance->mIsAutocompleteMethod)
				name += "AUTOCOMPLETE";

			auto module = methodInstance->GetOwner()->mModule;
			if (module->mCompiler->mIsResolveOnly)
			{
				// There are cases where we will reprocess a method in ResolveOnly for things like
				//  GetSymbolReferences, so we will have duplicate live local methodInstances in those cases
				name += HashEncode64((uint64)methodRefType->mMethodRef);
			}
			else
			{
				Mangle(mangleName, mangleContext.mIs64Bit, methodInstance);
				methodRefType->mMangledMethodName = mangleName;
			}
		}

		if (!mangleName.IsEmpty())
		{
			Val128 val128 = Hash128(mangleName.c_str(), (int)mangleName.length());
			name += HashEncode128(val128);
		}

// 		if (methodInstance->mIsAutocompleteMethod)
// 			name += "AUTOCOMPLETE";
//
// 		String mangleAdd = Mangle(mangleContext.mIs64Bit, methodInstance);
//
// 		auto module = methodInstance->GetOwner()->mModule;
// 		if (module->mCompiler->mIsResolveOnly)
// 		{
// 			// There are cases where we will reprocess a method in ResolveOnly for things like
// 			//  GetSymbolReferences, so we will have duplicate live local methodInstances in those cases
// 			name += HashEncode64((uint64)methodRefType->mMethodRef);
// 		}
// 		else
// 		{
// 			Val128 val128 = Hash128(mangleAdd.c_str(), (int)mangleAdd.length());
// 			name += HashEncode128(val128);
// 		}
		name += "@@";
	}
	else if (type->IsConstExprValue())
	{
		BfConstExprValueType* constExprValueType = (BfConstExprValueType*)type;
		int64 val = constExprValueType->mValue.mInt64;
		if ((!constExprValueType->mType->IsPrimitiveType()) ||
			(((BfPrimitiveType*)constExprValueType->mType)->mTypeDef->mTypeCode != BfTypeCode_IntPtr))
		{
			Mangle(mangleContext, name, constExprValueType->mType);
			name += "$";
		}
		MangleConst(mangleContext, name, val);
		if (constExprValueType->mValue.mTypeCode == BfTypeCode_Let)
			name += "Undef";
	}
	else
	{
		BF_ASSERT("Unhandled");
	}

	if ((useTypeList) && (!mangleContext.mInRet) && ((int)mangleContext.mSubstituteTypeList.size() < 10))
		mangleContext.mSubstituteTypeList.push_back(type);
}

void BfMSMangler::Mangle(StringImpl& name, bool is64Bit, BfType* type, BfModule* module)
{
	if (type->IsTypeAlias())
	{
		auto typeAlias = (BfTypeAliasType*)type;
		name += "__ALIAS_";
		name += typeAlias->mTypeDef->mName->ToString();
		name += '@';
		Mangle(name, is64Bit, typeAlias->mAliasToType, module);
		return;
	}

	MangleContext mangleContext;
	mangleContext.mIs64Bit = is64Bit;
	mangleContext.mModule = module;
	auto typeInst = type->ToTypeInstance();
	if ((typeInst != NULL) && (typeInst->mModule != NULL))
		mangleContext.mModule = typeInst->mModule;
	if (typeInst != NULL)
		Mangle(mangleContext, name, typeInst, true);
	else
		Mangle(mangleContext, name, type);
	while ((name.length() > 0) && (name[name.length() - 1] == '@'))
		name.Remove(name.length() - 1);
}

void BfMSMangler::Mangle(StringImpl& name, bool is64Bit, BfMethodInstance* methodInst)
{
	static int mangleIdx = 0;
	mangleIdx++;

	int startNameLen = name.mLength;
	if ((methodInst->mMethodDef->mCLink) && (!methodInst->mMangleWithIdx))
	{
		name += methodInst->mMethodDef->mName;
		return;
	}

	auto methodDef = methodInst->mMethodDef;
	auto methodDeclaration = BfNodeDynCastExact<BfMethodDeclaration>(methodDef->mMethodDeclaration);
	auto typeInst = methodInst->GetOwner();
	auto typeDef = typeInst->mTypeDef;

	MangleContext mangleContext;
	mangleContext.mIs64Bit = is64Bit;
	mangleContext.mModule = methodInst->GetOwner()->mModule;
	if (methodInst->mCallingConvention != BfCallingConvention_Unspecified)
		mangleContext.mCCompat = true;
	bool isCMangle = false;
	HandleCustomAttributes(methodInst->GetCustomAttributes(), typeInst->mConstHolder, mangleContext.mModule, name, isCMangle, mangleContext.mCPPMangle);
	if (isCMangle)
		name += methodInst->mMethodDef->mName;
	if (name.mLength > startNameLen)
		return;

	name += '?';

	if (methodInst->GetNumGenericArguments() != 0)
	{
		name += "?$";
	}

	bool isSpecialFunc = false;
	if (methodInst->mMethodDef->mIsOperator)
	{
		String methodName;

		auto operatorDef = (BfOperatorDef*)methodInst->mMethodDef;
		if (operatorDef->mOperatorDeclaration->mIsConvOperator)
		{
			name += "?B";
		}
		else
		{
			switch (operatorDef->mOperatorDeclaration->mBinOp)
			{
			case BfBinaryOp_Add:
				name += "?H";
				break;
			case BfBinaryOp_Subtract:
				name += "?G";
				break;
			case BfBinaryOp_Multiply:
				name += "?D";
				break;
			case BfBinaryOp_OverflowAdd:
				name += "?OH";
				break;
			case BfBinaryOp_OverflowSubtract:
				name += "?OG";
				break;
			case BfBinaryOp_OverflowMultiply:
				name += "?OD";
				break;
			case BfBinaryOp_Divide:
				name += "?K";
				break;
			case BfBinaryOp_Modulus:
				name += "?L";
				break;
			case BfBinaryOp_BitwiseAnd:
				name += "?I";
				break;
			case BfBinaryOp_BitwiseOr:
				name += "?U";
				break;
			case BfBinaryOp_ExclusiveOr:
				name += "?T";
				break;
			case BfBinaryOp_LeftShift:
				name += "?6";
				break;
			case BfBinaryOp_RightShift:
				name += "?5";
				break;
			case BfBinaryOp_Equality:
				name += "?8";
				break;
			case BfBinaryOp_InEquality:
				name += "?9";
				break;
			case BfBinaryOp_GreaterThan:
				name += "?O";
				break;
			case BfBinaryOp_LessThan:
				name += "?M";
				break;
			case BfBinaryOp_GreaterThanOrEqual:
				name += "?P";
				break;
			case BfBinaryOp_LessThanOrEqual:
				name += "?N";
				break;
			case BfBinaryOp_Compare:
				name += "__cmp__";
				break;
			case BfBinaryOp_ConditionalAnd:
				name += "?V";
				break;
			case BfBinaryOp_ConditionalOr:
				name += "?W";
				break;
			case BfBinaryOp_NullCoalesce:
				methodName = "__nc__";
				break;
			case BfBinaryOp_Is:
				methodName = "__is__";
				break;
			case BfBinaryOp_As:
				methodName = "__as__";
				break;
			default: break;
			}

			switch (operatorDef->mOperatorDeclaration->mUnaryOp)
			{
			case BfUnaryOp_AddressOf:
				name += "?I";
				break;
			case BfUnaryOp_Dereference:
				name += "?D";
				break;
			case BfUnaryOp_Negate:
				name += "?G";
				break;
			case BfUnaryOp_Not:
				name += "?7";
				break;
			case BfUnaryOp_Positive:
				name += "?H";
				break;
			case BfUnaryOp_InvertBits:
				name += "?S";
				break;
			case BfUnaryOp_Increment:
				name += "?E";
				break;
			case BfUnaryOp_Decrement:
				name += "?F";
				break;
			case BfUnaryOp_PostIncrement:
				methodName = "__pi__";
				break;
			case BfUnaryOp_PostDecrement:
				methodName = "__pd__";
				break;
			case BfUnaryOp_Ref:
				methodName = "__ref__";
				break;
			case BfUnaryOp_Out:
				methodName = "__out__";
				break;
			default: break;
			}

			switch (operatorDef->mOperatorDeclaration->mAssignOp)
			{
			case BfAssignmentOp_Assign:
				methodName += "__a__";
				break;
			case BfAssignmentOp_Add:
				methodName += "__a_add__";
				break;
			case BfAssignmentOp_Subtract:
				methodName += "__a_sub__";
				break;
			case BfAssignmentOp_Multiply:
				methodName += "__a_mul__";
				break;
			case BfAssignmentOp_Divide:
				methodName += "__a_div__";
				break;
			case BfAssignmentOp_Modulus:
				methodName += "__a_mod__";
				break;
			case BfAssignmentOp_ShiftLeft:
				methodName += "__a_shl__";
				break;
			case BfAssignmentOp_ShiftRight:
				methodName += "__a_shr__";
				break;
			case BfAssignmentOp_BitwiseAnd:
				methodName += "__a_bwa__";
				break;
			case BfAssignmentOp_BitwiseOr:
				methodName += "__a_bwo__";
				break;
			case BfAssignmentOp_ExclusiveOr:
				methodName += "__a_xor__";
				break;
			default: break;
			}
		}

		if (!methodName.empty())
		{
			AddStr(mangleContext, name, methodName);
		}
	}
	/*else if ((methodDef->mMethodType == BfMethodType_Ctor) && (!methodDef->mIsStatic))
	{
		isSpecialFunc = true;
		name += "?0";
		//AddAtomStr(mangleContext, name, typeDef->mName);
	}
	else if ((methodDef->mMethodType == BfMethodType_Dtor) && (!methodDef->mIsStatic))
	{
		isSpecialFunc = true;
		name += "?1";
		//AddAtomStr(mangleContext, name, typeDef->mName);
	}*/
	else if (methodInst->GetNumGenericArguments() != 0)
	{
		AddStr(mangleContext, name, methodDef->mName);
		AddGenericArgs(mangleContext, name, methodInst->mMethodInfoEx->mMethodGenericArguments);
		name += '@';
	}
	else
	{
		if ((!mangleContext.mCPPMangle) && (!methodDef->mIsMutating) && (!methodDef->mIsStatic) && (methodInst->GetOwner()->IsValueType()))
			AddStr(mangleContext, name, methodDef->mName + "__im");
		else
			AddStr(mangleContext, name, methodDef->mName);
	}

	if (((methodInst->GetOwner()->mTypeDef->IsGlobalsContainer()) &&
		 ((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_Dtor) || (methodDef->mName == BF_METHODNAME_MARKMEMBERS_STATIC))) ||
		((methodInst->mMethodDef->mDeclaringType->mPartialIdx != -1) && (methodInst->mMethodDef->mDeclaringType->IsExtension()) &&
		 (!methodInst->mIsForeignMethodDef) && (!methodInst->mMethodDef->mIsExtern) &&
		 ((!methodInst->mMethodDef->mIsOverride) || (methodDef->mName == BF_METHODNAME_MARKMEMBERS) || (methodDef->mMethodType == BfMethodType_Dtor))))
	{
		auto declType = methodInst->mMethodDef->mDeclaringType;
		BF_ASSERT(methodInst->GetOwner()->mTypeDef->mIsCombinedPartial);
		auto declProject = declType->mProject;
		bool addProjectName = (declProject != typeInst->mTypeDef->mProject);
		bool addIndex = true;

		if (typeInst->mTypeDef->IsGlobalsContainer())
		{
			addProjectName = true;

			if ((methodInst->mCallingConvention == BfCallingConvention_Cdecl) ||
				(methodInst->mCallingConvention == BfCallingConvention_Stdcall))
			{
				addProjectName = false;
				addIndex = false;
			}
		}

		if (addProjectName)
		{
			name += declProject->mName;
			name += '$';
		}
		if (addIndex)
			name += StrFormat("%d$", declType->mPartialIdx);
	}

	if (!mangleContext.mCPPMangle)
	{
		if (methodInst->mMangleWithIdx)
			name += StrFormat("i%d$", methodInst->mMethodDef->mIdx);
		if (methodDef->mCheckedKind == BfCheckedKind_Checked)
			name += "CHK$";
		else if (methodDef->mCheckedKind == BfCheckedKind_Unchecked)
			name += "UCHK$";

		if (methodDef->mHasComptime)
			name += "COMPTIME$";
	}

	/*if ((methodInst->mMethodInstanceGroup->mOwner->mTypeDef->IsGlobalsContainer()) && (methodInst->mMethodDef->mMethodDeclaration == NULL))
	{
		name += methodInst->mMethodInstanceGroup->mOwner->mTypeDef->mProject->mName;
		name += "$";
	}*/

	if (methodInst->GetForeignType() != NULL)
		Mangle(mangleContext, name, methodInst->GetForeignType(), true);
	if (methodInst->GetExplicitInterface() != NULL)
		Mangle(mangleContext, name, methodInst->GetExplicitInterface(), true);

	///
	{
		// Only use CCompat for params, not for the owning type name - unless we're doing an explicit CPP mangle
		SetAndRestoreValue<bool> prevCCompat(mangleContext.mCCompat, (mangleContext.mCCompat && mangleContext.mCPPMangle) ? true : false);
		Mangle(mangleContext, name, methodInst->GetOwner(), true);
	}

	/*bool isCppDecl = false;
	if (!isCppDecl)
	{
		if (name.EndsWith("@"))
			name.Remove(name.length() - 1);
		FindOrCreateNameSub(mangleContext, name, NameSubstitute(BfMangler::NameSubstitute::Kind_NamespaceAtom, typeInst->mModule->mSystem->mBfAtom));
		name += '@';
	}*/

	//QEA AXXZ

	char attrib ='A';
	if (methodDef->mProtection == BfProtection_Protected)
		attrib = 'I';
	else if (methodDef->mProtection == BfProtection_Public)
		attrib = 'Q';

	bool doExplicitThis = (!methodDef->mIsStatic) && (typeInst->IsTypedPrimitive());

	if ((methodDef->mIsStatic) || (doExplicitThis))
		attrib += 2;

	if ((methodDef->mIsVirtual) && (!methodDef->mIsOverride))
		attrib += 4;

	name += attrib;

	auto bfSystem = methodInst->GetOwner()->mModule->mSystem;
	if ((!methodDef->mIsStatic) && (!doExplicitThis))
	{
		/*char cvQualifier = 'A';
		if (mangleContext.mIs64Bit)
			cvQualifier = 'E';
		name += cvQualifier;*/
		if (mangleContext.mIs64Bit)
			name += "E";

		char qualifier = 'A'; // const / volatile
		name += qualifier;
	}

	auto callingConvention = mangleContext.mModule->GetIRCallingConvention(methodInst);

	char callingConv = 'A';
	if (callingConvention == BfIRCallingConv_StdCall)
		callingConv = 'G';
	else if (callingConvention == BfIRCallingConv_ThisCall)
		callingConv = 'E';
	name += callingConv;

	if (isSpecialFunc)
		name += '@';

	//
	if (!isSpecialFunc)
	{
		bool isConst = false;
		if (methodDeclaration != NULL)
			HandleParamCustomAttributes(methodDeclaration->mAttributes, true, isConst);

		mangleContext.mInRet = true;
		Mangle(mangleContext, name, methodInst->mReturnType, false, isConst);
		mangleContext.mInRet = false;
	}
	if ((methodInst->mParams.size() == 0) && (!doExplicitThis))
	{
		name += 'X';
	}
	else
	{
		// Is this right?
		if (methodInst->GetNumGenericArguments() != 0)
			mangleContext.mSubstituteList.clear();

		mangleContext.mInArgs = true;
		if (doExplicitThis)
		{
			Mangle(mangleContext, name, typeInst->GetUnderlyingType(), true);
		}

		for (int paramIdx = 0; paramIdx < methodInst->mParams.mSize; paramIdx++)
		{
			auto& param = methodInst->mParams[paramIdx];
			bool isConst = false;
			if ((param.mParamDefIdx >= 0) && (methodDeclaration != NULL) && (param.mParamDefIdx < methodDeclaration->mParams.mSize))
			{
				auto paramDecl = methodDeclaration->mParams[param.mParamDefIdx];
				HandleParamCustomAttributes(paramDecl->mAttributes, false, isConst);
			}

			if (!mangleContext.mCPPMangle)
			{
				auto paramKind = methodInst->GetParamKind(paramIdx);
				if (paramKind == BfParamKind_Params)
					name += "Tparams@@";
				else if (paramKind == BfParamKind_DelegateParam)
					name += "Tparam@@";
			}
			Mangle(mangleContext, name, param.mResolvedType, true, isConst);
		}
		name += '@';
	}

	name += 'Z';

	bool wantLog = false;
	if (wantLog)
	{
		BfLog2("Mangling #%d : %s -> %s\n", mangleIdx, mangleContext.mModule->MethodToString(methodInst).c_str(), name.c_str());

		if (!methodInst->mIsUnspecialized)
		{
			String demangled = BfDemangler::Demangle(name, DbgLanguage_Beef);
			BfLog2(" Demangled %d: %s\n", mangleIdx, demangled.c_str());
		}
	}
}

void BfMSMangler::Mangle(StringImpl& name, bool is64Bit, BfFieldInstance* fieldInstance)
{
	auto fieldDef = fieldInstance->GetFieldDef();
	if (fieldDef->mFieldDeclaration != NULL)
	{
		auto module = fieldInstance->mOwner->mModule;
		if ((fieldInstance->mCustomAttributes != NULL) && (fieldInstance->mCustomAttributes->Contains(module->mCompiler->mCLinkAttributeTypeDef)))
		{
			name += fieldDef->mName;
			return;
		}
	}

	BF_ASSERT(fieldDef->mIsStatic);
	MangleContext mangleContext;
	mangleContext.mIs64Bit = is64Bit;
	mangleContext.mModule = fieldInstance->mOwner->mModule;

	bool isCMangle = false;
	HandleCustomAttributes(fieldInstance->mCustomAttributes, fieldInstance->mOwner->mConstHolder, mangleContext.mModule, name, isCMangle, mangleContext.mCPPMangle);
	if (isCMangle)
		name += fieldInstance->GetFieldDef()->mName;
	if (!name.IsEmpty())
		return;

	name += '?';
	AddStr(mangleContext, name, fieldDef->mName);
	Mangle(mangleContext, name, fieldInstance->mOwner, true);
	name += '2'; //TODO: Don't always mark as 'public'
	Mangle(mangleContext, name, fieldInstance->mResolvedType);
	name += ('A' + /*(fieldDef->mIsConst ? 1 : 0) +*/ (fieldDef->mIsVolatile ? 2 : 0));
}

void BfMSMangler::MangleMethodName(StringImpl& name, bool is64Bit, BfTypeInstance* type, const StringImpl& methodName)
{
	MangleContext mangleContext;
	mangleContext.mIs64Bit = is64Bit;
	mangleContext.mModule = type->GetModule();
	name += '?';
	AddStr(mangleContext, name, methodName);
	Mangle(mangleContext, name, type, true);
	name += "SAXXZ";
}

void BfMSMangler::MangleStaticFieldName(StringImpl& name, bool is64Bit, BfTypeInstance* owner, const StringImpl& fieldName, BfType* fieldType)
{
	MangleContext mangleContext;
	mangleContext.mIs64Bit = is64Bit;
	mangleContext.mModule = owner->GetModule();
	name += '?';
	AddStr(mangleContext, name, fieldName);
	Mangle(mangleContext, name, owner, true);
	//name += "@@";
	name += '2'; // public
	if (fieldType == NULL)
		name += 'H';
	else
		Mangle(mangleContext, name, fieldType);
	name += "A"; // static
}

//////////////////////////////////////////////////////////////////////////

String BfSafeMangler::Mangle(BfType* type, BfModule* module)
{
	MangleContext mangleContext;
	mangleContext.mIs64Bit = true;
	mangleContext.mModule = module;
	mangleContext.mIsSafeMangle = true;
	auto typeInst = type->ToTypeInstance();
	String name;
	if (typeInst != NULL)
		BfMSMangler::Mangle(mangleContext, name, typeInst, true);
	else
		BfMSMangler::Mangle(mangleContext, name, type);
	return name;
}

//////////////////////////////////////////////////////////////////////////

void BfMangler::Mangle(StringImpl& outStr, MangleKind mangleKind, BfType* type, BfModule* module)
{
	if (mangleKind == BfMangler::MangleKind_GNU)
		outStr += BfGNUMangler::Mangle(type, module);
	else
		BfMSMangler::Mangle(outStr, mangleKind == BfMangler::MangleKind_Microsoft_64, type, module);
}

void BfMangler::Mangle(StringImpl& outStr, MangleKind mangleKind, BfMethodInstance* methodInst)
{
	if (mangleKind == BfMangler::MangleKind_GNU)
		outStr += BfGNUMangler::Mangle(methodInst);
	else
		BfMSMangler::Mangle(outStr, mangleKind == BfMangler::MangleKind_Microsoft_64, methodInst);
}

void BfMangler::Mangle(StringImpl& outStr, MangleKind mangleKind, BfFieldInstance* fieldInstance)
{
	if (mangleKind == BfMangler::MangleKind_GNU)
	{
		outStr += BfGNUMangler::Mangle(fieldInstance);
	}
	else
		BfMSMangler::Mangle(outStr, mangleKind == BfMangler::MangleKind_Microsoft_64, fieldInstance);
}

void BfMangler::MangleMethodName(StringImpl& outStr, MangleKind mangleKind, BfTypeInstance* type, const StringImpl& methodName)
{
	if (mangleKind == BfMangler::MangleKind_GNU)
		outStr += BfGNUMangler::MangleMethodName(type, methodName);
	else
		BfMSMangler::MangleMethodName(outStr, mangleKind == BfMangler::MangleKind_Microsoft_64, type, methodName);
}

void BfMangler::MangleStaticFieldName(StringImpl& outStr, MangleKind mangleKind, BfTypeInstance* type, const StringImpl& fieldName, BfType* fieldType)
{
	if (mangleKind == BfMangler::MangleKind_GNU)
		outStr += BfGNUMangler::MangleStaticFieldName(type, fieldName);
	else
		BfMSMangler::MangleStaticFieldName(outStr, mangleKind == BfMangler::MangleKind_Microsoft_64, type, fieldName, fieldType);
}

void BfMangler::HandleCustomAttributes(BfCustomAttributes* customAttributes, BfIRConstHolder* constHolder, BfModule* module, StringImpl& name, bool& isCMangle, bool& isCPPMangle)
{
	if (customAttributes == NULL)
		return;

	auto linkNameAttr = customAttributes->Get(module->mCompiler->mLinkNameAttributeTypeDef);
	if (linkNameAttr != NULL)
	{
		if (linkNameAttr->mCtorArgs.size() == 1)
		{
			if (module->TryGetConstString(constHolder, linkNameAttr->mCtorArgs[0], name))
				if (!name.IsWhitespace())
					return;

			auto constant = constHolder->GetConstant(linkNameAttr->mCtorArgs[0]);
			if (constant != NULL)
			{
				if (constant->mInt32 == 1) // C
				{
					isCMangle = true;
				}
				else if (constant->mInt32 == 2) // CPP
				{
					isCPPMangle = true;
				}
			}
		}
	}
}

void BfMangler::HandleParamCustomAttributes(BfAttributeDirective* attributes, bool isReturn, bool& isConst)
{
	while (attributes != NULL)
	{
		if (attributes->mAttributeTypeRef != NULL)
		{
			auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString();
			if (typeRefName == "MangleConst")
				isConst = true;
		}

		attributes = attributes->mNextAttribute;
	}
}