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; }
// Event handler delegate type
private int eventTypeReference;
public TypeInfo EventHandlerType => Assembly.Model.TypesByReferenceIndex[eventTypeReference];
private readonly TypeRef eventTypeReference;
public TypeInfo EventHandlerType => eventTypeReference.Value;
// True if the event has a special name
public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) == EventAttributes.SpecialName;
@@ -42,8 +42,8 @@ namespace Il2CppInspector.Reflection
Index = eventIndex;
Name = pkg.Strings[Definition.nameIndex];
eventTypeReference = Definition.typeIndex;
var eventType = pkg.TypeReferences[eventTypeReference];
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var eventType = pkg.TypeReferences[Definition.typeIndex];
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 int fieldTypeReference;
public TypeInfo FieldType => Assembly.Model.TypesByReferenceIndex[fieldTypeReference];
private readonly TypeRef fieldTypeReference;
public TypeInfo FieldType => fieldTypeReference.Value;
// 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
@@ -89,8 +89,8 @@ namespace Il2CppInspector.Reflection {
rawOffset = pkg.FieldOffsets[fieldIndex];
fieldTypeReference = Definition.typeIndex;
var fieldType = pkg.TypeReferences[fieldTypeReference];
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var fieldType = pkg.TypeReferences[Definition.typeIndex];
if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PRIVATE)
Attributes |= FieldAttributes.Private;

View File

@@ -17,8 +17,8 @@ namespace Il2CppInspector.Reflection
public ParameterInfo ReturnParameter { get; }
// Return type of the method
private readonly int returnTypeReference;
public TypeInfo ReturnType => Assembly.Model.TypesByReferenceIndex[returnTypeReference];
private readonly TypeRef returnTypeReference;
public TypeInfo ReturnType => returnTypeReference.Value;
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) {
// Add return parameter
returnTypeReference = Definition.returnType;
returnTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.returnType);
ReturnParameter = new ParameterInfo(pkg, -1, this);
}
public MethodInfo(Il2CppModel model, Il2CppMethodSpec spec, TypeInfo declaringType) : base(model, spec, declaringType) {
var methodDef = model.MethodsByDefinitionIndex[spec.methodDefinitionIndex];
// Add return parameter
returnTypeReference = methodDef.Definition.returnType;
// Add return parameter (TODO substitute type)
returnTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, methodDef.Definition.returnType);
ReturnParameter = ((MethodInfo) methodDef).ReturnParameter;
}

View File

@@ -43,8 +43,8 @@ namespace Il2CppInspector.Reflection
public string CSharpSafeName => Constants.Keywords.Contains(Name) ? "@" + Name : Name;
// Type of this parameter
private readonly int paramTypeReference;
public TypeInfo ParameterType => DeclaringMethod.Assembly.Model.TypesByReferenceIndex[paramTypeReference];
private readonly TypeRef paramTypeReference;
public TypeInfo ParameterType => paramTypeReference.Value;
// Zero-indexed position of the parameter in parameter list
public int Position { get; }
@@ -56,7 +56,7 @@ namespace Il2CppInspector.Reflection
if (paramIndex == -1) {
Position = -1;
paramTypeReference = declaringMethod.Definition.returnType;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.returnType);
Attributes |= ParameterAttributes.Retval;
return;
}
@@ -69,8 +69,9 @@ namespace Il2CppInspector.Reflection
Name = string.Format($"param_{Index:x8}");
Position = paramIndex - declaringMethod.Definition.parameterStart;
paramTypeReference = Definition.typeIndex;
var paramType = pkg.TypeReferences[paramTypeReference];
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.typeIndex);
var paramType = pkg.TypeReferences[Definition.typeIndex];
if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_HAS_DEFAULT) != 0)
Attributes |= ParameterAttributes.HasDefault;
@@ -103,8 +104,8 @@ namespace Il2CppInspector.Reflection
Position = generic.Position;
Attributes = generic.Attributes;
// Search for the concrete type's TypeRef index to store as the parameter type reference index
paramTypeReference = Array.IndexOf(model.TypesByReferenceIndex, concrete);
// TODO substitute concrete params?
paramTypeReference = TypeRef.FromTypeInfo(concrete);
DefaultValue = generic.DefaultValue;
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
public bool HasElementType => ElementType != null;
private readonly int[] implementedInterfaceReferences;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceReferences.Select(x => Assembly.Model.TypesByReferenceIndex[x]);
private readonly TypeRef[] implementedInterfaceReferences;
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceReferences.Select(x => x.Value);
public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
public bool IsArray { get; }
public bool IsByRef { get; }
public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class;
public bool IsEnum => enumUnderlyingTypeReference != -1;
public bool IsEnum { get; }
public bool IsGenericParameter { get; }
public bool IsGenericType { get; }
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");
// The underlying type of an enumeration (int by default)
private readonly int enumUnderlyingTypeReference = -1;
private TypeInfo enumUnderlyingType;
private readonly TypeRef enumUnderlyingTypeReference = null;
public TypeInfo GetEnumUnderlyingType() {
if (!IsEnum)
return null;
enumUnderlyingType ??= Assembly.Model.TypesByReferenceIndex[enumUnderlyingTypeReference];
return enumUnderlyingType;
return enumUnderlyingTypeReference.Value;
}
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;
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum)
if (((Definition.bitfield >> 1) & 1) == 1)
enumUnderlyingTypeReference = Definition.elementTypeIndex;
if (((Definition.bitfield >> 1) & 1) == 1) {
IsEnum = true;
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.elementTypeIndex);
}
// Pass-by-reference type
// NOTE: This should actually always evaluate to false in the current implementation
IsByRef = Index == Definition.byrefTypeIndex;
// 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++)
implementedInterfaceReferences[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i];
implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.interfacesStart + i]);
// Add all nested types
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 };
}
}