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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 ",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace Il2CppInspector
|
||||
TypeInfo tT = tBase.GenericTypeParameters[0];
|
||||
TypeInfo tU = tBase.GenericTypeParameters[1];
|
||||
TypeInfo tF = tDerived.GetField("F").FieldType;
|
||||
TypeInfo tF_closed = tDerived_closed.GetField("F").FieldType;
|
||||
TypeInfo tNested = asm.GetType("Il2CppTests.TestSources.Derived`1+Nested");
|
||||
|
||||
TypeInfo tNG = asm.GetType("Il2CppTests.TestSources.NonGeneric");
|
||||
@@ -87,6 +88,7 @@ namespace Il2CppInspector
|
||||
(tT, "T", false, false, true, true, 0),
|
||||
(tU, "U", false, false, true, true, 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),
|
||||
(tB, "B", false, false, true, true, 0),
|
||||
(tB.BaseType, "Derived`1[R]", true, false, true, false, -1),
|
||||
|
||||
Reference in New Issue
Block a user