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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
33
Il2CppInspector.Common/Reflection/TypeRef.cs
Normal file
33
Il2CppInspector.Common/Reflection/TypeRef.cs
Normal 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 };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user