Generify reference indices to prepare for generic instances

We use indices into the TypesByReferenceIndex table to defer type lookup
during model construction. However, this won't support fully generic
instances (which might not be in the type table at all). This patch
introduces a new TypeRef abstraction which can either represent a fixed
index or an instantiated generic type.
This commit is contained in:
Robert Xiao
2020-04-11 13:21:46 -07:00
committed by Katy
parent 3cd5af09fc
commit a9f6e7b4e0
6 changed files with 65 additions and 32 deletions

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 eventTypeReference; private readonly TypeRef eventTypeReference;
public TypeInfo EventHandlerType => Assembly.Model.TypesByReferenceIndex[eventTypeReference]; public TypeInfo EventHandlerType => eventTypeReference.Value;
// 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];
eventTypeReference = Definition.typeIndex; eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var eventType = pkg.TypeReferences[eventTypeReference]; var eventType = pkg.TypeReferences[Definition.typeIndex];
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

@@ -37,8 +37,8 @@ namespace Il2CppInspector.Reflection {
public FieldAttributes Attributes { get; } public FieldAttributes Attributes { get; }
// Type of field // Type of field
private readonly int fieldTypeReference; private readonly TypeRef fieldTypeReference;
public TypeInfo FieldType => Assembly.Model.TypesByReferenceIndex[fieldTypeReference]; public TypeInfo FieldType => fieldTypeReference.Value;
// 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
@@ -89,8 +89,8 @@ namespace Il2CppInspector.Reflection {
rawOffset = pkg.FieldOffsets[fieldIndex]; rawOffset = pkg.FieldOffsets[fieldIndex];
fieldTypeReference = Definition.typeIndex; fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var fieldType = pkg.TypeReferences[fieldTypeReference]; var fieldType = pkg.TypeReferences[Definition.typeIndex];
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,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 returnTypeReference; private readonly TypeRef returnTypeReference;
public TypeInfo ReturnType => Assembly.Model.TypesByReferenceIndex[returnTypeReference]; public TypeInfo ReturnType => returnTypeReference.Value;
public override bool RequiresUnsafeContext => base.RequiresUnsafeContext || ReturnType.RequiresUnsafeContext; public override bool RequiresUnsafeContext => base.RequiresUnsafeContext || ReturnType.RequiresUnsafeContext;
@@ -26,15 +26,14 @@ 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
returnTypeReference = Definition.returnType; returnTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.returnType);
ReturnParameter = new ParameterInfo(pkg, -1, this); ReturnParameter = new ParameterInfo(pkg, -1, this);
} }
public MethodInfo(Il2CppModel model, Il2CppMethodSpec spec, TypeInfo declaringType) : base(model, spec, declaringType) { public MethodInfo(Il2CppModel model, Il2CppMethodSpec spec, TypeInfo declaringType) : base(model, spec, declaringType) {
var methodDef = model.MethodsByDefinitionIndex[spec.methodDefinitionIndex]; var methodDef = model.MethodsByDefinitionIndex[spec.methodDefinitionIndex];
// Add return parameter (TODO substitute type)
// Add return parameter returnTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, methodDef.Definition.returnType);
returnTypeReference = methodDef.Definition.returnType;
ReturnParameter = ((MethodInfo) methodDef).ReturnParameter; ReturnParameter = ((MethodInfo) methodDef).ReturnParameter;
} }

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 paramTypeReference; private readonly TypeRef paramTypeReference;
public TypeInfo ParameterType => DeclaringMethod.Assembly.Model.TypesByReferenceIndex[paramTypeReference]; public TypeInfo ParameterType => paramTypeReference.Value;
// 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;
paramTypeReference = declaringMethod.Definition.returnType; paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.returnType);
Attributes |= ParameterAttributes.Retval; Attributes |= ParameterAttributes.Retval;
return; return;
} }
@@ -69,8 +69,9 @@ 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;
paramTypeReference = Definition.typeIndex; paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.typeIndex);
var paramType = pkg.TypeReferences[paramTypeReference];
var paramType = pkg.TypeReferences[Definition.typeIndex];
if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_HAS_DEFAULT) != 0) if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_HAS_DEFAULT) != 0)
Attributes |= ParameterAttributes.HasDefault; Attributes |= ParameterAttributes.HasDefault;
@@ -103,8 +104,8 @@ namespace Il2CppInspector.Reflection
Position = generic.Position; Position = generic.Position;
Attributes = generic.Attributes; Attributes = generic.Attributes;
// Search for the concrete type's TypeRef index to store as the parameter type reference index // TODO substitute concrete params?
paramTypeReference = Array.IndexOf(model.TypesByReferenceIndex, concrete); paramTypeReference = TypeRef.FromTypeInfo(concrete);
DefaultValue = generic.DefaultValue; DefaultValue = generic.DefaultValue;
DefaultValueMetadataAddress = generic.DefaultValueMetadataAddress; DefaultValueMetadataAddress = generic.DefaultValueMetadataAddress;

View File

@@ -510,14 +510,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[] implementedInterfaceReferences; private readonly TypeRef[] implementedInterfaceReferences;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceReferences.Select(x => Assembly.Model.TypesByReferenceIndex[x]); public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceReferences.Select(x => x.Value);
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 => enumUnderlyingTypeReference != -1; public bool IsEnum { get; }
public bool IsGenericParameter { get; } public bool IsGenericParameter { get; }
public bool IsGenericType { get; } public bool IsGenericType { get; }
public bool IsGenericTypeDefinition => (Definition != null) && genericArguments.Any(); public bool IsGenericTypeDefinition => (Definition != null) && genericArguments.Any();
@@ -559,14 +559,12 @@ 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 enumUnderlyingTypeReference = -1; private readonly TypeRef enumUnderlyingTypeReference = null;
private TypeInfo enumUnderlyingType;
public TypeInfo GetEnumUnderlyingType() { public TypeInfo GetEnumUnderlyingType() {
if (!IsEnum) if (!IsEnum)
return null; return null;
enumUnderlyingType ??= Assembly.Model.TypesByReferenceIndex[enumUnderlyingTypeReference]; return enumUnderlyingTypeReference.Value;
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");
@@ -634,17 +632,19 @@ namespace Il2CppInspector.Reflection {
Attributes |= TypeAttributes.Interface; Attributes |= TypeAttributes.Interface;
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference 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) {
enumUnderlyingTypeReference = Definition.elementTypeIndex; IsEnum = true;
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, 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
implementedInterfaceReferences = new int[Definition.interfaces_count]; implementedInterfaceReferences = new TypeRef[Definition.interfaces_count];
for (var i = 0; i < Definition.interfaces_count; i++) for (var i = 0; i < Definition.interfaces_count; i++)
implementedInterfaceReferences[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i]; implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, 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];

View File

@@ -0,0 +1,33 @@
namespace Il2CppInspector.Reflection
{
/// <summary>
/// A class which lazily refers to a TypeInfo instance
/// </summary>
internal class TypeRef {
private Il2CppModel model;
private int referenceIndex = -1;
private int definitionIndex = -1;
private TypeInfo typeInfo = null;
private TypeRef() { }
public TypeInfo Value {
get {
if (referenceIndex != -1)
return model.TypesByReferenceIndex[referenceIndex];
if (definitionIndex != -1)
return model.TypesByDefinitionIndex[definitionIndex];
return typeInfo;
}
}
public static TypeRef FromReferenceIndex(Il2CppModel model, int index)
=> new TypeRef { model = model, referenceIndex = index };
public static TypeRef FromDefinitionIndex(Il2CppModel model, int index)
=> new TypeRef { model = model, definitionIndex = index };
public static TypeRef FromTypeInfo(TypeInfo type)
=> new TypeRef { typeInfo = type };
}
}