diff --git a/Il2CppInspector.Common/Reflection/EventInfo.cs b/Il2CppInspector.Common/Reflection/EventInfo.cs index 0adb09e..3a7b9cf 100644 --- a/Il2CppInspector.Common/Reflection/EventInfo.cs +++ b/Il2CppInspector.Common/Reflection/EventInfo.cs @@ -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 CustomAttributes => CustomAttributeData.GetCustomAttributes(this); + public override IEnumerable 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); + } } } \ No newline at end of file diff --git a/Il2CppInspector.Common/Reflection/FieldInfo.cs b/Il2CppInspector.Common/Reflection/FieldInfo.cs index 4560704..22bbbf3 100644 --- a/Il2CppInspector.Common/Reflection/FieldInfo.cs +++ b/Il2CppInspector.Common/Reflection/FieldInfo.cs @@ -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 CustomAttributes => CustomAttributeData.GetCustomAttributes(this); + public override IEnumerable 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 ", diff --git a/Il2CppInspector.Common/Reflection/PropertyInfo.cs b/Il2CppInspector.Common/Reflection/PropertyInfo.cs index ab1d519..4c770fb 100644 --- a/Il2CppInspector.Common/Reflection/PropertyInfo.cs +++ b/Il2CppInspector.Common/Reflection/PropertyInfo.cs @@ -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 CustomAttributes => CustomAttributeData.GetCustomAttributes(this); + public override IEnumerable 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); + } } } \ No newline at end of file diff --git a/Il2CppInspector.Common/Reflection/TypeInfo.cs b/Il2CppInspector.Common/Reflection/TypeInfo.cs index 7e0f894..495fec2 100644 --- a/Il2CppInspector.Common/Reflection/TypeInfo.cs +++ b/Il2CppInspector.Common/Reflection/TypeInfo.cs @@ -109,8 +109,33 @@ namespace Il2CppInspector.Reflection { } } - public List DeclaredEvents { get; } = new List(); - public List DeclaredFields { get; } = new List(); + private List declaredEvents; + public ReadOnlyCollection 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().AsReadOnly(); + } + } + + private List declaredFields; + public ReadOnlyCollection 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().AsReadOnly(); + } + } public List DeclaredMembers => new IEnumerable[] { DeclaredConstructors, DeclaredEvents, DeclaredFields, DeclaredMethods, @@ -147,7 +172,19 @@ namespace Il2CppInspector.Reflection { } } - public List DeclaredProperties { get; } = new List(); + private List declaredProperties; + public ReadOnlyCollection 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().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(); 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(); @@ -752,8 +790,9 @@ namespace Il2CppInspector.Reflection { } // Add all properties + declaredProperties = new List(); 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(); 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 diff --git a/Il2CppTests/TestGenerics.cs b/Il2CppTests/TestGenerics.cs index 3c925fc..06533e3 100644 --- a/Il2CppTests/TestGenerics.cs +++ b/Il2CppTests/TestGenerics.cs @@ -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),