Model: Re-implement the handling of Il2CppType

This commit is contained in:
Katy Coe
2019-10-31 17:40:33 +01:00
parent edbf9dc2fa
commit 3560661f9c
8 changed files with 85 additions and 46 deletions

View File

@@ -16,25 +16,32 @@ namespace Il2CppInspector.Reflection
public List<Assembly> Assemblies { get; } = new List<Assembly>();
// List of all types ordered by their TypeDefinitionIndex
public TypeInfo[] TypesByIndex { get; }
public TypeInfo[] TypesByDefinitionIndex { get; }
// List of all type usages ordered by their type usage index
public TypeInfo[] TypesByUsageIndex { get; }
// List of type usages that are initialized via pointers in the image
public Dictionary<ulong, TypeInfo> TypesByVirtualAddress { get; } = new Dictionary<ulong, TypeInfo>();
// List of all types
public Il2CppModel(Il2CppInspector package) {
Package = package;
TypesByIndex = new TypeInfo[package.TypeDefinitions.Length];
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
TypesByUsageIndex = new TypeInfo[package.TypeUsages.Count];
// Create Assembly objects from Il2Cpp package
for (var image = 0; image < package.Images.Length; image++)
Assemblies.Add(new Assembly(this, image));
}
// Get or generate a type from its IL2CPP binary type usage reference
// (field, return type, generic type parameter etc.)
public TypeInfo GetType(Il2CppType pType, MemberTypes memberType = MemberTypes.All) {
switch (pType.type) {
private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) {
switch (usage.type) {
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
// Classes defined in the metadata
return TypesByIndex[(int) pType.datapoint];
return TypesByDefinitionIndex[usage.datapoint];
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
@@ -43,20 +50,49 @@ namespace Il2CppInspector.Reflection
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
// Everything that requires special handling
return new TypeInfo(this, pType, memberType);
return new TypeInfo(this, usage, memberType);
default:
return GetTypeFromEnum(pType.type);
// Primitive types
return GetTypeFromTypeEnum(usage.type);
}
}
// Get or generate a type from its IL2CPP binary type usage reference
// (field, return type, generic type parameter etc.)
public TypeInfo GetTypeFromUsage(int typeUsageIndex, MemberTypes memberType = MemberTypes.All) {
// Already generated type previously?
if (TypesByUsageIndex[typeUsageIndex] != null)
return TypesByUsageIndex[typeUsageIndex];
var usage = Package.TypeUsages[typeUsageIndex];
var newUsage = getNewTypeUsage(usage, memberType);
TypesByUsageIndex[typeUsageIndex] = newUsage;
return newUsage;
}
// Basic primitive types
public TypeInfo GetTypeFromEnum(Il2CppTypeEnum t) {
public TypeInfo GetTypeFromTypeEnum(Il2CppTypeEnum t) {
if ((int)t >= Il2CppConstants.FullNameTypeString.Count)
return null;
var fqn = Il2CppConstants.FullNameTypeString[(int) t];
return TypesByIndex.First(x => x.FullName == fqn);
return TypesByDefinitionIndex.First(x => x.FullName == fqn);
}
// Type from a virtual address pointer
// These are always nested types frorm usages within another type
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
if (TypesByVirtualAddress.ContainsKey(ptr))
return TypesByVirtualAddress[ptr];
var type = Package.BinaryImage.ReadMappedObject<Il2CppType>(ptr);
var newUsage = getNewTypeUsage(type, MemberTypes.NestedType);
TypesByVirtualAddress.Add(ptr, newUsage);
return newUsage;
}
}
}

View File

@@ -16,7 +16,6 @@ namespace Il2CppInspector.Reflection
public static readonly string TypeConstructorName = ".cctor";
// TODO
public override MemberTypes MemberType => MemberTypes.Constructor;
public ConstructorInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) { }

View File

@@ -24,8 +24,8 @@ namespace Il2CppInspector.Reflection
public MethodInfo RaiseMethod { get; }
// Event handler delegate type
private Il2CppType eventType;
public TypeInfo EventHandlerType => Assembly.Model.GetType(eventType, MemberTypes.TypeInfo);
private int eventTypeUsage;
public TypeInfo EventHandlerType => Assembly.Model.GetTypeFromUsage(eventTypeUsage, MemberTypes.TypeInfo);
// True if the event has a special name
public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) == EventAttributes.SpecialName;
@@ -38,7 +38,9 @@ namespace Il2CppInspector.Reflection
Index = eventIndex;
Name = pkg.Strings[Definition.nameIndex];
eventType = pkg.TypeUsages[Definition.typeIndex];
eventTypeUsage = Definition.typeIndex;
var eventType = pkg.TypeUsages[eventTypeUsage];
if ((eventType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME) == Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME)
Attributes |= EventAttributes.SpecialName;

View File

@@ -37,8 +37,8 @@ namespace Il2CppInspector.Reflection {
public FieldAttributes Attributes { get; }
// Type of field
private readonly Il2CppType fieldType;
public TypeInfo FieldType => Assembly.Model.GetType(fieldType, MemberTypes.Field);
private readonly int fieldTypeUsage;
public TypeInfo FieldType => Assembly.Model.GetTypeFromUsage(fieldTypeUsage, MemberTypes.Field);
// For the Is* definitions below, see:
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.fieldinfo.isfamilyandassembly?view=netframework-4.7.1#System_Reflection_FieldInfo_IsFamilyAndAssembly
@@ -88,7 +88,9 @@ namespace Il2CppInspector.Reflection {
Offset = pkg.FieldOffsets[fieldIndex];
Name = pkg.Strings[Definition.nameIndex];
fieldType = pkg.TypeUsages[Definition.typeIndex];
fieldTypeUsage = Definition.typeIndex;
var fieldType = pkg.TypeUsages[fieldTypeUsage];
if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PRIVATE)
Attributes |= FieldAttributes.Private;
if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PUBLIC)

View File

@@ -19,7 +19,7 @@ namespace Il2CppInspector.Reflection {
// Type that this type is declared in for nested types
protected int declaringTypeDefinitionIndex { private get; set; } = -1;
public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByIndex[declaringTypeDefinitionIndex] : null;
public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByDefinitionIndex[declaringTypeDefinitionIndex] : null;
// What sort of member this is, eg. method, field etc.
public abstract MemberTypes MemberType { get; }

View File

@@ -17,14 +17,14 @@ namespace Il2CppInspector.Reflection
public ParameterInfo ReturnParameter { get; }
// Return type of the method
private readonly Il2CppType returnType;
public TypeInfo ReturnType => Assembly.Model.GetType(returnType, MemberTypes.TypeInfo);
private readonly int returnTypeUsage;
public TypeInfo ReturnType => Assembly.Model.GetTypeFromUsage(returnTypeUsage, MemberTypes.TypeInfo);
// TODO: ReturnTypeCustomAttributes
public MethodInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) {
// Add return parameter
returnType = pkg.TypeUsages[Definition.returnType];
returnTypeUsage = Definition.returnType;
ReturnParameter = new ParameterInfo(pkg, -1, this);
}

View File

@@ -34,8 +34,8 @@ namespace Il2CppInspector.Reflection
public string Name { get; }
// Type of this parameter
private readonly Il2CppType paramType;
public TypeInfo ParameterType => Member.Assembly.Model.GetType(paramType, MemberTypes.TypeInfo);
private readonly int paramTypeUsage;
public TypeInfo ParameterType => Member.Assembly.Model.GetTypeFromUsage(paramTypeUsage, MemberTypes.TypeInfo);
// Zero-indexed position of the parameter in parameter list
public int Position { get; }
@@ -46,7 +46,7 @@ namespace Il2CppInspector.Reflection
if (paramIndex == -1) {
Position = -1;
paramType = pkg.TypeUsages[declaringMethod.Definition.returnType];
paramTypeUsage = declaringMethod.Definition.returnType;
Attributes |= ParameterAttributes.Retval;
return;
}
@@ -54,7 +54,8 @@ namespace Il2CppInspector.Reflection
var param = pkg.Params[paramIndex];
Name = pkg.Strings[param.nameIndex];
Position = paramIndex - declaringMethod.Definition.parameterStart;
paramType = pkg.TypeUsages[param.typeIndex];
paramTypeUsage = param.typeIndex;
var paramType = pkg.TypeUsages[paramTypeUsage];
if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_OPTIONAL) != 0)
Attributes |= ParameterAttributes.Optional;

View File

@@ -21,8 +21,8 @@ namespace Il2CppInspector.Reflection {
public TypeAttributes Attributes { get; }
// Type that this type inherits from
private Il2CppType baseType;
public TypeInfo BaseType => baseType != null? Assembly.Model.GetType(baseType, MemberTypes.TypeInfo) : null;
private int baseTypeUsage;
public TypeInfo BaseType => Assembly.Model.GetTypeFromUsage(baseTypeUsage, MemberTypes.TypeInfo);
// True if the type contains unresolved generic type parameters
public bool ContainsGenericParameters { get; }
@@ -49,7 +49,7 @@ namespace Il2CppInspector.Reflection {
public List<MethodInfo> DeclaredMethods { get; } = new List<MethodInfo>();
private int[] declaredNestedTypes;
public IEnumerable<TypeInfo> DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByIndex[x]);
public IEnumerable<TypeInfo> DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByDefinitionIndex[x]);
public List<PropertyInfo> DeclaredProperties { get; } = new List<PropertyInfo>();
@@ -57,12 +57,12 @@ namespace Il2CppInspector.Reflection {
public MethodBase DeclaringMethod => throw new NotImplementedException();
// Gets the type of the object encompassed or referred to by the current array, pointer or reference type
private Il2CppType enumElementType;
private int enumElementTypeUsage;
private TypeInfo elementType;
public TypeInfo ElementType {
get {
if (IsEnum && elementType == null)
elementType = Assembly.Model.GetType(enumElementType, MemberTypes.TypeInfo);
elementType = Assembly.Model.GetTypeFromUsage(enumElementTypeUsage, MemberTypes.TypeInfo);
return elementType;
}
}
@@ -82,8 +82,8 @@ namespace Il2CppInspector.Reflection {
public bool HasElementType => ElementType != null;
private Il2CppType[] implementedInterfaces;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaces.Select(x => Assembly.Model.GetType(x, MemberTypes.TypeInfo));
private int[] implementedInterfaceUsages;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceUsages.Select(x => Assembly.Model.GetTypeFromUsage(x, MemberTypes.TypeInfo));
public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
public bool IsArray { get; }
@@ -155,15 +155,16 @@ namespace Il2CppInspector.Reflection {
Name = pkg.Strings[Definition.nameIndex];
if (Definition.parentIndex >= 0)
baseType = pkg.TypeUsages[Definition.parentIndex];
baseTypeUsage = Definition.parentIndex;
// Nested type?
if (Definition.declaringTypeIndex >= 0) {
declaringTypeDefinitionIndex = (int) pkg.TypeUsages[Definition.declaringTypeIndex].datapoint;
MemberType |= MemberTypes.NestedType;
}
// Add to global type definition list
Assembly.Model.TypesByIndex[Index] = this;
Assembly.Model.TypesByDefinitionIndex[Index] = this;
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0)
Attributes |= TypeAttributes.Serializable;
@@ -196,16 +197,16 @@ namespace Il2CppInspector.Reflection {
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_INTERFACE) != 0)
Attributes |= TypeAttributes.Interface;
// Enumerations - bit 1 of bitfield indicates this (also the baseType will be System.Enum)
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeUsage will be System.Enum)
if (((Definition.bitfield >> 1) & 1) == 1) {
IsEnum = true;
enumElementType = pkg.TypeUsages[Definition.elementTypeIndex];
enumElementTypeUsage = Definition.elementTypeIndex;
}
// Add all implemented interfaces
implementedInterfaces = new Il2CppType[Definition.interfaces_count];
implementedInterfaceUsages = new int[Definition.interfaces_count];
for (var i = 0; i < Definition.interfaces_count; i++)
implementedInterfaces[i] = pkg.TypeUsages[pkg.InterfaceUsageIndices[Definition.interfacesStart + i]];
implementedInterfaceUsages[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i];
// Add all nested types
declaredNestedTypes = new int[Definition.nested_type_count];
@@ -246,7 +247,7 @@ namespace Il2CppInspector.Reflection {
// Generic type unresolved and concrete instance types
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint);
var genericTypeDef = model.TypesByIndex[generic.typeDefinitionIndex];
var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex];
Namespace = genericTypeDef.Namespace;
Name = genericTypeDef.Name;
@@ -261,10 +262,10 @@ namespace Il2CppInspector.Reflection {
GenericTypeParameters = new List<TypeInfo>();
foreach (var pArg in genericTypeParameters) {
var argType = image.ReadMappedObject<Il2CppType>((ulong) pArg);
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(model.GetType(argType)); // TODO: Fix MemberType here
GenericTypeParameters.Add(argType); // TODO: Fix MemberType here
}
Attributes |= TypeAttributes.Class;
}
@@ -272,8 +273,7 @@ namespace Il2CppInspector.Reflection {
// Array with known dimensions and bounds
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) {
var descriptor = image.ReadMappedObject<Il2CppArrayType>(pType.datapoint);
var elementType = image.ReadMappedObject<Il2CppType>(descriptor.etype);
this.elementType = model.GetType(elementType);
elementType = model.GetTypeFromVirtualAddress(descriptor.etype);
Namespace = ElementType.Namespace;
Name = ElementType.Name;
@@ -283,8 +283,7 @@ namespace Il2CppInspector.Reflection {
// Dynamically allocated array
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) {
var elementType = image.ReadMappedObject<Il2CppType>(pType.datapoint);
this.elementType = model.GetType(elementType);
elementType = model.GetTypeFromVirtualAddress(pType.datapoint);
Namespace = ElementType.Namespace;
Name = ElementType.Name;