Model: Introduce TypeInfo.GetGenericArguments(), refactor handling

This commit is contained in:
Katy Coe
2020-01-29 10:14:41 +01:00
parent 871c368e49
commit a338196057
2 changed files with 39 additions and 48 deletions

View File

@@ -555,12 +555,11 @@ namespace Il2CppInspector
sb.Append($" // TypeDefIndex: {type.Index}"); sb.Append($" // TypeDefIndex: {type.Index}");
sb.Append("\n"); sb.Append("\n");
if (type.GenericTypeParameters != null) foreach (var gp in type.GetGenericArguments()) {
foreach (var gp in type.GenericTypeParameters) { var constraint = gp.GetTypeConstraintsString(scope);
var constraint = gp.GetTypeConstraintsString(scope); if (constraint != string.Empty)
if (constraint != string.Empty) sb.Append($"{prefix}\t{constraint}\n");
sb.Append($"{prefix}\t{constraint}\n"); }
}
sb.Append(prefix + "{\n"); sb.Append(prefix + "{\n");

View File

@@ -47,11 +47,9 @@ namespace Il2CppInspector.Reflection {
var n = (i != -1 ? Il2CppConstants.CSharpTypeString[i] : base.Name); var n = (i != -1 ? Il2CppConstants.CSharpTypeString[i] : base.Name);
if (n?.IndexOf("`", StringComparison.Ordinal) != -1) if (n?.IndexOf("`", StringComparison.Ordinal) != -1)
n = n?.Remove(n.IndexOf("`", StringComparison.Ordinal)); n = n?.Remove(n.IndexOf("`", StringComparison.Ordinal));
var g = (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.CSharpName)) + ">" : ""); n += (GetGenericArguments().Any()? "<" + string.Join(", ", GetGenericArguments().Select(x => x.CSharpName)) + ">" : "");
g = (GenericTypeArguments != null ? "<" + string.Join(", ", GenericTypeArguments.Select(x => x.CSharpName)) + ">" : g); if (s == "System.Nullable`1" && GetGenericArguments().Any())
n += g; n = GetGenericArguments()[0].CSharpName + "?";
if (s == "System.Nullable`1" && GenericTypeArguments.Any())
n = GenericTypeArguments[0].CSharpName + "?";
if (HasElementType) if (HasElementType)
n = ElementType.CSharpName; n = ElementType.CSharpName;
if ((GenericParameterAttributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant) if ((GenericParameterAttributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant)
@@ -67,7 +65,7 @@ namespace Il2CppInspector.Reflection {
// C# name as it would be written in a type declaration // C# name as it would be written in a type declaration
public string CSharpTypeDeclarationName { public string CSharpTypeDeclarationName {
get { get {
var gtp = IsNested? GenericTypeParameters?.Where(p => DeclaringType.GenericTypeParameters?.All(dp => dp.Name != p.Name) ?? true) : GenericTypeParameters; var ga = IsNested ? GetGenericArguments().Where(p => DeclaringType.GetGenericArguments().All(dp => dp.Name != p.Name)) : GetGenericArguments();
return (IsByRef ? "ref " : "") return (IsByRef ? "ref " : "")
+ (HasElementType + (HasElementType
@@ -75,8 +73,7 @@ namespace Il2CppInspector.Reflection {
: ((GenericParameterAttributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant ? "in " : "") : ((GenericParameterAttributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant ? "in " : "")
+ ((GenericParameterAttributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant ? "out " : "") + ((GenericParameterAttributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant ? "out " : "")
+ (base.Name.IndexOf("`", StringComparison.Ordinal) == -1 ? base.Name : base.Name.Remove(base.Name.IndexOf("`", StringComparison.Ordinal))) + (base.Name.IndexOf("`", StringComparison.Ordinal) == -1 ? base.Name : base.Name.Remove(base.Name.IndexOf("`", StringComparison.Ordinal)))
+ (gtp?.Any() ?? false? "<" + string.Join(", ", gtp.Select(x => x.CSharpTypeDeclarationName)) + ">" : "") + (ga.Any()? "<" + string.Join(", ", ga.Select(x => (!x.IsGenericTypeParameter ? x.Namespace + "." : "") + x.CSharpTypeDeclarationName)) + ">" : ""))
+ (GenericTypeArguments != null ? "<" + string.Join(", ", GenericTypeArguments.Select(x => (!x.IsGenericTypeParameter ? x.Namespace + "." : "") + x.CSharpTypeDeclarationName)) + ">" : ""))
+ (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "")
+ (IsPointer ? "*" : ""); + (IsPointer ? "*" : "");
} }
@@ -148,6 +145,7 @@ namespace Il2CppInspector.Reflection {
public TypeInfo ElementType { get; } public TypeInfo ElementType { get; }
// Type name including namespace // Type name including namespace
// Fully qualified generic type names from the C# compiler use backtick and arity rather than a list of generic arguments
public string FullName => public string FullName =>
IsGenericParameter? null : IsGenericParameter? null :
HasElementType && ElementType.IsGenericParameter? null : HasElementType && ElementType.IsGenericParameter? null :
@@ -312,8 +310,8 @@ namespace Il2CppInspector.Reflection {
n += "<" + g + ">"; n += "<" + g + ">";
// Nullable types // Nullable types
if (s == "System.Nullable`1" && GenericTypeArguments.Any()) if (s == "System.Nullable`1" && GetGenericArguments().Any())
n = GenericTypeArguments[0].GetScopedCSharpName(usingScope) + "?"; n = GetGenericArguments()[0].GetScopedCSharpName(usingScope) + "?";
// Arrays, pointers, references // Arrays, pointers, references
if (HasElementType) if (HasElementType)
@@ -325,12 +323,11 @@ namespace Il2CppInspector.Reflection {
// Get the generic type parameters for a specific usage of this type based on its scope, // Get the generic type parameters for a specific usage of this type based on its scope,
// or all generic type parameters if no scope specified // or all generic type parameters if no scope specified
private IEnumerable<TypeInfo> getGenericTypeParameters(Scope scope = null) { private IEnumerable<TypeInfo> getGenericTypeParameters(Scope scope = null) {
// Merge generic type parameters and generic type arguments var ga = GetGenericArguments();
var gp = (GenericTypeParameters ?? new List<TypeInfo>()).Concat(GenericTypeArguments ?? new List<TypeInfo>());
// If no scope or empty scope specified, or no type parameters, stop here // If no scope or empty scope specified, or no type parameters, stop here
if (scope?.Current == null || !gp.Any()) if (scope?.Current == null || !ga.Any())
return gp; return ga;
// In order to elide generic type parameters, the using scope must be a parent of the declaring scope // In order to elide generic type parameters, the using scope must be a parent of the declaring scope
// Determine if the using scope is a parent of the declaring scope (always a child if using scope is empty) // Determine if the using scope is a parent of the declaring scope (always a child if using scope is empty)
@@ -340,14 +337,14 @@ namespace Il2CppInspector.Reflection {
usingScopeIsParent = true; usingScopeIsParent = true;
if (!usingScopeIsParent) if (!usingScopeIsParent)
return gp; return ga;
// Get the generic type parameters available in the using scope // Get the generic type parameters available in the using scope
// (no need to recurse because every nested type inherits all of the generic type parameters of all of its ancestors) // (no need to recurse because every nested type inherits all of the generic type parameters of all of its ancestors)
var gpsInScope = (scope.Current.GenericTypeParameters ?? new List<TypeInfo>()).Concat(scope.Current.GenericTypeArguments ?? new List<TypeInfo>()); var gasInScope = scope.Current.GetGenericArguments();
// Return all of the generic type parameters this type uses minus those already in scope // Return all of the generic type parameters this type uses minus those already in scope
return gp.Where(p => gpsInScope.All(pp => pp.Name != p.Name)); return ga.Where(p => gasInScope.All(pp => pp.Name != p.Name));
} }
public GenericParameterAttributes GenericParameterAttributes { get; } public GenericParameterAttributes GenericParameterAttributes { get; }
@@ -360,9 +357,17 @@ namespace Il2CppInspector.Reflection {
private set => genericParameterPosition = value; private set => genericParameterPosition = value;
} }
public List<TypeInfo> GenericTypeParameters { get; } // For a generic type definition: the list of generic type parameters
// For an open generic type: a mix of generic type parameters and generic type arguments
// For a closed generic type: the list of generic type arguments
private readonly List<TypeInfo> genericArguments = new List<TypeInfo>();
public List<TypeInfo> GenericTypeArguments { get; } public List<TypeInfo> GenericTypeParameters => IsGenericTypeDefinition ? genericArguments : new List<TypeInfo>();
public List<TypeInfo> GenericTypeArguments => !IsGenericTypeDefinition ? genericArguments : new List<TypeInfo>();
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.getgenericarguments?view=netframework-4.8
public List<TypeInfo> GetGenericArguments() => genericArguments;
// True if an array, pointer or reference, otherwise false // True if an array, pointer or reference, otherwise false
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.haselementtype?view=netframework-4.8 // See: https://docs.microsoft.com/en-us/dotnet/api/system.type.haselementtype?view=netframework-4.8
@@ -378,7 +383,7 @@ namespace Il2CppInspector.Reflection {
public bool IsEnum => enumUnderlyingTypeUsage != -1; public bool IsEnum => enumUnderlyingTypeUsage != -1;
public bool IsGenericParameter { get; } public bool IsGenericParameter { get; }
public bool IsGenericType { get; } public bool IsGenericType { get; }
public bool IsGenericTypeDefinition { get; } public bool IsGenericTypeDefinition => genericArguments.Any() && genericArguments.All(a => a.IsGenericTypeParameter);
public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import; public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import;
public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
public bool IsNested => (MemberType & MemberTypes.NestedType) == MemberTypes.NestedType; public bool IsNested => (MemberType & MemberTypes.NestedType) == MemberTypes.NestedType;
@@ -454,13 +459,12 @@ namespace Il2CppInspector.Reflection {
if (Definition.genericContainerIndex >= 0) { if (Definition.genericContainerIndex >= 0) {
IsGenericType = true; IsGenericType = true;
IsGenericParameter = false; IsGenericParameter = false;
IsGenericTypeDefinition = true; // All of our generic type parameters are unresolved
ContainsGenericParameters = true; ContainsGenericParameters = true;
// Store the generic type parameters for later instantiation // Store the generic type parameters for later instantiation
var container = pkg.GenericContainers[Definition.genericContainerIndex]; var container = pkg.GenericContainers[Definition.genericContainerIndex];
GenericTypeParameters = pkg.GenericParameters.Skip((int) container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToList(); genericArguments = pkg.GenericParameters.Skip((int) container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToList();
} }
// Add to global type definition list // Add to global type definition list
@@ -572,12 +576,12 @@ namespace Il2CppInspector.Reflection {
DeclaredEvents.Add(new EventInfo(pkg, e, this)); DeclaredEvents.Add(new EventInfo(pkg, e, this));
} }
// Initialize type from binary usage // Initialize type from type reference
// Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType // Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType
public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) { public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) {
var image = model.Package.BinaryImage; var image = model.Package.BinaryImage;
// Generic type unresolved and concrete instance types // Open and closed generic types
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); // Il2CppGenericClass * var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); // Il2CppGenericClass *
var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex]; var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex];
@@ -602,7 +606,6 @@ namespace Il2CppInspector.Reflection {
IsGenericType = true; IsGenericType = true;
IsGenericParameter = false; IsGenericParameter = false;
IsGenericTypeDefinition = false; // This is a use of a generic type definition
ContainsGenericParameters = true; ContainsGenericParameters = true;
// Get the instantiation // Get the instantiation
@@ -614,13 +617,9 @@ namespace Il2CppInspector.Reflection {
// Get list of pointers to type parameters (both unresolved and concrete) // Get list of pointers to type parameters (both unresolved and concrete)
var genericTypeArguments = image.ReadMappedWordArray(genericInstance.type_argv, (int)genericInstance.type_argc); var genericTypeArguments = image.ReadMappedWordArray(genericInstance.type_argv, (int)genericInstance.type_argc);
GenericTypeArguments = new List<TypeInfo>();
foreach (var pArg in genericTypeArguments) { foreach (var pArg in genericTypeArguments) {
var argType = model.GetTypeFromVirtualAddress((ulong) pArg); var argType = model.GetTypeFromVirtualAddress((ulong) pArg);
// TODO: Detect whether unresolved or concrete (add concrete to GenericTypeArguments instead) genericArguments.Add(argType);
// TODO: Assembly etc.
GenericTypeArguments.Add(argType); // TODO: Fix MemberType here
} }
} }
@@ -693,7 +692,6 @@ namespace Il2CppInspector.Reflection {
IsGenericParameter = true; IsGenericParameter = true;
ContainsGenericParameters = true; ContainsGenericParameters = true;
IsGenericType = false; IsGenericType = false;
IsGenericTypeDefinition = false;
} }
} }
@@ -725,7 +723,6 @@ namespace Il2CppInspector.Reflection {
IsGenericParameter = true; IsGenericParameter = true;
IsGenericType = false; IsGenericType = false;
IsGenericTypeDefinition = false;
ContainsGenericParameters = true; ContainsGenericParameters = true;
} }
@@ -809,15 +806,11 @@ namespace Il2CppInspector.Reflection {
refs.Add(GetEnumUnderlyingType()); refs.Add(GetEnumUnderlyingType());
// Generic type parameters and constraints // Generic type parameters and constraints
if (GenericTypeParameters != null) refs.UnionWith(GetGenericArguments());
refs.UnionWith(GenericTypeParameters);
if (GenericTypeArguments != null)
refs.UnionWith(GenericTypeArguments);
refs.UnionWith(GetGenericParameterConstraints()); refs.UnionWith(GetGenericParameterConstraints());
// Generic type constraints of type parameters in generic type definition // Generic type constraints of type parameters in generic type definition
if (GenericTypeParameters != null) refs.UnionWith(GenericTypeParameters.SelectMany(p => p.GetGenericParameterConstraints()));
refs.UnionWith(GenericTypeParameters.SelectMany(p => p.GetGenericParameterConstraints()));
// Implemented interfaces // Implemented interfaces
refs.UnionWith(ImplementedInterfaces); refs.UnionWith(ImplementedInterfaces);
@@ -829,7 +822,7 @@ namespace Il2CppInspector.Reflection {
// Type arguments in generic types that may have been a field, method parameter etc. // Type arguments in generic types that may have been a field, method parameter etc.
IEnumerable<TypeInfo> genericArguments = refs.ToList(); IEnumerable<TypeInfo> genericArguments = refs.ToList();
do { do {
genericArguments = genericArguments.Where(r => r.GenericTypeArguments != null).SelectMany(r => r.GenericTypeArguments); genericArguments = genericArguments.SelectMany(r => r.GetGenericArguments());
refs.UnionWith(genericArguments); refs.UnionWith(genericArguments);
} while (genericArguments.Any()); } while (genericArguments.Any());
@@ -852,8 +845,7 @@ namespace Il2CppInspector.Reflection {
(HasElementType? ElementType.Name : (HasElementType? ElementType.Name :
(DeclaringType != null ? DeclaringType.Name + "+" : "") (DeclaringType != null ? DeclaringType.Name + "+" : "")
+ base.Name + base.Name
+ (GenericTypeParameters != null ? "[" + string.Join(",", GenericTypeParameters.Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : "") + (GetGenericArguments().Any()? "[" + string.Join(",", GetGenericArguments().Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : ""))
+ (GenericTypeArguments != null ? "[" + string.Join(",", GenericTypeArguments.Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : ""))
+ (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "")
+ (IsByRef ? "&" : "") + (IsByRef ? "&" : "")
+ (IsPointer ? "*" : ""); + (IsPointer ? "*" : "");
@@ -905,7 +897,7 @@ namespace Il2CppInspector.Reflection {
// Check if we are in a nested type, and if so, exclude ourselves if we are a generic type parameter from the outer type // Check if we are in a nested type, and if so, exclude ourselves if we are a generic type parameter from the outer type
// All constraints are inherited automatically by all nested types so we only have to look at the immediate outer type // All constraints are inherited automatically by all nested types so we only have to look at the immediate outer type
if (DeclaringMethod == null && DeclaringType.IsNested && (DeclaringType.DeclaringType.GenericTypeParameters?.Any(p => p.Name == Name) ?? false)) if (DeclaringMethod == null && DeclaringType.IsNested && DeclaringType.DeclaringType.GetGenericArguments().Any(p => p.Name == Name))
return string.Empty; return string.Empty;
// Check if we are in an overriding method, and if so, exclude ourselves if we are a generic type parameter from the base method // Check if we are in an overriding method, and if so, exclude ourselves if we are a generic type parameter from the base method