Output: Implement extension methods

This commit is contained in:
Katy Coe
2019-11-11 03:01:29 +01:00
parent a026acf528
commit 13ceaaf47a
2 changed files with 48 additions and 21 deletions

View File

@@ -25,6 +25,8 @@ namespace Il2CppInspector
private const string CGAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
private const string FBAttribute = "System.Runtime.CompilerServices.FixedBufferAttribute";
private const string ExtAttribute = "System.Runtime.CompilerServices.ExtensionAttribute";
private const string DMAttribute = "System.Reflection.DefaultMemberAttribute";
public Il2CppCSharpDumper(Il2CppModel model) => this.model = model;
@@ -36,7 +38,7 @@ namespace Il2CppInspector
writer.Write($"// Image {asm.Index}: {asm.FullName} - {asm.ImageDefinition.typeStart}\n");
// Assembly-level attributes
writer.Write(asm.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(attributePrefix: "assembly: "));
writer.Write(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name).ToString(attributePrefix: "assembly: "));
if (asm.CustomAttributes.Any())
writer.Write("\n");
}
@@ -75,7 +77,8 @@ namespace Il2CppInspector
// Custom attributes
// TODO: DefaultMemberAttribute should be output if it is present and the type does not have an indexer, otherwise suppressed
// See https://docs.microsoft.com/en-us/dotnet/api/system.reflection.defaultmemberattribute?view=netframework-4.8
writer.Write(type.CustomAttributes.Where(a => a.AttributeType.Name != "DefaultMemberAttribute").OrderBy(a => a.AttributeType.Name).ToString(prefix));
writer.Write(type.CustomAttributes.Where(a => a.AttributeType.FullName != DMAttribute && a.AttributeType.FullName != ExtAttribute)
.OrderBy(a => a.AttributeType.Name).ToString(prefix));
// Roll-up multicast delegates to use the 'delegate' syntactic sugar
if (type.IsClass && type.IsSealed && type.BaseType?.FullName == "System.MulticastDelegate") {
@@ -230,28 +233,50 @@ namespace Il2CppInspector
writer.Write("\n");
// Methods
if (type.DeclaredMethods.Except(usedMethods).Any())
// Don't re-output methods for constructors, properties, events etc.
var methods = type.DeclaredMethods.Except(usedMethods).Where(m => m.CustomAttributes.All(a => a.AttributeType.FullName != ExtAttribute));
if (methods.Any()) {
writer.Write(prefix + "\t// Methods\n");
// Don't re-output methods for constructors, properties, events etc.
foreach (var method in type.DeclaredMethods.Except(usedMethods)) {
if (SuppressGenerated && method.GetCustomAttributes(CGAttribute).Any())
continue;
// Attributes
writer.Write(method.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t"));
// IL2CPP doesn't seem to retain return type attributes
//writer.Write(method.ReturnType.CustomAttributes.ToString(prefix + "\t", "return: "));
writer.Write($"{prefix}\t{method.GetModifierString()}");
if (method.Name != "op_Implicit" && method.Name != "op_Explicit")
writer.Write($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}");
else
writer.Write($"{method.CSharpName}{method.ReturnType.CSharpName}");
writer.Write("(" + method.GetParametersString());
writer.Write(");" + (method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
foreach (var method in methods) {
writer.Write(generateMethod(method, prefix));
usedMethods.Add(method);
}
writer.Write("\n");
}
// Extension methods
methods = type.DeclaredMethods.Except(usedMethods);
if (methods.Any())
writer.Write(prefix + "\t// Extension methods\n");
foreach (var method in type.DeclaredMethods.Except(usedMethods)) {
writer.Write(generateMethod(method, prefix));
}
writer.Write(prefix + "}\n");
}
private string generateMethod(MethodInfo method, string prefix) {
if (SuppressGenerated && method.GetCustomAttributes(CGAttribute).Any())
return string.Empty;
var writer = new StringBuilder();
// Attributes
writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t"));
// IL2CPP doesn't seem to retain return type attributes
//writer.Append(method.ReturnType.CustomAttributes.ToString(prefix + "\t", "return: "));
writer.Append($"{prefix}\t{method.GetModifierString()}");
if (method.Name != "op_Implicit" && method.Name != "op_Explicit")
writer.Append($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}");
else
writer.Append($"{method.CSharpName}{method.ReturnType.CSharpName}");
writer.Append("(" + method.GetParametersString());
writer.Append(");" + (method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
return writer.ToString();
}
}
}

View File

@@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Il2CppInspector.Reflection
@@ -96,7 +97,8 @@ namespace Il2CppInspector.Reflection
public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}";
public string GetParameterString() => IsRetval? null :
$"{CustomAttributes.ToString(inline: true).Replace("[ParamArray]", "params")}"
(Position == 0 && Member.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"")
+ $"{CustomAttributes.ToString(inline: true).Replace("[ParamArray]", "params")}"
+ $"{getCSharpSignatureString()} {Name}"
+ (HasDefaultValue ? " = " + DefaultValue.ToCSharpValue() : "");