Model: Handle generic type definitions correctly

This commit is contained in:
Katy Coe
2019-11-01 15:03:40 +01:00
parent ac47c811be
commit 05bcf98dd3
5 changed files with 94 additions and 20 deletions

View File

@@ -6,7 +6,6 @@ using System.IO;
using System.Linq;
using System.Text;
using Il2CppInspector.Reflection;
using ParameterInfo = Il2CppInspector.Reflection.ParameterInfo;
namespace Il2CppInspector
{

View File

@@ -30,6 +30,8 @@ namespace Il2CppInspector
public Il2CppFieldDefinition[] Fields => Metadata.Fields;
public Il2CppPropertyDefinition[] Properties => Metadata.Properties;
public Il2CppEventDefinition[] Events => Metadata.Events;
public Il2CppGenericContainer[] GenericContainers => Metadata.GenericContainers;
public Il2CppGenericParameter[] GenericParameters => Metadata.GenericParameters;
public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices;
public int[] NestedTypeIndices => Metadata.NestedTypeIndices;
public Dictionary<int, object> FieldDefaultValue { get; } = new Dictionary<int, object>();

View File

@@ -26,6 +26,8 @@ namespace Il2CppInspector
public Il2CppFieldDefaultValue[] FieldDefaultValues { get; }
public Il2CppPropertyDefinition[] Properties { get; }
public Il2CppEventDefinition[] Events { get; }
public Il2CppGenericContainer[] GenericContainers { get; }
public Il2CppGenericParameter[] GenericParameters { get; }
public int[] InterfaceUsageIndices { get; }
public int[] NestedTypeIndices { get; }
@@ -98,7 +100,9 @@ namespace Il2CppInspector
Events = ReadArray<Il2CppEventDefinition>(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition)));
InterfaceUsageIndices = ReadArray<int>(Header.interfacesOffset, Header.interfacesCount / sizeof(int));
NestedTypeIndices = ReadArray<int>(Header.nestedTypesOffset, Header.nestedTypesCount / sizeof(int));
// TODO: ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes
GenericContainers = ReadArray<Il2CppGenericContainer>(Header.genericContainersOffset, Header.genericContainersCount / Sizeof(typeof(Il2CppGenericContainer)));
GenericParameters = ReadArray<Il2CppGenericParameter>(Header.genericParametersOffset, Header.genericParametersCount / Sizeof(typeof(Il2CppGenericParameter)));
// TODO: ParameterDefaultValue, ParameterConstraints, MetadataUsage, CustomAttributes
// Get all string literals
Position = Header.stringOffset;

View File

@@ -301,4 +301,25 @@ namespace Il2CppInspector
[Version(Min = 19)]
public uint token;
}
public class Il2CppGenericContainer
{
/* index of the generic type definition or the generic method definition corresponding to this container */
public int ownerIndex; // either index into Il2CppClass metadata array or Il2CppMethodDefinition array
public int type_argc;
/* If true, we're a generic method, otherwise a generic type definition. */
public int is_method;
/* Our type parameters. */
public uint genericParameterStart; // GenericParameterIndex
}
public class Il2CppGenericParameter
{
public int ownerIndex; /* Type or method this parameter was defined in. */ // GenericContainerIndex
public int nameIndex; // StringIndex
public short constraintsStart; // GenericParameterConstraintIndex
public short constraintsCount;
public ushort num;
public ushort flags;
}
}

View File

@@ -21,8 +21,11 @@ namespace Il2CppInspector.Reflection {
public TypeAttributes Attributes { get; }
// Type that this type inherits from
private int baseTypeUsage;
public TypeInfo BaseType => Assembly.Model.GetTypeFromUsage(baseTypeUsage, MemberTypes.TypeInfo);
private readonly int baseTypeUsage = -1;
public TypeInfo BaseType => baseTypeUsage != -1
? Assembly.Model.GetTypeFromUsage(baseTypeUsage, MemberTypes.TypeInfo)
: Assembly.Model.TypesByDefinitionIndex.First(t => t.FullName == "System.Object");
// True if the type contains unresolved generic type parameters
public bool ContainsGenericParameters { get; }
@@ -54,10 +57,13 @@ namespace Il2CppInspector.Reflection {
public List<PropertyInfo> DeclaredProperties { get; } = new List<PropertyInfo>();
// Method that the type is declared in if this is a type parameter of a generic method
public MethodBase DeclaringMethod => throw new NotImplementedException();
public MethodBase DeclaringMethod => null; // TODO: Implement for methods
public bool IsGenericTypeParameter => IsGenericParameter && DeclaringMethod == null;
public bool IsGenericMethodParameter => IsGenericParameter && DeclaringMethod != null;
// Gets the type of the object encompassed or referred to by the current array, pointer or reference type
private int enumElementTypeUsage;
private readonly int enumElementTypeUsage;
private TypeInfo elementType;
public TypeInfo ElementType {
get {
@@ -68,7 +74,9 @@ namespace Il2CppInspector.Reflection {
}
// Type name including namespace
public string FullName => (IsPointer? "void *" : "")
public string FullName =>
IsGenericParameter? null :
(IsPointer? "void *" : "")
+ Namespace
+ (Namespace.Length > 0? "." : "")
+ (DeclaringType != null? DeclaringType.Name + "." : "")
@@ -80,9 +88,11 @@ namespace Il2CppInspector.Reflection {
public List<TypeInfo> GenericTypeParameters { get; }
public List<TypeInfo> GenericTypeArguments { get; }
public bool HasElementType => ElementType != null;
private int[] implementedInterfaceUsages;
private readonly int[] implementedInterfaceUsages;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceUsages.Select(x => Assembly.Model.GetTypeFromUsage(x, MemberTypes.TypeInfo));
public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
@@ -91,8 +101,8 @@ namespace Il2CppInspector.Reflection {
public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class;
public bool IsEnum { get; }
public bool IsGenericParameter { get; }
public bool IsGenericType => throw new NotImplementedException();
public bool IsGenericTypeDefinition => throw new NotImplementedException();
public bool IsGenericType { get; }
public bool IsGenericTypeDefinition { get; }
public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import;
public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
public bool IsNested => DeclaringType != null;
@@ -104,7 +114,7 @@ namespace Il2CppInspector.Reflection {
public bool IsNestedPublic => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic;
public bool IsNotPublic => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic;
public bool IsPointer { get; }
// Prinitive types table: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table (we exclude Object and String)
// Primitive types table: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table (we exclude Object and String)
public bool IsPrimitive => Namespace == "System" && new[] { "Boolean", "Byte", "SByte", "Int16", "UInt16", "Int32", "UInt32", "Int64", "UInt64", "IntPtr", "UIntPtr", "Char", "Decimal", "Double", "Single" }.Contains(Name);
public bool IsPublic => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public;
public bool IsSealed => (Attributes & TypeAttributes.Sealed) == TypeAttributes.Sealed;
@@ -154,6 +164,7 @@ namespace Il2CppInspector.Reflection {
Namespace = pkg.Strings[Definition.namespaceIndex];
Name = pkg.Strings[Definition.nameIndex];
// Derived type?
if (Definition.parentIndex >= 0)
baseTypeUsage = Definition.parentIndex;
@@ -163,6 +174,22 @@ namespace Il2CppInspector.Reflection {
MemberType |= MemberTypes.NestedType;
}
// Generic type?
if (Definition.genericContainerIndex >= 0) {
IsGenericType = true;
IsGenericParameter = false;
IsGenericTypeDefinition = true; // TODO: Only if all of the parameters are unresolved generic type parameters
ContainsGenericParameters = true;
// Store the generic type parameters for later instantiation
var gc = pkg.GenericContainers[Definition.genericContainerIndex];
GenericTypeParameters = pkg.GenericParameters.Skip((int) gc.genericParameterStart).Take(gc.type_argc).Select(p => new TypeInfo(this, p)).ToList();
// TODO: Constraints
// TODO: Attributes
}
// Add to global type definition list
Assembly.Model.TypesByDefinitionIndex[Index] = this;
@@ -251,6 +278,7 @@ namespace Il2CppInspector.Reflection {
Namespace = genericTypeDef.Namespace;
Name = genericTypeDef.Name;
Attributes |= TypeAttributes.Class;
// TODO: Generic* properties and ContainsGenericParameters
@@ -258,16 +286,17 @@ namespace Il2CppInspector.Reflection {
var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst);
// Get list of pointers to type parameters (both unresolved and concrete)
var genericTypeParameters = image.ReadMappedWordArray(genericInstance.type_argv, (int)genericInstance.type_argc);
var genericTypeArguments = image.ReadMappedWordArray(genericInstance.type_argv, (int)genericInstance.type_argc);
GenericTypeParameters = new List<TypeInfo>();
foreach (var pArg in genericTypeParameters) {
GenericTypeArguments = new List<TypeInfo>();
foreach (var pArg in genericTypeArguments) {
var argType = model.GetTypeFromVirtualAddress((ulong) pArg);
// TODO: Detect whether unresolved or concrete (add concrete to GenericTypeArguments instead)
// TODO: GenericParameterPosition etc. in types we generate here
GenericTypeParameters.Add(argType); // TODO: Fix MemberType here
// TODO: Assembly
GenericTypeArguments.Add(argType); // TODO: Fix MemberType here
}
Attributes |= TypeAttributes.Class;
}
// Array with known dimensions and bounds
@@ -292,17 +321,36 @@ namespace Il2CppInspector.Reflection {
// Unresolved generic type variable
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_VAR || pType.type == Il2CppTypeEnum.IL2CPP_TYPE_MVAR) {
ContainsGenericParameters = true;
Attributes |= TypeAttributes.Class;
IsGenericParameter = true;
Name = "T"; // TODO: Don't hardcode parameter name
// TODO: GenericTypeParameters?
// TODO: Add to GenericTypeParameters? ContainsGenericParameters?
}
// Pointer type
// TODO: Should set ElementType etc.
IsPointer = (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_PTR);
}
// Initialize a type that is a generic parameter
// 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
Attributes = declaringType.Attributes;
// Same namespace as delcaring type
Namespace = declaringType.Namespace;
// Base type of object
// TODO: This may change under constraints
// Name of parameter
Name = declaringType.Assembly.Model.Package.Strings[param.nameIndex];
IsGenericParameter = true;
IsGenericType = false;
IsGenericTypeDefinition = false;
ContainsGenericParameters = true;
}
}
}