From fdab846710e5e510d79ddaa84791bfef82094ab2 Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Sat, 2 Nov 2019 01:44:09 +0100 Subject: [PATCH] Model: Handle generic methods and set DeclaringMethod --- Il2CppInspector/Il2CppModel.cs | 4 +++ Il2CppInspector/Reflection/MethodBase.cs | 32 +++++++++++++++--- Il2CppInspector/Reflection/MethodInfo.cs | 1 + Il2CppInspector/Reflection/TypeInfo.cs | 41 ++++++++++++++++-------- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/Il2CppInspector/Il2CppModel.cs b/Il2CppInspector/Il2CppModel.cs index 6075031..ec4e2c9 100644 --- a/Il2CppInspector/Il2CppModel.cs +++ b/Il2CppInspector/Il2CppModel.cs @@ -24,12 +24,16 @@ namespace Il2CppInspector.Reflection // List of type usages that are initialized via pointers in the image public Dictionary TypesByVirtualAddress { get; } = new Dictionary(); + // List of all methods ordered by their MethodDefinitionIndex + public MethodBase[] MethodsByDefinitionIndex { get; } + // List of all types public Il2CppModel(Il2CppInspector package) { Package = package; TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; TypesByUsageIndex = new TypeInfo[package.TypeUsages.Count]; + MethodsByDefinitionIndex = new MethodBase[package.Methods.Length]; // Create Assembly objects from Il2Cpp package for (var image = 0; image < package.Images.Length; image++) diff --git a/Il2CppInspector/Reflection/MethodBase.cs b/Il2CppInspector/Reflection/MethodBase.cs index 8a66a9e..96bacb2 100644 --- a/Il2CppInspector/Reflection/MethodBase.cs +++ b/Il2CppInspector/Reflection/MethodBase.cs @@ -22,11 +22,12 @@ namespace Il2CppInspector.Reflection // Information/flags about the method public MethodAttributes Attributes { get; protected set; } - // True if the type contains unresolved generic type parameters - public bool ContainsGenericParameters => throw new NotImplementedException(); + // True if the method contains unresolved generic type parameters + public bool ContainsGenericParameters { get; } // TODO: Custom attribute stuff + public List GenericTypeParameters { get; } // System.Reflection.MethodInfo.GetGenericArguments() public List DeclaredParameters { get; } = new List(); public bool IsAbstract => (Attributes & MethodAttributes.Abstract) == MethodAttributes.Abstract; @@ -36,8 +37,8 @@ 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 => throw new NotImplementedException(); - public bool IsGenericMethodDefinition => throw new NotImplementedException(); + 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; @@ -59,6 +60,24 @@ namespace Il2CppInspector.Reflection // Find method pointer VirtualAddress = pkg.GetMethodPointer(Assembly.Module, Definition); + // Add to global method definition list + Assembly.Model.MethodsByDefinitionIndex[Index] = this; + + // Generic method definition? + if (Definition.genericContainerIndex >= 0) { + IsGenericMethod = true; + IsGenericMethodDefinition = true; // TODO: Only if all of the parameters are unresolved generic type parameters + 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(); + + // TODO: Constraints + // TODO: Attributes + } + // Set method attributes if ((Definition.flags & Il2CppConstants.METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == Il2CppConstants.METHOD_ATTRIBUTE_PRIVATE) Attributes |= MethodAttributes.Private; @@ -90,7 +109,7 @@ namespace Il2CppInspector.Reflection Attributes |= MethodAttributes.SpecialName; if ((Definition.flags & Il2CppConstants.METHOD_ATTRIBUTE_UNMANAGED_EXPORT) != 0) Attributes |= MethodAttributes.UnmanagedExport; - + // Add arguments for (var p = Definition.parameterStart; p < Definition.parameterStart + Definition.parameterCount; p++) DeclaredParameters.Add(new ParameterInfo(pkg, p, this)); @@ -142,6 +161,9 @@ namespace Il2CppInspector.Reflection public string GetParametersString() => string.Join(", ", DeclaredParameters.Select(p => $"{p.GetModifierString()}{p.ParameterType.CSharpName} {p.Name}")); + public string GetTypeParametersString() => GenericTypeParameters == null? "" : + "<" + string.Join(", ", GenericTypeParameters.Select(p => p.CSharpName)) + ">"; + // List of operator overload metadata names // https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads public static Dictionary OperatorMethodNames = new Dictionary { diff --git a/Il2CppInspector/Reflection/MethodInfo.cs b/Il2CppInspector/Reflection/MethodInfo.cs index d5c7fde..35b062d 100644 --- a/Il2CppInspector/Reflection/MethodInfo.cs +++ b/Il2CppInspector/Reflection/MethodInfo.cs @@ -28,6 +28,7 @@ namespace Il2CppInspector.Reflection ReturnParameter = new ParameterInfo(pkg, -1, this); } + // TODO: Generic arguments (and on ConstructorInfo) public override string ToString() => ReturnType.Name + " " + Name + "(" + string.Join(", ", DeclaredParameters.Select(x => x.ParameterType.Name)) + ")"; } } \ No newline at end of file diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index d90ca3d..0a45ce5 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -30,6 +30,11 @@ namespace Il2CppInspector.Reflection { // True if the type contains unresolved generic type parameters public bool ContainsGenericParameters { get; } + public string BaseName => base.Name; + + // Get rid of generic backticks + public string UnmangledBaseName => base.Name.IndexOf("`", StringComparison.Ordinal) == -1 ? base.Name : base.Name.Remove(base.Name.IndexOf("`", StringComparison.Ordinal)); + // C# colloquial name of the type (if available) public string CSharpName { get { @@ -46,6 +51,14 @@ namespace Il2CppInspector.Reflection { } } + // C# name as it would be written in a type declaration + public string CSharpTypeDeclarationName => + (IsPointer ? "void *" : "") + + (base.Name.IndexOf("`", StringComparison.Ordinal) == -1 ? base.Name : base.Name.Remove(base.Name.IndexOf("`", StringComparison.Ordinal))) + + (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "") + + (GenericTypeArguments != null ? "<" + string.Join(", ", GenericTypeArguments.Select(x => x.Name)) + ">" : "") + + (IsArray ? "[]" : ""); + public List DeclaredConstructors { get; } = new List(); public List DeclaredEvents { get; } = new List(); public List DeclaredFields { get; } = new List(); @@ -58,7 +71,9 @@ namespace Il2CppInspector.Reflection { public List DeclaredProperties { get; } = new List(); // Method that the type is declared in if this is a type parameter of a generic method - public MethodBase DeclaringMethod => null; // TODO: Implement for methods + public MethodBase DeclaringMethod; + + // IsGenericTypeParameter and IsGenericMethodParameter from https://github.com/dotnet/corefx/issues/23883 public bool IsGenericTypeParameter => IsGenericParameter && DeclaringMethod == null; public bool IsGenericMethodParameter => IsGenericParameter && DeclaringMethod != null; @@ -127,17 +142,6 @@ namespace Il2CppInspector.Reflection { // May get overridden by Il2CppType-based constructor below public override MemberTypes MemberType { get; } = MemberTypes.TypeInfo; - public string BaseName => base.Name; - - public override string Name { - get => (IsPointer ? "void *" : "") - + (base.Name.IndexOf("`", StringComparison.Ordinal) == -1? base.Name : base.Name.Remove(base.Name.IndexOf("`", StringComparison.Ordinal))) - + (GenericTypeParameters != null? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "") - + (GenericTypeArguments != null ? "<" + string.Join(", ", GenericTypeArguments.Select(x => x.Name)) + ">" : "") - + (IsArray ? "[]" : ""); - protected set => base.Name = value; - } - private string @namespace; public string Namespace { get => !string.IsNullOrEmpty(@namespace) ? @namespace : DeclaringType?.Namespace ?? ""; @@ -179,7 +183,7 @@ namespace Il2CppInspector.Reflection { MemberType |= MemberTypes.NestedType; } - // Generic type? + // Generic type definition? if (Definition.genericContainerIndex >= 0) { IsGenericType = true; IsGenericParameter = false; @@ -358,6 +362,10 @@ namespace Il2CppInspector.Reflection { declaringTypeDefinitionIndex = ownerType.Index; MemberType = memberType | MemberTypes.NestedType; + // All generic method type parameters have a declared method + if (container.is_method == 1) + DeclaringMethod = model.MethodsByDefinitionIndex[container.ownerIndex]; + IsGenericParameter = true; ContainsGenericParameters = true; IsGenericType = false; @@ -369,7 +377,7 @@ namespace Il2CppInspector.Reflection { IsPointer = (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_PTR); } - // Initialize a type that is a generic parameter + // Initialize a type that is a generic parameter of a generic type // See: https://docs.microsoft.com/en-us/dotnet/api/system.type.isgenerictype?view=netframework-4.8 public TypeInfo(TypeInfo declaringType, Il2CppGenericParameter param) : base(declaringType) { // Same visibility attributes as declaring type @@ -389,5 +397,10 @@ namespace Il2CppInspector.Reflection { IsGenericTypeDefinition = false; ContainsGenericParameters = true; } + + // Initialize a type that is a generic parameter of a generic method + public TypeInfo(MethodBase declaringMethod, Il2CppGenericParameter param) : this(declaringMethod.DeclaringType, param) { + DeclaringMethod = declaringMethod; + } } } \ No newline at end of file