Model: Ensure all TypeRefs are initialized with the model

Ensures that all types are output in the IDA Python script
Includes TypeRefs that aren't used in any type definition or member
This commit is contained in:
Katy Coe
2020-01-30 07:54:45 +01:00
parent a1e332620c
commit 37f1c49828
7 changed files with 96 additions and 92 deletions

View File

@@ -47,7 +47,7 @@ namespace Il2CppInspector.Reflection
continue; continue;
} }
attribute = new CustomAttributeData { Index = customAttributeIndex, AttributeType = asm.Model.GetTypeFromUsage(typeIndex) }; attribute = new CustomAttributeData { Index = customAttributeIndex, AttributeType = asm.Model.TypesByReferenceIndex[typeIndex] };
asm.Model.AttributesByIndices.TryAdd(i, attribute); asm.Model.AttributesByIndices.TryAdd(i, attribute);
yield return attribute; yield return attribute;

View File

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

View File

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

View File

@@ -17,20 +17,20 @@ 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 // List of all types from TypeDefs ordered by their TypeDefinitionIndex
public TypeInfo[] TypesByDefinitionIndex { get; } public TypeInfo[] TypesByDefinitionIndex { get; }
// List of all type definitions by fully qualified name // List of all types from TypeRefs ordered by index
public TypeInfo[] TypesByReferenceIndex { get; }
// List of all type definitions by fully qualified name (TypeDefs only)
public Dictionary<string, TypeInfo> TypesByFullName { get; } = new Dictionary<string, TypeInfo>(); public Dictionary<string, TypeInfo> TypesByFullName { get; } = new Dictionary<string, TypeInfo>();
// List of all type references ordered by index // List of type references that are initialized via pointers in the image
public TypeInfo[] TypeReferences { get; }
// List of type usages that are initialized via pointers in the image
public ConcurrentDictionary<ulong, TypeInfo> TypesByVirtualAddress { get; } = new ConcurrentDictionary<ulong, TypeInfo>(); public ConcurrentDictionary<ulong, TypeInfo> TypesByVirtualAddress { get; } = new ConcurrentDictionary<ulong, TypeInfo>();
// Every type // Every type
public IEnumerable<TypeInfo> Types => new IEnumerable<TypeInfo>[] {TypesByDefinitionIndex, TypeReferences, TypesByVirtualAddress.Values}.SelectMany(t => t); public IEnumerable<TypeInfo> Types => new IEnumerable<TypeInfo>[] {TypesByDefinitionIndex, TypesByReferenceIndex, TypesByVirtualAddress.Values}.SelectMany(t => t);
// List of all methods ordered by their MethodDefinitionIndex // List of all methods ordered by their MethodDefinitionIndex
public MethodBase[] MethodsByDefinitionIndex { get; } public MethodBase[] MethodsByDefinitionIndex { get; }
@@ -38,67 +38,66 @@ namespace Il2CppInspector.Reflection
// List of all generated CustomAttributeData objects by their index into AttributeTypeIndices // List of all generated CustomAttributeData objects by their index into AttributeTypeIndices
public ConcurrentDictionary<int, CustomAttributeData> AttributesByIndices { get; } = new ConcurrentDictionary<int, CustomAttributeData>(); public ConcurrentDictionary<int, CustomAttributeData> AttributesByIndices { get; } = new ConcurrentDictionary<int, CustomAttributeData>();
public Il2CppModel(Il2CppInspector package) {
Package = package;
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
TypeReferences = new TypeInfo[package.TypeReferences.Count];
MethodsByDefinitionIndex = new MethodBase[package.Methods.Length];
// Create Assembly objects from Il2Cpp package
for (var image = 0; image < package.Images.Length; image++)
Assemblies.Add(new Assembly(this, image));
}
// Get an assembly by its image name // Get an assembly by its image name
public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name); public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name);
private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) { // Create type model
public Il2CppModel(Il2CppInspector package) {
Package = package;
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count];
MethodsByDefinitionIndex = new MethodBase[package.Methods.Length];
// Recursively create hierarchy of assemblies and types from TypeDefs
// No code that executes here can access any type through a TypeRef (ie. via TypesByReferenceIndex)
for (var image = 0; image < package.Images.Length; image++)
Assemblies.Add(new Assembly(this, image));
// Create and reference types from TypeRefs
// Note that you can't resolve any TypeRefs until all the TypeDefs have been processed
for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) {
var typeRef = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(typeRef);
TypesByReferenceIndex[typeRefIndex] = referencedType;
}
}
private TypeInfo resolveTypeReference(Il2CppType typeRef) {
TypeInfo underlyingType; TypeInfo underlyingType;
switch (usage.type) { switch (typeRef.type) {
// Classes defined in the metadata (reference to a TypeDef)
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 underlyingType = TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex
underlyingType = TypesByDefinitionIndex[usage.datapoint]; // klassIndex
break; break;
// Constructed types
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_PTR: case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
// Generic type and generic method parameters
case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
// Everything that requires special handling
underlyingType = new TypeInfo(this, usage, memberType); underlyingType = new TypeInfo(this, typeRef);
break; break;
// Primitive types
default: default:
// Primitive types underlyingType = getTypeDefinitionFromTypeEnum(typeRef.type);
underlyingType = GetTypeFromTypeEnum(usage.type);
break; break;
} }
// Create a reference type if necessary // Create a reference type if necessary
return usage.byref ? underlyingType.MakeByRefType() : underlyingType; return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType;
} }
// Get or generate a type from its IL2CPP binary type usage reference // Basic primitive types are specified via a flag value
// (field, return type, generic type parameter etc.) private TypeInfo getTypeDefinitionFromTypeEnum(Il2CppTypeEnum t) {
public TypeInfo GetTypeFromUsage(int typeUsageIndex, MemberTypes memberType = MemberTypes.All) {
// Already generated type previously?
if (TypeReferences[typeUsageIndex] != null)
return TypeReferences[typeUsageIndex];
var usage = Package.TypeReferences[typeUsageIndex];
var newUsage = getNewTypeUsage(usage, memberType);
TypeReferences[typeUsageIndex] = newUsage;
return newUsage;
}
// Basic primitive types
public TypeInfo GetTypeFromTypeEnum(Il2CppTypeEnum t) {
if ((int) t >= Il2CppConstants.FullNameTypeString.Count) if ((int) t >= Il2CppConstants.FullNameTypeString.Count)
return null; return null;
@@ -107,16 +106,17 @@ namespace Il2CppInspector.Reflection
} }
// Type from a virtual address pointer // Type from a virtual address pointer
// These are always nested types frorm usages within another type // These are always nested types from references within another TypeRef
// TODO: Eliminate GetTypeFromVirtualAddress() - use base and offset from MetadataRegistration.ptypes (Package.TypeReferences) instead
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) { public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
if (TypesByVirtualAddress.ContainsKey(ptr)) if (TypesByVirtualAddress.ContainsKey(ptr))
return TypesByVirtualAddress[ptr]; return TypesByVirtualAddress[ptr];
var type = Package.BinaryImage.ReadMappedObject<Il2CppType>(ptr); var type = Package.BinaryImage.ReadMappedObject<Il2CppType>(ptr);
var newUsage = getNewTypeUsage(type, MemberTypes.NestedType); var referencedType = resolveTypeReference(type);
TypesByVirtualAddress.TryAdd(ptr, newUsage); TypesByVirtualAddress.TryAdd(ptr, referencedType);
return newUsage; return referencedType;
} }
// The attribute index is an index into AttributeTypeRanges, each of which is a start-end range index into AttributeTypeIndices, each of which is a TypeIndex // The attribute index is an index into AttributeTypeRanges, each of which is a start-end range index into AttributeTypeIndices, each of which is a TypeIndex
@@ -131,12 +131,12 @@ namespace Il2CppInspector.Reflection
return index; return index;
} }
// Get the name of a metadata usage // Get the name of a metadata typeRef
public string GetMetadataUsageName(MetadataUsage usage) { public string GetMetadataUsageName(MetadataUsage usage) {
switch (usage.Type) { switch (usage.Type) {
case MetadataUsageType.TypeInfo: case MetadataUsageType.TypeInfo:
case MetadataUsageType.Type: case MetadataUsageType.Type:
var type = GetTypeFromUsage(usage.SourceIndex); var type = TypesByReferenceIndex[usage.SourceIndex];
return type.Name; return type.Name;
case MetadataUsageType.MethodDef: case MetadataUsageType.MethodDef:
@@ -145,7 +145,7 @@ namespace Il2CppInspector.Reflection
case MetadataUsageType.FieldInfo: case MetadataUsageType.FieldInfo:
var fieldRef = Package.FieldRefs[usage.SourceIndex]; var fieldRef = Package.FieldRefs[usage.SourceIndex];
type = GetTypeFromUsage(fieldRef.typeIndex); type = TypesByReferenceIndex[fieldRef.typeIndex];
var field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex); var field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex);
return $"{type.Name}.{field.Name}"; return $"{type.Name}.{field.Name}";

View File

@@ -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. All rights reserved.
*/ */
@@ -17,8 +17,8 @@ namespace Il2CppInspector.Reflection
public ParameterInfo ReturnParameter { get; } public ParameterInfo ReturnParameter { get; }
// Return type of the method // Return type of the method
private readonly int returnTypeUsage; private readonly int returnTypeReference;
public TypeInfo ReturnType => Assembly.Model.GetTypeFromUsage(returnTypeUsage, MemberTypes.TypeInfo); public TypeInfo ReturnType => Assembly.Model.TypesByReferenceIndex[returnTypeReference];
public override bool RequiresUnsafeContext => base.RequiresUnsafeContext || ReturnType.RequiresUnsafeContext; public override bool RequiresUnsafeContext => base.RequiresUnsafeContext || ReturnType.RequiresUnsafeContext;
@@ -26,7 +26,7 @@ namespace Il2CppInspector.Reflection
public MethodInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) { public MethodInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) {
// Add return parameter // Add return parameter
returnTypeUsage = Definition.returnType; returnTypeReference = Definition.returnType;
ReturnParameter = new ParameterInfo(pkg, -1, this); ReturnParameter = new ParameterInfo(pkg, -1, this);
} }

View File

@@ -43,8 +43,8 @@ namespace Il2CppInspector.Reflection
public string CSharpSafeName => Constants.Keywords.Contains(Name) ? "@" + Name : Name; public string CSharpSafeName => Constants.Keywords.Contains(Name) ? "@" + Name : Name;
// Type of this parameter // Type of this parameter
private readonly int paramTypeUsage; private readonly int paramTypeReference;
public TypeInfo ParameterType => DeclaringMethod.Assembly.Model.GetTypeFromUsage(paramTypeUsage, MemberTypes.TypeInfo); public TypeInfo ParameterType => DeclaringMethod.Assembly.Model.TypesByReferenceIndex[paramTypeReference];
// Zero-indexed position of the parameter in parameter list // Zero-indexed position of the parameter in parameter list
public int Position { get; } public int Position { get; }
@@ -56,7 +56,7 @@ namespace Il2CppInspector.Reflection
if (paramIndex == -1) { if (paramIndex == -1) {
Position = -1; Position = -1;
paramTypeUsage = declaringMethod.Definition.returnType; paramTypeReference = declaringMethod.Definition.returnType;
Attributes |= ParameterAttributes.Retval; Attributes |= ParameterAttributes.Retval;
return; return;
} }
@@ -69,8 +69,8 @@ namespace Il2CppInspector.Reflection
Name = string.Format($"param_{Index:x8}"); Name = string.Format($"param_{Index:x8}");
Position = paramIndex - declaringMethod.Definition.parameterStart; Position = paramIndex - declaringMethod.Definition.parameterStart;
paramTypeUsage = Definition.typeIndex; paramTypeReference = Definition.typeIndex;
var paramType = pkg.TypeReferences[paramTypeUsage]; var paramType = pkg.TypeReferences[paramTypeReference];
if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_HAS_DEFAULT) != 0) if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_HAS_DEFAULT) != 0)
Attributes |= ParameterAttributes.HasDefault; Attributes |= ParameterAttributes.HasDefault;

View File

@@ -22,11 +22,11 @@ namespace Il2CppInspector.Reflection {
public TypeAttributes Attributes { get; } public TypeAttributes Attributes { get; }
// Type that this type inherits from // Type that this type inherits from
private readonly int baseTypeUsage = -1; private readonly int baseTypeReference = -1;
public TypeInfo BaseType => IsPointer? null : public TypeInfo BaseType => IsPointer? null :
baseTypeUsage != -1? baseTypeReference != -1?
Assembly.Model.GetTypeFromUsage(baseTypeUsage, MemberTypes.TypeInfo) Assembly.Model.TypesByReferenceIndex[baseTypeReference]
: IsArray? Assembly.Model.TypesByFullName["System.Array"] : IsArray? Assembly.Model.TypesByFullName["System.Array"]
: Namespace != "System" || BaseName != "Object" ? Assembly.Model.TypesByFullName["System.Object"] : Namespace != "System" || BaseName != "Object" ? Assembly.Model.TypesByFullName["System.Object"]
: null; : null;
@@ -109,7 +109,7 @@ namespace Il2CppInspector.Reflection {
public TypeInfo[] GetGenericParameterConstraints() { public TypeInfo[] GetGenericParameterConstraints() {
var types = new TypeInfo[genericConstraintCount]; var types = new TypeInfo[genericConstraintCount];
for (int c = 0; c < genericConstraintCount; c++) for (int c = 0; c < genericConstraintCount; c++)
types[c] = Assembly.Model.GetTypeFromUsage(Assembly.Model.Package.GenericConstraintIndices[genericConstraintIndex + c], MemberTypes.TypeInfo); types[c] = Assembly.Model.TypesByReferenceIndex[Assembly.Model.Package.GenericConstraintIndices[genericConstraintIndex + c]];
return types; return types;
} }
@@ -373,14 +373,14 @@ namespace Il2CppInspector.Reflection {
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.haselementtype?view=netframework-4.8 // See: https://docs.microsoft.com/en-us/dotnet/api/system.type.haselementtype?view=netframework-4.8
public bool HasElementType => ElementType != null; public bool HasElementType => ElementType != null;
private readonly int[] implementedInterfaceUsages; private readonly int[] implementedInterfaceReferences;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceUsages.Select(x => Assembly.Model.GetTypeFromUsage(x, MemberTypes.TypeInfo)); public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceReferences.Select(x => Assembly.Model.TypesByReferenceIndex[x]);
public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract; public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
public bool IsArray { get; } public bool IsArray { get; }
public bool IsByRef { get; } public bool IsByRef { get; }
public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class; public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class;
public bool IsEnum => enumUnderlyingTypeUsage != -1; public bool IsEnum => enumUnderlyingTypeReference != -1;
public bool IsGenericParameter { get; } public bool IsGenericParameter { get; }
public bool IsGenericType { get; } public bool IsGenericType { get; }
public bool IsGenericTypeDefinition => genericArguments.Any() && genericArguments.All(a => a.IsGenericTypeParameter); public bool IsGenericTypeDefinition => genericArguments.Any() && genericArguments.All(a => a.IsGenericTypeParameter);
@@ -422,21 +422,19 @@ namespace Il2CppInspector.Reflection {
public string[] GetEnumNames() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.Name).ToArray() : throw new InvalidOperationException("Type is not an enumeration"); public string[] GetEnumNames() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.Name).ToArray() : throw new InvalidOperationException("Type is not an enumeration");
// The underlying type of an enumeration (int by default) // The underlying type of an enumeration (int by default)
private readonly int enumUnderlyingTypeUsage = -1; private readonly int enumUnderlyingTypeReference = -1;
private TypeInfo enumUnderlyingType; private TypeInfo enumUnderlyingType;
public TypeInfo GetEnumUnderlyingType() { public TypeInfo GetEnumUnderlyingType() {
if (!IsEnum) if (!IsEnum)
return null; return null;
enumUnderlyingType ??= Assembly.Model.GetTypeFromUsage(enumUnderlyingTypeUsage, MemberTypes.TypeInfo); enumUnderlyingType ??= Assembly.Model.TypesByReferenceIndex[enumUnderlyingTypeReference];
return enumUnderlyingType; return enumUnderlyingType;
} }
public Array GetEnumValues() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.DefaultValue).ToArray() : throw new InvalidOperationException("Type is not an enumeration"); public Array GetEnumValues() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.DefaultValue).ToArray() : throw new InvalidOperationException("Type is not an enumeration");
// Initialize from specified type index in metadata // Initialize type from TypeDef using specified index in metadata
// Top-level types
public TypeInfo(int typeIndex, Assembly owner) : base(owner) { public TypeInfo(int typeIndex, Assembly owner) : base(owner) {
var pkg = Assembly.Model.Package; var pkg = Assembly.Model.Package;
@@ -447,7 +445,7 @@ namespace Il2CppInspector.Reflection {
// Derived type? // Derived type?
if (Definition.parentIndex >= 0) if (Definition.parentIndex >= 0)
baseTypeUsage = Definition.parentIndex; baseTypeReference = Definition.parentIndex;
// Nested type? // Nested type?
if (Definition.declaringTypeIndex >= 0) { if (Definition.declaringTypeIndex >= 0) {
@@ -501,18 +499,18 @@ namespace Il2CppInspector.Reflection {
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_INTERFACE) != 0) if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_INTERFACE) != 0)
Attributes |= TypeAttributes.Interface; Attributes |= TypeAttributes.Interface;
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeUsage will be System.Enum) // Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum)
if (((Definition.bitfield >> 1) & 1) == 1) if (((Definition.bitfield >> 1) & 1) == 1)
enumUnderlyingTypeUsage = Definition.elementTypeIndex; enumUnderlyingTypeReference = Definition.elementTypeIndex;
// Pass-by-reference type // Pass-by-reference type
// NOTE: This should actually always evaluate to false in the current implementation // NOTE: This should actually always evaluate to false in the current implementation
IsByRef = Index == Definition.byrefTypeIndex; IsByRef = Index == Definition.byrefTypeIndex;
// Add all implemented interfaces // Add all implemented interfaces
implementedInterfaceUsages = new int[Definition.interfaces_count]; implementedInterfaceReferences = new int[Definition.interfaces_count];
for (var i = 0; i < Definition.interfaces_count; i++) for (var i = 0; i < Definition.interfaces_count; i++)
implementedInterfaceUsages[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i]; implementedInterfaceReferences[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i];
// Add all nested types // Add all nested types
declaredNestedTypes = new int[Definition.nested_type_count]; declaredNestedTypes = new int[Definition.nested_type_count];
@@ -575,14 +573,20 @@ namespace Il2CppInspector.Reflection {
DeclaredEvents.Add(new EventInfo(pkg, e, this)); DeclaredEvents.Add(new EventInfo(pkg, e, this));
} }
// Initialize type from type reference // Initialize type from type reference (TypeRef)
// Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType // Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType
public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) { public TypeInfo(Il2CppModel model, Il2CppType pType) {
var image = model.Package.BinaryImage; var image = model.Package.BinaryImage;
// Open and closed generic types // Open and closed generic types
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); // Il2CppGenericClass * var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); // Il2CppGenericClass *
// We have seen one test case where the TypeRef can point to no generic instance
// This is going to leave the TypeInfo in an undefined state
if (generic.typeDefinitionIndex == 0x0000_0000_ffff_ffff)
return;
var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex]; var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex];
Assembly = genericTypeDef.Assembly; Assembly = genericTypeDef.Assembly;
@@ -592,12 +596,12 @@ namespace Il2CppInspector.Reflection {
// Derived type? // Derived type?
if (genericTypeDef.Definition.parentIndex >= 0) if (genericTypeDef.Definition.parentIndex >= 0)
baseTypeUsage = genericTypeDef.Definition.parentIndex; baseTypeReference = genericTypeDef.Definition.parentIndex;
// Nested type? // Nested type?
if (genericTypeDef.Definition.declaringTypeIndex >= 0) { if (genericTypeDef.Definition.declaringTypeIndex >= 0) {
declaringTypeDefinitionIndex = (int)model.Package.TypeReferences[genericTypeDef.Definition.declaringTypeIndex].datapoint; declaringTypeDefinitionIndex = (int)model.Package.TypeReferences[genericTypeDef.Definition.declaringTypeIndex].datapoint;
MemberType = memberType | MemberTypes.NestedType; MemberType |= MemberTypes.NestedType;
} }
IsGenericType = true; IsGenericType = true;
@@ -665,11 +669,11 @@ namespace Il2CppInspector.Reflection {
// Derived type? // Derived type?
if (ownerType.Definition.parentIndex >= 0) if (ownerType.Definition.parentIndex >= 0)
baseTypeUsage = ownerType.Definition.parentIndex; baseTypeReference = ownerType.Definition.parentIndex;
// Nested type always - sets DeclaringType used below // Nested type always - sets DeclaringType used below
declaringTypeDefinitionIndex = ownerType.Index; declaringTypeDefinitionIndex = ownerType.Index;
MemberType = memberType | MemberTypes.NestedType; MemberType |= MemberTypes.NestedType;
// All generic method type parameters have a declared method // All generic method type parameters have a declared method
if (container.is_method == 1) if (container.is_method == 1)