diff --git a/Il2CppDumper/Il2CppCSharpDumper.cs b/Il2CppDumper/Il2CppCSharpDumper.cs index 82f38ef..064397d 100644 --- a/Il2CppDumper/Il2CppCSharpDumper.cs +++ b/Il2CppDumper/Il2CppCSharpDumper.cs @@ -607,12 +607,11 @@ namespace Il2CppInspector writer.Append("(" + method.GetParametersString(scope, !SuppressMetadata) + ")"); // Generic type constraints - if (method.GenericTypeParameters != null) - foreach (var gp in method.GenericTypeParameters) { - var constraint = gp.GetTypeConstraintsString(scope); - if (constraint != string.Empty) - writer.Append($"\n{prefix}\t\t{constraint}"); - } + foreach (var gp in method.GetGenericArguments()) { + var constraint = gp.GetTypeConstraintsString(scope); + if (constraint != string.Empty) + writer.Append($"\n{prefix}\t\t{constraint}"); + } // Body var methodBody = method switch { diff --git a/Il2CppInspector/Reflection/MethodBase.cs b/Il2CppInspector/Reflection/MethodBase.cs index 11aa404..4338657 100644 --- a/Il2CppInspector/Reflection/MethodBase.cs +++ b/Il2CppInspector/Reflection/MethodBase.cs @@ -1,5 +1,5 @@ /* - Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + Copyright 2017-2020 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com All rights reserved. */ @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; -using System.Text.RegularExpressions; namespace Il2CppInspector.Reflection { @@ -23,13 +22,9 @@ namespace Il2CppInspector.Reflection // Information/flags about the method public MethodAttributes Attributes { get; protected set; } - // True if the method contains unresolved generic type parameters - public bool ContainsGenericParameters { get; } - // Custom attributes for this member public override IEnumerable CustomAttributes => CustomAttributeData.GetCustomAttributes(this); - - public List GenericTypeParameters { get; } // System.Reflection.MethodInfo.GetGenericArguments() + public List DeclaredParameters { get; } = new List(); public bool IsAbstract => (Attributes & MethodAttributes.Abstract) == MethodAttributes.Abstract; @@ -39,8 +34,6 @@ namespace Il2CppInspector.Reflection public bool IsFamilyAndAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem; public bool IsFamilyOrAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; public bool IsFinal => (Attributes & MethodAttributes.Final) == MethodAttributes.Final; - public bool IsGenericMethod { get; } - public bool IsGenericMethodDefinition { get; } public bool IsHideBySig => (Attributes & MethodAttributes.HideBySig) == MethodAttributes.HideBySig; public bool IsPrivate => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; public bool IsPublic => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; @@ -50,6 +43,25 @@ namespace Il2CppInspector.Reflection public virtual bool RequiresUnsafeContext => DeclaredParameters.Any(p => p.ParameterType.RequiresUnsafeContext); + // True if the method contains unresolved generic type parameters, or if it is a non-generic method in an open ganeric type + // See: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.containsgenericparameters?view=netframework-4.8 + public bool ContainsGenericParameters => DeclaringType.ContainsGenericParameters || genericArguments.Any(ga => ga.ContainsGenericParameters); + + // For a generic method definition: the list of generic type parameters + // For an open generic method: a mix of generic type parameters and generic type arguments + // For a closed generic method: the list of generic type arguments + private readonly List genericArguments = new List(); + + // See: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getgenericarguments?view=netframework-4.8 + public List GetGenericArguments() => genericArguments; + + // This was added in .NET Core 2.1 and isn't properly documented yet + public bool IsConstructedGenericMethod => genericArguments.All(ga => !ga.ContainsGenericParameters); + + // See: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.isgenericmethod?view=netframework-4.8 + public bool IsGenericMethod { get; } + public bool IsGenericMethodDefinition => genericArguments.Any() && genericArguments.All(a => a.IsGenericTypeParameter); + // TODO: GetMethodBody() public string CSharpName => @@ -85,13 +97,11 @@ namespace Il2CppInspector.Reflection // Generic method definition? if (Definition.genericContainerIndex >= 0) { IsGenericMethod = true; - IsGenericMethodDefinition = true; - ContainsGenericParameters = true; // Store the generic type parameters for later instantiation var container = pkg.GenericContainers[Definition.genericContainerIndex]; - GenericTypeParameters = pkg.GenericParameters.Skip((int)container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToList(); + genericArguments = pkg.GenericParameters.Skip((int)container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToList(); } // Set method attributes @@ -191,11 +201,11 @@ namespace Il2CppInspector.Reflection public string GetParametersString(Scope usingScope, bool emitPointer = false, bool commentAttributes = false) => string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString(usingScope, emitPointer, commentAttributes))); - public string GetTypeParametersString(Scope usingScope) => GenericTypeParameters == null? "" : - "<" + string.Join(", ", GenericTypeParameters.Select(p => p.GetScopedCSharpName(usingScope))) + ">"; + public string GetTypeParametersString(Scope usingScope) => !GetGenericArguments().Any()? "" : + "<" + string.Join(", ", GetGenericArguments().Select(p => p.GetScopedCSharpName(usingScope))) + ">"; - public string GetFullTypeParametersString() => GenericTypeParameters == null? "" : - "[" + string.Join(",", GenericTypeParameters.Select(p => p.FullName ?? p.Name)) + "]"; + public string GetFullTypeParametersString() => !GetGenericArguments().Any()? "" : + "[" + string.Join(",", GetGenericArguments().Select(p => p.FullName ?? p.Name)) + "]"; public abstract string GetSignatureString(); diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index 8168b74..f8f7ba7 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -773,8 +773,8 @@ namespace Il2CppInspector.Reflection { refs.UnionWith(DeclaredMethods.SelectMany(m => m.DeclaredParameters).Select(p => p.ParameterType)); // Method generic type parameters and constraints - refs.UnionWith(DeclaredMethods.SelectMany(m => m.GenericTypeParameters ?? new List())); - refs.UnionWith(DeclaredMethods.SelectMany(m => m.GenericTypeParameters ?? new List()) + refs.UnionWith(DeclaredMethods.SelectMany(m => m.GetGenericArguments())); + refs.UnionWith(DeclaredMethods.SelectMany(m => m.GetGenericArguments()) .SelectMany(p => p.GetGenericParameterConstraints())); // Type declaration attributes @@ -898,7 +898,7 @@ namespace Il2CppInspector.Reflection { // Find nearest ancestor base method which has us as a generic type parameter var sig = DeclaringMethod.GetSignatureString(); var method = DeclaringMethod.DeclaringType.BaseType.GetAllMethods() - .FirstOrDefault(m => m.IsHideBySig && m.IsVirtual && m.GetSignatureString() == sig && (m.GenericTypeParameters?.Any(p => p.Name == Name) ?? false)); + .FirstOrDefault(m => m.IsHideBySig && m.IsVirtual && m.GetSignatureString() == sig && m.GetGenericArguments().Any(p => p.Name == Name)); // Stop if we are inherited from a base method if (method != null)