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:
LukeFZ
2023-12-06 20:09:35 +01:00
parent e9434f4cad
commit 5b1d9c67d1
11 changed files with 335 additions and 119 deletions

View 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');
}
}