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
public Il2CppEventDefinition Definition { get; }
public int Index { get; }
// Root definition: the event with Definition != null
protected readonly EventInfo rootDefinition;
// Information/flags about the event
public EventAttributes Attributes { get; }
// 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
public MethodInfo AddMethod { get; }
@@ -41,6 +43,7 @@ namespace Il2CppInspector.Reflection
Definition = pkg.Events[eventIndex];
Index = eventIndex;
Name = pkg.Strings[Definition.nameIndex];
rootDefinition = this;
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var eventType = pkg.TypeReferences[Definition.typeIndex];
@@ -57,5 +60,17 @@ namespace Il2CppInspector.Reflection
if (Definition.raise >= 0)
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.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -15,6 +16,8 @@ namespace Il2CppInspector.Reflection {
// IL2CPP-specific data
public Il2CppFieldDefinition Definition { 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
// 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; }
// 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 object DefaultValue { get; }
@@ -89,6 +92,8 @@ namespace Il2CppInspector.Reflection {
rawOffset = pkg.FieldOffsets[fieldIndex];
rootDefinition = this;
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, 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 {
{ IsPrivate: true } => "private ",
{ IsPublic: true } => "public ",

View File

@@ -15,12 +15,14 @@ namespace Il2CppInspector.Reflection {
// IL2CPP-specific data
public Il2CppPropertyDefinition Definition { get; }
public int Index { get; }
// Root definition: the property with Definition != null
protected readonly PropertyInfo rootDefinition;
public bool CanRead => GetMethod != null;
public bool CanWrite => SetMethod != null;
// 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 SetMethod { get; }
@@ -50,6 +52,7 @@ namespace Il2CppInspector.Reflection {
Index = propIndex;
Definition = pkg.Properties[propIndex];
Name = pkg.Strings[Definition.nameIndex];
rootDefinition = this;
// prop.get and prop.set are method indices from the first method of the declaring type
if (Definition.get >= 0)
@@ -63,10 +66,19 @@ namespace Il2CppInspector.Reflection {
base(declaringType) {
Index = -1;
Definition = null;
rootDefinition = this;
Name = (getter ?? setter).Name.Replace(".get_", ".").Replace(".set_", ".");
GetMethod = getter;
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>();
public List<FieldInfo> DeclaredFields { get; } = new List<FieldInfo>();
private List<EventInfo> declaredEvents;
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>[] {
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
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]);
// Add all fields
declaredFields = new List<FieldInfo>();
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
declaredConstructors = new List<ConstructorInfo>();
@@ -752,8 +790,9 @@ namespace Il2CppInspector.Reflection {
}
// Add all properties
declaredProperties = new List<PropertyInfo>();
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
// 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)
DeclaredProperties.Add(new PropertyInfo(prop.get, prop.set, this));
declaredProperties.Add(new PropertyInfo(prop.get, prop.set, this));
// Add all events
declaredEvents = new List<EventInfo>();
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
@@ -816,11 +856,6 @@ namespace Il2CppInspector.Reflection {
IsGenericType = true;
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