Add name mangling to Methods/MethodInfo/TypeInfo/TypeRef, remove Boxing from ValueTypes when used as the this parameter, fix crashes when a module has no attributes
This commit is contained in:
204
Il2CppInspector.Common/Cpp/MangledNameBuilder.cs
Normal file
204
Il2CppInspector.Common/Cpp/MangledNameBuilder.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Il2CppInspector.Reflection;
|
||||
|
||||
namespace Il2CppInspector.Cpp;
|
||||
|
||||
// This follows Itanium/GCC mangling specifications.
|
||||
public class MangledNameBuilder
|
||||
{
|
||||
private readonly StringBuilder _sb = new("_Z");
|
||||
|
||||
public override string ToString()
|
||||
=> _sb.ToString();
|
||||
|
||||
public static string Method(MethodBase method)
|
||||
{
|
||||
var builder = new MangledNameBuilder();
|
||||
builder.BuildMethod(method);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static string MethodInfo(MethodBase method)
|
||||
{
|
||||
var builder = new MangledNameBuilder();
|
||||
builder.BuildMethod(method, "MethodInfo");
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static string TypeInfo(TypeInfo type)
|
||||
{
|
||||
var builder = new MangledNameBuilder();
|
||||
builder.BeginName();
|
||||
builder.WriteIdentifier("TypeInfo");
|
||||
builder.WriteTypeName(type);
|
||||
builder.WriteEnd();
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static string TypeRef(TypeInfo type)
|
||||
{
|
||||
var builder = new MangledNameBuilder();
|
||||
builder.BeginName();
|
||||
builder.WriteIdentifier("TypeRef");
|
||||
builder.WriteTypeName(type);
|
||||
builder.WriteEnd();
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void BuildMethod(MethodBase method, string prefix = "")
|
||||
{
|
||||
/*
|
||||
* We do not have any CV-qualifiers nor ref-qualifiers,
|
||||
* so we immediately write the nested name.
|
||||
*/
|
||||
|
||||
BeginName();
|
||||
|
||||
if (prefix.Length > 0)
|
||||
WriteIdentifier(prefix);
|
||||
|
||||
WriteTypeName(method.DeclaringType);
|
||||
|
||||
switch (method.Name)
|
||||
{
|
||||
case ".ctor":
|
||||
_sb.Append("C1"); // Constructor
|
||||
break;
|
||||
case ".cctor":
|
||||
WriteIdentifier("cctor");
|
||||
break;
|
||||
default:
|
||||
WriteIdentifier(method.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
var genericParams = method.GetGenericArguments();
|
||||
|
||||
WriteGenericParams(genericParams);
|
||||
|
||||
WriteEnd(); // End nested name
|
||||
|
||||
// Now write the method parameters
|
||||
|
||||
if (genericParams.Length > 0 && method is MethodInfo mInfo)
|
||||
{
|
||||
// If this is a generic method, the first parameter needs to be the return type
|
||||
WriteType(mInfo.ReturnType);
|
||||
}
|
||||
|
||||
if (method.DeclaredParameters.Count == 0)
|
||||
_sb.Append('v');
|
||||
else
|
||||
{
|
||||
foreach (var param in method.DeclaredParameters)
|
||||
WriteType(param.ParameterType);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteTypeName(TypeInfo type)
|
||||
{
|
||||
if (type.HasElementType)
|
||||
type = type.ElementType;
|
||||
|
||||
WriteName(type.Namespace);
|
||||
|
||||
if (type.DeclaringType != null)
|
||||
WriteIdentifier(type.DeclaringType.Name);
|
||||
|
||||
WriteIdentifier(type.CSharpBaseName);
|
||||
WriteGenericParams(type.GenericTypeArguments);
|
||||
}
|
||||
|
||||
private void WriteType(TypeInfo type)
|
||||
{
|
||||
if (type.FullName == "System.Void")
|
||||
{
|
||||
_sb.Append('v');
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.IsByRef)
|
||||
_sb.Append('R');
|
||||
|
||||
if (type.IsPointer)
|
||||
_sb.Append('P');
|
||||
|
||||
if (type.IsArray)
|
||||
_sb.Append("A_");
|
||||
|
||||
if (type.IsPrimitive && type.Name != "Decimal")
|
||||
{
|
||||
if (type.Name is "IntPtr" or "UIntPtr")
|
||||
_sb.Append("Pv"); // void*
|
||||
else
|
||||
{
|
||||
_sb.Append(type.Name switch
|
||||
{
|
||||
"Boolean" => 'b',
|
||||
"Byte" => 'h',
|
||||
"SByte" => 'a',
|
||||
"Int16" => 's',
|
||||
"UInt16" => 't',
|
||||
"Int32" => 'i',
|
||||
"UInt32" => 'j',
|
||||
"Int64" => 'l',
|
||||
"UInt64" => 'm',
|
||||
"Char" => 'w',
|
||||
"Single" => 'f',
|
||||
"Double" => 'd',
|
||||
_ => throw new UnreachableException()
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginName();
|
||||
WriteTypeName(type);
|
||||
WriteEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteGenericParams(TypeInfo[] generics)
|
||||
{
|
||||
if (generics.Length > 0)
|
||||
{
|
||||
BeginGenerics();
|
||||
|
||||
foreach (var arg in generics)
|
||||
WriteType(arg);
|
||||
|
||||
WriteEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteIdentifier(string identifier)
|
||||
{
|
||||
_sb.Append(identifier.Length);
|
||||
_sb.Append(identifier);
|
||||
}
|
||||
|
||||
private void WriteName(string name)
|
||||
{
|
||||
foreach (var part in name.Split("."))
|
||||
{
|
||||
if (part.Length > 0)
|
||||
WriteIdentifier(part);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginName()
|
||||
{
|
||||
_sb.Append('N');
|
||||
}
|
||||
|
||||
private void BeginGenerics()
|
||||
{
|
||||
_sb.Append('I');
|
||||
}
|
||||
|
||||
private void WriteEnd()
|
||||
{
|
||||
_sb.Append('E');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user