Model: Handle nested types

This commit is contained in:
Katy Coe
2019-10-31 16:02:32 +01:00
parent cb9d391d52
commit 70a541c36c
7 changed files with 53 additions and 27 deletions

View File

@@ -31,6 +31,7 @@ namespace Il2CppInspector
public Il2CppPropertyDefinition[] Properties => Metadata.Properties; public Il2CppPropertyDefinition[] Properties => Metadata.Properties;
public Il2CppEventDefinition[] Events => Metadata.Events; public Il2CppEventDefinition[] Events => Metadata.Events;
public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices; public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices;
public int[] NestedTypeIndices => Metadata.NestedTypeIndices;
public Dictionary<int, object> FieldDefaultValue { get; } = new Dictionary<int, object>(); public Dictionary<int, object> FieldDefaultValue { get; } = new Dictionary<int, object>();
public List<long> FieldOffsets { get; } public List<long> FieldOffsets { get; }
public List<Il2CppType> TypeUsages => Binary.Types; public List<Il2CppType> TypeUsages => Binary.Types;

View File

@@ -15,20 +15,18 @@ namespace Il2CppInspector.Reflection
public Il2CppInspector Package { get; } public Il2CppInspector Package { get; }
public List<Assembly> Assemblies { get; } = new List<Assembly>(); public List<Assembly> Assemblies { get; } = new List<Assembly>();
// List of all types ordered by their TypeDefinitionIndex
public TypeInfo[] TypesByIndex { get; }
public Il2CppModel(Il2CppInspector package) { public Il2CppModel(Il2CppInspector package) {
Package = package; Package = package;
TypesByIndex = new TypeInfo[package.TypeDefinitions.Length];
// Create Assembly objects from Il2Cpp package // Create Assembly objects from Il2Cpp package
for (var image = 0; image < package.Images.Length; image++) for (var image = 0; image < package.Images.Length; image++)
Assemblies.Add(new Assembly(this, image)); Assemblies.Add(new Assembly(this, image));
} }
// Get the assembly in which a type is defined
public Assembly GetAssembly(TypeInfo type) => Assemblies.FirstOrDefault(x => x.DefinedTypes.Contains(type));
// Get a type from its IL2CPP type index
public TypeInfo GetTypeFromIndex(long typeIndex) => Assemblies.SelectMany(x => x.DefinedTypes).FirstOrDefault(x => x.Index == typeIndex);
// Get or generate a type from its IL2CPP binary type usage reference // Get or generate a type from its IL2CPP binary type usage reference
// (field, return type, generic type parameter etc.) // (field, return type, generic type parameter etc.)
public TypeInfo GetType(Il2CppType pType, MemberTypes memberType = MemberTypes.All) { public TypeInfo GetType(Il2CppType pType, MemberTypes memberType = MemberTypes.All) {
@@ -36,7 +34,7 @@ namespace Il2CppInspector.Reflection
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
// Classes defined in the metadata // Classes defined in the metadata
return GetTypeFromIndex((int) pType.datapoint); return TypesByIndex[(int) pType.datapoint];
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
@@ -57,7 +55,8 @@ namespace Il2CppInspector.Reflection
if ((int)t >= Il2CppConstants.FullNameTypeString.Count) if ((int)t >= Il2CppConstants.FullNameTypeString.Count)
return null; return null;
return Assemblies.SelectMany(x => x.DefinedTypes).First(x => x.FullName == Il2CppConstants.FullNameTypeString[(int)t]); var fqn = Il2CppConstants.FullNameTypeString[(int) t];
return TypesByIndex.First(x => x.FullName == fqn);
} }
} }
} }

View File

@@ -28,6 +28,7 @@ namespace Il2CppInspector
public Il2CppEventDefinition[] Events { get; } public Il2CppEventDefinition[] Events { get; }
public int[] InterfaceUsageIndices { get; } public int[] InterfaceUsageIndices { get; }
public int[] NestedTypeIndices { get; }
public Dictionary<int, string> Strings { get; } = new Dictionary<int, string>(); public Dictionary<int, string> Strings { get; } = new Dictionary<int, string>();
@@ -96,6 +97,7 @@ namespace Il2CppInspector
Properties = ReadArray<Il2CppPropertyDefinition>(Header.propertiesOffset, Header.propertiesCount / Sizeof(typeof(Il2CppPropertyDefinition))); Properties = ReadArray<Il2CppPropertyDefinition>(Header.propertiesOffset, Header.propertiesCount / Sizeof(typeof(Il2CppPropertyDefinition)));
Events = ReadArray<Il2CppEventDefinition>(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition))); Events = ReadArray<Il2CppEventDefinition>(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition)));
InterfaceUsageIndices = ReadArray<int>(Header.interfacesOffset, Header.interfacesCount / sizeof(int)); 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 // TODO: ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes
// Get all string literals // Get all string literals

View File

@@ -47,7 +47,7 @@ namespace Il2CppInspector.Reflection {
// Generate types in DefinedTypes from typeStart to typeStart+typeCount-1 // Generate types in DefinedTypes from typeStart to typeStart+typeCount-1
for (var t = Definition.typeStart; t < Definition.typeStart + Definition.typeCount; t++) { for (var t = Definition.typeStart; t < Definition.typeStart + Definition.typeCount; t++) {
var type = new TypeInfo(Model.Package, t, this); var type = new TypeInfo(t, this);
// Don't add empty module definitions // Don't add empty module definitions
if (type.Name != "<Module>") if (type.Name != "<Module>")

View File

@@ -18,7 +18,8 @@ namespace Il2CppInspector.Reflection {
public IEnumerable<CustomAttributeData> CustomAttributes => throw new NotImplementedException(); public IEnumerable<CustomAttributeData> CustomAttributes => throw new NotImplementedException();
// Type that this type is declared in for nested types // Type that this type is declared in for nested types
public TypeInfo DeclaringType { get; } protected int declaringTypeDefinitionIndex { private get; set; } = -1;
public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByIndex[declaringTypeDefinitionIndex] : null;
// What sort of member this is, eg. method, field etc. // What sort of member this is, eg. method, field etc.
public abstract MemberTypes MemberType { get; } public abstract MemberTypes MemberType { get; }
@@ -29,16 +30,13 @@ namespace Il2CppInspector.Reflection {
// TODO: GetCustomAttributes etc. // TODO: GetCustomAttributes etc.
// For top-level members in an assembly (ie. non-nested types) // For top-level members in an assembly (ie. non-nested types)
protected MemberInfo(Assembly asm, TypeInfo declaringType = null) { protected MemberInfo(Assembly asm) => Assembly = asm;
Assembly = asm;
DeclaringType = declaringType;
}
// For lower level members, eg. fields, properties etc. and nested types // For lower level members, eg. fields, properties etc. and nested types
protected MemberInfo(TypeInfo declaringType) { protected MemberInfo(TypeInfo declaringType = null) {
if (declaringType != null) { if (declaringType != null) {
Assembly = declaringType.Assembly; Assembly = declaringType.Assembly;
DeclaringType = declaringType; declaringTypeDefinitionIndex = declaringType.Index;
} }
} }

View File

@@ -38,7 +38,7 @@ namespace Il2CppInspector.Reflection
public TypeInfo ParameterType => Member.Assembly.Model.GetType(paramType, MemberTypes.TypeInfo); public TypeInfo ParameterType => Member.Assembly.Model.GetType(paramType, MemberTypes.TypeInfo);
// Zero-indexed position of the parameter in parameter list // Zero-indexed position of the parameter in parameter list
public int Position { get; private set; } public int Position { get; }
// Create a parameter. Specify paramIndex == -1 for a return type parameter // Create a parameter. Specify paramIndex == -1 for a return type parameter
public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMethod) { public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMethod) {

View File

@@ -47,7 +47,10 @@ namespace Il2CppInspector.Reflection {
public List<FieldInfo> DeclaredFields { get; } = new List<FieldInfo>(); public List<FieldInfo> DeclaredFields { get; } = new List<FieldInfo>();
public List<MemberInfo> DeclaredMembers => throw new NotImplementedException(); public List<MemberInfo> DeclaredMembers => throw new NotImplementedException();
public List<MethodInfo> DeclaredMethods { get; } = new List<MethodInfo>(); public List<MethodInfo> DeclaredMethods { get; } = new List<MethodInfo>();
public List<TypeInfo> DeclaredNestedTypes => throw new NotImplementedException();
private int[] declaredNestedTypes;
public IEnumerable<TypeInfo> DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByIndex[x]);
public List<PropertyInfo> DeclaredProperties { get; } = new List<PropertyInfo>(); 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 // Method that the type is declared in if this is a type parameter of a generic method
@@ -68,6 +71,7 @@ namespace Il2CppInspector.Reflection {
public string FullName => (IsPointer? "void *" : "") public string FullName => (IsPointer? "void *" : "")
+ Namespace + Namespace
+ (Namespace.Length > 0? "." : "") + (Namespace.Length > 0? "." : "")
+ (DeclaringType != null? DeclaringType.Name + "." : "")
+ base.Name + base.Name
+ (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "") + (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "")
+ (IsArray? "[]" : ""); + (IsArray? "[]" : "");
@@ -91,7 +95,7 @@ namespace Il2CppInspector.Reflection {
public bool IsGenericTypeDefinition => throw new NotImplementedException(); public bool IsGenericTypeDefinition => throw new NotImplementedException();
public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import; public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import;
public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
public bool IsNested { get; } // TODO: Partially implemented public bool IsNested => DeclaringType != null;
public bool IsNestedAssembly => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; public bool IsNestedAssembly => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly;
public bool IsNestedFamANDAssem => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; public bool IsNestedFamANDAssem => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem;
public bool IsNestedFamily => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; public bool IsNestedFamily => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily;
@@ -108,7 +112,8 @@ namespace Il2CppInspector.Reflection {
public bool IsSpecialName => (Attributes & TypeAttributes.SpecialName) == TypeAttributes.SpecialName; public bool IsSpecialName => (Attributes & TypeAttributes.SpecialName) == TypeAttributes.SpecialName;
public bool IsValueType => BaseType?.FullName == "System.ValueType"; public bool IsValueType => BaseType?.FullName == "System.ValueType";
public override MemberTypes MemberType { get; } // May get overridden by Il2CppType-based constructor below
public override MemberTypes MemberType { get; } = MemberTypes.TypeInfo;
public override string Name { public override string Name {
get => (IsPointer ? "void *" : "") get => (IsPointer ? "void *" : "")
@@ -118,7 +123,11 @@ namespace Il2CppInspector.Reflection {
protected set => base.Name = value; protected set => base.Name = value;
} }
public string Namespace { get; } private string @namespace;
public string Namespace {
get => !string.IsNullOrEmpty(@namespace) ? @namespace : DeclaringType?.Namespace ?? "<default namespace>";
set => @namespace = value;
}
// Number of dimensions of an array // Number of dimensions of an array
private readonly int arrayRank; private readonly int arrayRank;
@@ -135,8 +144,11 @@ namespace Il2CppInspector.Reflection {
// TODO: Generic stuff // TODO: Generic stuff
// Initialize from specified type index in metadata // Initialize from specified type index in metadata
public TypeInfo(Il2CppInspector pkg, int typeIndex, Assembly owner) :
base(owner) { // Top-level types
public TypeInfo(int typeIndex, Assembly owner) : base(owner) {
var pkg = Assembly.Model.Package;
Definition = pkg.TypeDefinitions[typeIndex]; Definition = pkg.TypeDefinitions[typeIndex];
Index = typeIndex; Index = typeIndex;
Namespace = pkg.Strings[Definition.namespaceIndex]; Namespace = pkg.Strings[Definition.namespaceIndex];
@@ -145,6 +157,14 @@ namespace Il2CppInspector.Reflection {
if (Definition.parentIndex >= 0) if (Definition.parentIndex >= 0)
baseType = pkg.TypeUsages[Definition.parentIndex]; baseType = pkg.TypeUsages[Definition.parentIndex];
// Nested type?
if (Definition.declaringTypeIndex >= 0) {
declaringTypeDefinitionIndex = (int) pkg.TypeUsages[Definition.declaringTypeIndex].datapoint;
}
// Add to global type definition list
Assembly.Model.TypesByIndex[Index] = this;
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0) if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0)
Attributes |= TypeAttributes.Serializable; Attributes |= TypeAttributes.Serializable;
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_VISIBILITY_MASK) == Il2CppConstants.TYPE_ATTRIBUTE_PUBLIC) if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_VISIBILITY_MASK) == Il2CppConstants.TYPE_ATTRIBUTE_PUBLIC)
@@ -187,6 +207,11 @@ namespace Il2CppInspector.Reflection {
for (var i = 0; i < Definition.interfaces_count; i++) for (var i = 0; i < Definition.interfaces_count; i++)
implementedInterfaces[i] = pkg.TypeUsages[pkg.InterfaceUsageIndices[Definition.interfacesStart + i]]; implementedInterfaces[i] = pkg.TypeUsages[pkg.InterfaceUsageIndices[Definition.interfacesStart + i]];
// Add all nested types
declaredNestedTypes = new int[Definition.nested_type_count];
for (var n = 0; n < Definition.nested_type_count; n++)
declaredNestedTypes[n] = pkg.NestedTypeIndices[Definition.nestedTypesStart + n];
// Add all fields // Add all fields
for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++) for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++)
DeclaredFields.Add(new FieldInfo(pkg, f, this)); DeclaredFields.Add(new FieldInfo(pkg, f, this));
@@ -207,20 +232,21 @@ namespace Il2CppInspector.Reflection {
// Add all events // Add all events
for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++) for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++)
DeclaredEvents.Add(new EventInfo(pkg, e, this)); DeclaredEvents.Add(new EventInfo(pkg, e, this));
MemberType = MemberTypes.TypeInfo;
} }
// Initialize type from binary usage // Initialize type from binary usage
public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) : base(null) { public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) {
var image = model.Package.BinaryImage; var image = model.Package.BinaryImage;
IsNested = true; // TODO: IsNested = true;
MemberType = memberType; MemberType = memberType;
// TODO: Set Assembly and DeclaringType
// Generic type unresolved and concrete instance types // Generic type unresolved and concrete instance types
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint);
var genericTypeDef = model.GetTypeFromIndex(generic.typeDefinitionIndex); var genericTypeDef = model.TypesByIndex[generic.typeDefinitionIndex];
Namespace = genericTypeDef.Namespace; Namespace = genericTypeDef.Namespace;
Name = genericTypeDef.Name; Name = genericTypeDef.Name;