diff --git a/Il2CppInspector.Common/Reflection/Il2CppModel.cs b/Il2CppInspector.Common/Reflection/Il2CppModel.cs index 6ae57fd..265beec 100644 --- a/Il2CppInspector.Common/Reflection/Il2CppModel.cs +++ b/Il2CppInspector.Common/Reflection/Il2CppModel.cs @@ -22,6 +22,9 @@ namespace Il2CppInspector.Reflection // List of all types from TypeRefs ordered by instanceIndex public TypeInfo[] TypesByReferenceIndex { get; } + // List of all types from GenericParameters + public TypeInfo[] GenericParameterTypes { get; } + // List of all methods from MethodSpecs (closed generic methods that can be called; does not need to be in a generic class) public Dictionary GenericMethods { get; } = new Dictionary(); @@ -64,6 +67,7 @@ namespace Il2CppInspector.Reflection Package = package; TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count]; + GenericParameterTypes = new TypeInfo[package.GenericParameters.Length]; MethodsByDefinitionIndex = new MethodBase[package.Methods.Length]; MethodInvokers = new MethodInvoker[package.MethodInvokePointers.Length]; @@ -168,6 +172,25 @@ namespace Il2CppInspector.Reflection return referencedType; } + public TypeInfo GetGenericParameterType(int index) { + if (GenericParameterTypes[index] != null) + return GenericParameterTypes[index]; + + var paramType = Package.GenericParameters[index]; // genericParameterIndex + var container = Package.GenericContainers[paramType.ownerIndex]; + TypeInfo result; + + if (container.is_method == 1) { + var owner = MethodsByDefinitionIndex[container.ownerIndex]; + result = new TypeInfo(owner, paramType); + } else { + var owner = TypesByDefinitionIndex[container.ownerIndex]; + result = new TypeInfo(owner, paramType); + } + GenericParameterTypes[index] = result; + return result; + } + // The attribute index is an index into AttributeTypeRanges, each of which is a start-end range index into AttributeTypeIndices, each of which is a TypeIndex public int GetCustomAttributeIndex(Assembly asm, uint token, int customAttributeIndex) { // Prior to v24.1, Type, Field, Parameter, Method, Event, Property, Assembly definitions had their own customAttributeIndex field diff --git a/Il2CppInspector.Common/Reflection/MethodBase.cs b/Il2CppInspector.Common/Reflection/MethodBase.cs index 10b245b..f301ba4 100644 --- a/Il2CppInspector.Common/Reflection/MethodBase.cs +++ b/Il2CppInspector.Common/Reflection/MethodBase.cs @@ -104,8 +104,8 @@ namespace Il2CppInspector.Reflection // Store the generic type parameters for later instantiation var container = pkg.GenericContainers[Definition.genericContainerIndex]; - - genericArguments = pkg.GenericParameters.Skip((int)container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToArray(); + genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc) + .Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray(); } // Set method attributes diff --git a/Il2CppInspector.Common/Reflection/TypeInfo.cs b/Il2CppInspector.Common/Reflection/TypeInfo.cs index 5d7d179..561ea2c 100644 --- a/Il2CppInspector.Common/Reflection/TypeInfo.cs +++ b/Il2CppInspector.Common/Reflection/TypeInfo.cs @@ -33,10 +33,21 @@ namespace Il2CppInspector.Reflection { } + // Cached derived types + private Dictionary generatedArrayTypes = new Dictionary(); + private TypeInfo generatedByRefType; + private TypeInfo generatedPointerType; // This property exposes all types which have been generated directly from this one. public IEnumerable CachedGeneratedTypes { get { - return genericTypeInstances?.Values ?? Enumerable.Empty(); + IEnumerable result = generatedArrayTypes.Values; + if (genericTypeInstances != null) + result = result.Concat(genericTypeInstances.Values); + if (generatedByRefType != null) + result = result.Append(generatedByRefType); + if (generatedPointerType != null) + result = result.Append(generatedPointerType); + return result; } } @@ -590,6 +601,9 @@ namespace Il2CppInspector.Reflection { MemberType |= MemberTypes.NestedType; } + // Add to global type definition list + Assembly.Model.TypesByDefinitionIndex[Index] = this; + // Generic type definition? if (Definition.genericContainerIndex >= 0) { IsGenericType = true; @@ -598,13 +612,13 @@ namespace Il2CppInspector.Reflection { // Store the generic type parameters for later instantiation var container = pkg.GenericContainers[Definition.genericContainerIndex]; - genericArguments = pkg.GenericParameters.Skip((int) container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToArray(); + genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc) + .Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray(); genericTypeInstances = new Dictionary(new TypeArgumentsComparer()); genericTypeInstances[genericArguments] = this; } - // Add to global type definition list - Assembly.Model.TypesByDefinitionIndex[Index] = this; + // TODO: Move this to after we've populated TypesByReferenceIndex, since FullName might touch that Assembly.Model.TypesByFullName[FullName] = this; if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0) @@ -749,30 +763,21 @@ namespace Il2CppInspector.Reflection { case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: var descriptor = image.ReadMappedObject(typeRef.datapoint); var elementType = model.GetTypeFromVirtualAddress(descriptor.etype); - underlyingType = new TypeInfo(elementType, descriptor.rank); + underlyingType = elementType.MakeArrayType(descriptor.rank); break; case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: elementType = model.GetTypeFromVirtualAddress(typeRef.datapoint); - underlyingType = new TypeInfo(elementType, 1); + underlyingType = elementType.MakeArrayType(1); break; case Il2CppTypeEnum.IL2CPP_TYPE_PTR: elementType = model.GetTypeFromVirtualAddress(typeRef.datapoint); - underlyingType = new TypeInfo(elementType, isPointer: true); + underlyingType = elementType.MakePointerType(); break; // Generic type and generic method parameters case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: - var paramType = model.Package.GenericParameters[typeRef.datapoint]; // genericParameterIndex - var container = model.Package.GenericContainers[paramType.ownerIndex]; - - if(container.is_method == 1) { - var owner = model.MethodsByDefinitionIndex[container.ownerIndex]; - underlyingType = new TypeInfo(owner, paramType); - } else { - var owner = model.TypesByDefinitionIndex[container.ownerIndex]; - underlyingType = new TypeInfo(owner, paramType); - } + underlyingType = model.GetGenericParameterType((int)typeRef.datapoint); break; // Primitive types @@ -892,7 +897,26 @@ namespace Il2CppInspector.Reflection { Name = ElementType.Name; } - public TypeInfo MakeByRefType() => new TypeInfo(this, isPointer: false); + public TypeInfo MakeArrayType(int rank = 1) { + TypeInfo type; + if (generatedArrayTypes.TryGetValue(rank, out type)) + return type; + type = new TypeInfo(this, rank); + generatedArrayTypes[rank] = type; + return type; + } + + public TypeInfo MakeByRefType() { + if (generatedByRefType == null) + generatedByRefType = new TypeInfo(this, isPointer: false); + return generatedByRefType; + } + + public TypeInfo MakePointerType() { + if (generatedPointerType == null) + generatedPointerType = new TypeInfo(this, isPointer: true); + return generatedPointerType; + } // Get all the other types directly referenced by this type (single level depth; no recursion) public List GetAllTypeReferences() {