Add events, fields and properties to concrete generics.

This basically finishes the concrete generics implementation. We can now
enumerate all members of a concrete generic type with full type
substitution implemented.

Also add a simple test to verify that we can obtain the correct type for
a field of a concrete generic type.
This commit is contained in:
Robert Xiao
2020-04-13 06:45:04 -07:00
committed by Katy
parent 1a12567227
commit 4207464208
5 changed files with 98 additions and 15 deletions

View File

@@ -15,12 +15,14 @@ namespace Il2CppInspector.Reflection
// IL2CPP-specific data // IL2CPP-specific data
public Il2CppEventDefinition Definition { get; } public Il2CppEventDefinition Definition { get; }
public int Index { get; } public int Index { get; }
// Root definition: the event with Definition != null
protected readonly EventInfo rootDefinition;
// Information/flags about the event // Information/flags about the event
public EventAttributes Attributes { get; } public EventAttributes Attributes { get; }
// Custom attributes for this member // Custom attributes for this member
public override IEnumerable<CustomAttributeData> CustomAttributes => CustomAttributeData.GetCustomAttributes(this); public override IEnumerable<CustomAttributeData> CustomAttributes => CustomAttributeData.GetCustomAttributes(rootDefinition);
// Methods for the event // Methods for the event
public MethodInfo AddMethod { get; } public MethodInfo AddMethod { get; }
@@ -41,6 +43,7 @@ namespace Il2CppInspector.Reflection
Definition = pkg.Events[eventIndex]; Definition = pkg.Events[eventIndex];
Index = eventIndex; Index = eventIndex;
Name = pkg.Strings[Definition.nameIndex]; Name = pkg.Strings[Definition.nameIndex];
rootDefinition = this;
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex); eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var eventType = pkg.TypeReferences[Definition.typeIndex]; var eventType = pkg.TypeReferences[Definition.typeIndex];
@@ -57,5 +60,17 @@ namespace Il2CppInspector.Reflection
if (Definition.raise >= 0) if (Definition.raise >= 0)
RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.raise); RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.raise);
} }
public EventInfo(EventInfo eventDef, TypeInfo declaringType) : base(declaringType) {
rootDefinition = eventDef;
Name = eventDef.Name;
Attributes = eventDef.Attributes;
eventTypeReference = TypeRef.FromTypeInfo(eventDef.EventHandlerType.SubstituteGenericArguments(declaringType.GetGenericArguments()));
AddMethod = declaringType.GetMethodByDefinition(eventDef.AddMethod);
RemoveMethod = declaringType.GetMethodByDefinition(eventDef.RemoveMethod);
RaiseMethod = declaringType.GetMethodByDefinition(eventDef.RaiseMethod);
}
} }
} }

View File

@@ -4,6 +4,7 @@
All rights reserved. All rights reserved.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -15,6 +16,8 @@ namespace Il2CppInspector.Reflection {
// IL2CPP-specific data // IL2CPP-specific data
public Il2CppFieldDefinition Definition { get; } public Il2CppFieldDefinition Definition { get; }
public int Index { get; } public int Index { get; }
// Root definition: the field with Definition != null
protected readonly FieldInfo rootDefinition;
// Offsets for reference types start at 0x8 or 0x10 due to Il2CppObject "header" containing 2 pointers // Offsets for reference types start at 0x8 or 0x10 due to Il2CppObject "header" containing 2 pointers
// Value types don't have this header but the offsets are still stored as starting at 0x8 or 0x10, so we have to subtract this // Value types don't have this header but the offsets are still stored as starting at 0x8 or 0x10, so we have to subtract this
@@ -26,7 +29,7 @@ namespace Il2CppInspector.Reflection {
public ulong DefaultValueMetadataAddress { get; } public ulong DefaultValueMetadataAddress { get; }
// Custom attributes for this member // Custom attributes for this member
public override IEnumerable<CustomAttributeData> CustomAttributes => CustomAttributeData.GetCustomAttributes(this); public override IEnumerable<CustomAttributeData> CustomAttributes => CustomAttributeData.GetCustomAttributes(rootDefinition);
public bool HasDefaultValue => (Attributes & FieldAttributes.HasDefault) != 0; public bool HasDefaultValue => (Attributes & FieldAttributes.HasDefault) != 0;
public object DefaultValue { get; } public object DefaultValue { get; }
@@ -89,6 +92,8 @@ namespace Il2CppInspector.Reflection {
rawOffset = pkg.FieldOffsets[fieldIndex]; rawOffset = pkg.FieldOffsets[fieldIndex];
rootDefinition = this;
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex); fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var fieldType = pkg.TypeReferences[Definition.typeIndex]; var fieldType = pkg.TypeReferences[Definition.typeIndex];
@@ -126,6 +131,20 @@ namespace Il2CppInspector.Reflection {
} }
} }
public FieldInfo(FieldInfo fieldDef, TypeInfo declaringType) : base(declaringType) {
if (fieldDef.Definition == null)
throw new ArgumentException("Argument must be a bare field definition");
rootDefinition = fieldDef;
Name = fieldDef.Name;
Attributes = fieldDef.Attributes;
fieldTypeReference = TypeRef.FromTypeInfo(fieldDef.FieldType.SubstituteGenericArguments(declaringType.GetGenericArguments()));
DefaultValue = fieldDef.DefaultValue;
DefaultValueMetadataAddress = fieldDef.DefaultValueMetadataAddress;
}
public string GetAccessModifierString() => this switch { public string GetAccessModifierString() => this switch {
{ IsPrivate: true } => "private ", { IsPrivate: true } => "private ",
{ IsPublic: true } => "public ", { IsPublic: true } => "public ",

View File

@@ -15,12 +15,14 @@ namespace Il2CppInspector.Reflection {
// IL2CPP-specific data // IL2CPP-specific data
public Il2CppPropertyDefinition Definition { get; } public Il2CppPropertyDefinition Definition { get; }
public int Index { get; } public int Index { get; }
// Root definition: the property with Definition != null
protected readonly PropertyInfo rootDefinition;
public bool CanRead => GetMethod != null; public bool CanRead => GetMethod != null;
public bool CanWrite => SetMethod != null; public bool CanWrite => SetMethod != null;
// Custom attributes for this member // Custom attributes for this member
public override IEnumerable<CustomAttributeData> CustomAttributes => CustomAttributeData.GetCustomAttributes(this); public override IEnumerable<CustomAttributeData> CustomAttributes => CustomAttributeData.GetCustomAttributes(rootDefinition);
public MethodInfo GetMethod { get; } public MethodInfo GetMethod { get; }
public MethodInfo SetMethod { get; } public MethodInfo SetMethod { get; }
@@ -50,6 +52,7 @@ namespace Il2CppInspector.Reflection {
Index = propIndex; Index = propIndex;
Definition = pkg.Properties[propIndex]; Definition = pkg.Properties[propIndex];
Name = pkg.Strings[Definition.nameIndex]; Name = pkg.Strings[Definition.nameIndex];
rootDefinition = this;
// prop.get and prop.set are method indices from the first method of the declaring type // prop.get and prop.set are method indices from the first method of the declaring type
if (Definition.get >= 0) if (Definition.get >= 0)
@@ -63,10 +66,19 @@ namespace Il2CppInspector.Reflection {
base(declaringType) { base(declaringType) {
Index = -1; Index = -1;
Definition = null; Definition = null;
rootDefinition = this;
Name = (getter ?? setter).Name.Replace(".get_", ".").Replace(".set_", "."); Name = (getter ?? setter).Name.Replace(".get_", ".").Replace(".set_", ".");
GetMethod = getter; GetMethod = getter;
SetMethod = setter; SetMethod = setter;
} }
public PropertyInfo(PropertyInfo propertyDef, TypeInfo declaringType) : base(declaringType) {
rootDefinition = propertyDef;
Name = propertyDef.Name;
GetMethod = declaringType.GetMethodByDefinition(propertyDef.GetMethod);
SetMethod = declaringType.GetMethodByDefinition(propertyDef.SetMethod);
}
} }
} }

View File

@@ -109,8 +109,33 @@ namespace Il2CppInspector.Reflection {
} }
} }
public List<EventInfo> DeclaredEvents { get; } = new List<EventInfo>(); private List<EventInfo> declaredEvents;
public List<FieldInfo> DeclaredFields { get; } = new List<FieldInfo>(); public ReadOnlyCollection<EventInfo> DeclaredEvents {
get {
if (declaredEvents != null)
return declaredEvents.AsReadOnly();
if (genericTypeDefinition != null) {
var result = genericTypeDefinition.DeclaredEvents.Select(c => new EventInfo(c, this)).ToList();
declaredEvents = result;
return result.AsReadOnly();
}
return new List<EventInfo>().AsReadOnly();
}
}
private List<FieldInfo> declaredFields;
public ReadOnlyCollection<FieldInfo> DeclaredFields {
get {
if (declaredFields != null)
return declaredFields.AsReadOnly();
if (genericTypeDefinition != null) {
var result = genericTypeDefinition.DeclaredFields.Select(c => new FieldInfo(c, this)).ToList();
declaredFields = result;
return result.AsReadOnly();
}
return new List<FieldInfo>().AsReadOnly();
}
}
public List<MemberInfo> DeclaredMembers => new IEnumerable<MemberInfo>[] { public List<MemberInfo> DeclaredMembers => new IEnumerable<MemberInfo>[] {
DeclaredConstructors, DeclaredEvents, DeclaredFields, DeclaredMethods, DeclaredConstructors, DeclaredEvents, DeclaredFields, DeclaredMethods,
@@ -147,7 +172,19 @@ namespace Il2CppInspector.Reflection {
} }
} }
public List<PropertyInfo> DeclaredProperties { get; } = new List<PropertyInfo>(); private List<PropertyInfo> declaredProperties;
public ReadOnlyCollection<PropertyInfo> DeclaredProperties {
get {
if (declaredProperties != null)
return declaredProperties.AsReadOnly();
if (genericTypeDefinition != null) {
var result = genericTypeDefinition.DeclaredProperties.Select(c => new PropertyInfo(c, this)).ToList();
declaredProperties = result;
return result.AsReadOnly();
}
return new List<PropertyInfo>().AsReadOnly();
}
}
// Get a field by its name // Get a field by its name
public FieldInfo GetField(string name) => DeclaredFields.FirstOrDefault(f => f.Name == name); public FieldInfo GetField(string name) => DeclaredFields.FirstOrDefault(f => f.Name == name);
@@ -737,8 +774,9 @@ namespace Il2CppInspector.Reflection {
declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.nestedTypesStart + n]); declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.nestedTypesStart + n]);
// Add all fields // Add all fields
declaredFields = new List<FieldInfo>();
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));
// Add all methods // Add all methods
declaredConstructors = new List<ConstructorInfo>(); declaredConstructors = new List<ConstructorInfo>();
@@ -752,8 +790,9 @@ namespace Il2CppInspector.Reflection {
} }
// Add all properties // Add all properties
declaredProperties = new List<PropertyInfo>();
for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++) for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++)
DeclaredProperties.Add(new PropertyInfo(pkg, p, this)); declaredProperties.Add(new PropertyInfo(pkg, p, this));
// There are rare cases when explicitly implemented interface properties // There are rare cases when explicitly implemented interface properties
// are only given as methods in the metadata. Find these and add them as properties // are only given as methods in the metadata. Find these and add them as properties
@@ -787,11 +826,12 @@ namespace Il2CppInspector.Reflection {
} }
foreach (var prop in pairedEip) foreach (var prop in pairedEip)
DeclaredProperties.Add(new PropertyInfo(prop.get, prop.set, this)); declaredProperties.Add(new PropertyInfo(prop.get, prop.set, this));
// Add all events // Add all events
declaredEvents = new List<EventInfo>();
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));
} }
// Initialize a type from a concrete generic instance // Initialize a type from a concrete generic instance
@@ -816,11 +856,6 @@ namespace Il2CppInspector.Reflection {
IsGenericType = true; IsGenericType = true;
genericArguments = genericArgs; genericArguments = genericArgs;
/* TODO: This is a bare definition at the moment. We need to iterate over all the members of genericTypeDefinition
* and replace the matching generic type parameters with our concrete type parameters,
* as well as setting the various TypeInfo properties here
*/
} }
// Substitutes the elements of an array of types for the type parameters of the current generic type definition // Substitutes the elements of an array of types for the type parameters of the current generic type definition

View File

@@ -41,6 +41,7 @@ namespace Il2CppInspector
TypeInfo tT = tBase.GenericTypeParameters[0]; TypeInfo tT = tBase.GenericTypeParameters[0];
TypeInfo tU = tBase.GenericTypeParameters[1]; TypeInfo tU = tBase.GenericTypeParameters[1];
TypeInfo tF = tDerived.GetField("F").FieldType; TypeInfo tF = tDerived.GetField("F").FieldType;
TypeInfo tF_closed = tDerived_closed.GetField("F").FieldType;
TypeInfo tNested = asm.GetType("Il2CppTests.TestSources.Derived`1+Nested"); TypeInfo tNested = asm.GetType("Il2CppTests.TestSources.Derived`1+Nested");
TypeInfo tNG = asm.GetType("Il2CppTests.TestSources.NonGeneric"); TypeInfo tNG = asm.GetType("Il2CppTests.TestSources.NonGeneric");
@@ -87,6 +88,7 @@ namespace Il2CppInspector
(tT, "T", false, false, true, true, 0), (tT, "T", false, false, true, true, 0),
(tU, "U", false, false, true, true, 1), (tU, "U", false, false, true, true, 1),
(tF, "G`1[Derived`1[V]]", true, false, true, false, -1), (tF, "G`1[Derived`1[V]]", true, false, true, false, -1),
(tF_closed, "G`1[Derived`1[System.Int32]]", true, false, false, false, -1),
(tNested, "Derived`1[V]+Nested[V]", true, true, true, false, -1), (tNested, "Derived`1[V]+Nested[V]", true, true, true, false, -1),
(tB, "B", false, false, true, true, 0), (tB, "B", false, false, true, true, 0),
(tB.BaseType, "Derived`1[R]", true, false, true, false, -1), (tB.BaseType, "Derived`1[R]", true, false, true, false, -1),