Implement proper generic parameter substitution
With this patch, generic parameters in BaseType and method param/return types are substituted correctly and deeply. Next up will be to apply the same substitution rules to fields, properties, events, ...
This commit is contained in:
@@ -110,14 +110,11 @@ namespace Il2CppInspector.Reflection
|
|||||||
DefaultValueMetadataAddress = generic.DefaultValueMetadataAddress;
|
DefaultValueMetadataAddress = generic.DefaultValueMetadataAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParameterInfo SubstituteGenericArguments(TypeInfo[] typeArguments, TypeInfo[] methodArguments) {
|
public ParameterInfo SubstituteGenericArguments(TypeInfo[] typeArguments, TypeInfo[] methodArguments = null) {
|
||||||
/* TODO: Deep substitution */
|
TypeInfo t = ParameterType.SubstituteGenericArguments(typeArguments, methodArguments);
|
||||||
if (ParameterType.IsGenericTypeParameter)
|
if (t == ParameterType)
|
||||||
return new ParameterInfo(this, typeArguments[ParameterType.GenericParameterPosition]);
|
|
||||||
else if (ParameterType.IsGenericMethodParameter)
|
|
||||||
return new ParameterInfo(this, methodArguments[ParameterType.GenericParameterPosition]);
|
|
||||||
else
|
|
||||||
return this;
|
return this;
|
||||||
|
return new ParameterInfo(this, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref will be handled as part of the type name
|
// ref will be handled as part of the type name
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ namespace Il2CppInspector.Reflection {
|
|||||||
return Assembly.Model.TypesByReferenceIndex[Definition.parentIndex];
|
return Assembly.Model.TypesByReferenceIndex[Definition.parentIndex];
|
||||||
}
|
}
|
||||||
if (genericTypeDefinition != null) {
|
if (genericTypeDefinition != null) {
|
||||||
/* TODO substitute generic arguments */
|
return genericTypeDefinition.BaseType.SubstituteGenericArguments(genericArguments);
|
||||||
return genericTypeDefinition.BaseType;
|
|
||||||
}
|
}
|
||||||
if (Namespace != "System" || BaseName != "Object")
|
if (Namespace != "System" || BaseName != "Object")
|
||||||
return Assembly.Model.TypesByFullName["System.Object"];
|
return Assembly.Model.TypesByFullName["System.Object"];
|
||||||
@@ -90,8 +89,7 @@ namespace Il2CppInspector.Reflection {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
if (genericTypeDefinition != null) {
|
if (genericTypeDefinition != null) {
|
||||||
/* Generic type instance */
|
// Generic parameters are *not* substituted in the DeclaringType
|
||||||
/* TODO substitute generic arguments */
|
|
||||||
return genericTypeDefinition.DeclaringType;
|
return genericTypeDefinition.DeclaringType;
|
||||||
}
|
}
|
||||||
return base.DeclaringType;
|
return base.DeclaringType;
|
||||||
@@ -264,6 +262,9 @@ namespace Il2CppInspector.Reflection {
|
|||||||
n += "*";
|
n += "*";
|
||||||
return n;
|
return n;
|
||||||
} else {
|
} else {
|
||||||
|
/* XXX This is not exactly accurate to C# Type.Name:
|
||||||
|
* Type.Name should be the bare name (with & * [] suffixes)
|
||||||
|
* but without nested types or generic arguments */
|
||||||
var n = base.Name;
|
var n = base.Name;
|
||||||
if (DeclaringType != null)
|
if (DeclaringType != null)
|
||||||
n = DeclaringType.Name + "+" + n;
|
n = DeclaringType.Name + "+" + n;
|
||||||
@@ -832,6 +833,10 @@ namespace Il2CppInspector.Reflection {
|
|||||||
// and returns a TypeInfo object representing the resulting constructed type.
|
// and returns a TypeInfo object representing the resulting constructed type.
|
||||||
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype?view=netframework-4.8
|
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype?view=netframework-4.8
|
||||||
public TypeInfo MakeGenericType(params TypeInfo[] typeArguments) {
|
public TypeInfo MakeGenericType(params TypeInfo[] typeArguments) {
|
||||||
|
if(typeArguments.Length != genericArguments.Length) {
|
||||||
|
throw new ArgumentException("The number of generic arguments provided does not match the generic type definition.");
|
||||||
|
}
|
||||||
|
|
||||||
TypeInfo result;
|
TypeInfo result;
|
||||||
if (genericTypeInstances.TryGetValue(typeArguments, out result))
|
if (genericTypeInstances.TryGetValue(typeArguments, out result))
|
||||||
return result;
|
return result;
|
||||||
@@ -840,6 +845,31 @@ namespace Il2CppInspector.Reflection {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeInfo SubstituteGenericArguments(TypeInfo[] typeArguments, TypeInfo[] methodArguments = null) {
|
||||||
|
if (!ContainsGenericParameters)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (IsGenericTypeParameter)
|
||||||
|
return typeArguments[GenericParameterPosition];
|
||||||
|
else if (IsGenericMethodParameter)
|
||||||
|
return methodArguments[GenericParameterPosition];
|
||||||
|
else if(IsGenericTypeDefinition)
|
||||||
|
return MakeGenericType(typeArguments);
|
||||||
|
else if(HasElementType) {
|
||||||
|
var elementType = ElementType.SubstituteGenericArguments(typeArguments, methodArguments);
|
||||||
|
if (IsArray)
|
||||||
|
return elementType.MakeArrayType(GetArrayRank());
|
||||||
|
else if (IsByRef)
|
||||||
|
return elementType.MakeByRefType();
|
||||||
|
else if (IsPointer)
|
||||||
|
return elementType.MakePointerType();
|
||||||
|
throw new InvalidOperationException("TypeInfo element type state is invalid!");
|
||||||
|
} else {
|
||||||
|
var newGenericArguments = genericArguments.Select(x => x.SubstituteGenericArguments(typeArguments, methodArguments));
|
||||||
|
return genericTypeDefinition.MakeGenericType(newGenericArguments.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize a type that is a generic parameter of a generic type
|
// Initialize a type that is a generic parameter of a generic type
|
||||||
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.isgenerictype?view=netframework-4.8
|
// See: https://docs.microsoft.com/en-us/dotnet/api/system.type.isgenerictype?view=netframework-4.8
|
||||||
public TypeInfo(TypeInfo declaringType, Il2CppGenericParameter param) : base(declaringType) {
|
public TypeInfo(TypeInfo declaringType, Il2CppGenericParameter param) : base(declaringType) {
|
||||||
|
|||||||
@@ -32,8 +32,11 @@ namespace Il2CppInspector
|
|||||||
// Act
|
// Act
|
||||||
TypeInfo tBase = asm.GetType("Il2CppTests.TestSources.Base`2");
|
TypeInfo tBase = asm.GetType("Il2CppTests.TestSources.Base`2");
|
||||||
TypeInfo tDerived = asm.GetType("Il2CppTests.TestSources.Derived`1");
|
TypeInfo tDerived = asm.GetType("Il2CppTests.TestSources.Derived`1");
|
||||||
|
TypeInfo tDerived_closed = model.GetType("Il2CppTests.TestSources.Derived`1[System.Int32]");
|
||||||
TypeInfo tDerivedBase = tDerived.BaseType;
|
TypeInfo tDerivedBase = tDerived.BaseType;
|
||||||
|
TypeInfo tDerivedBase_closed = tDerived_closed.BaseType;
|
||||||
TypeInfo tDerivedArray = model.GetType("Il2CppTests.TestSources.Derived`1[System.Int32][]");
|
TypeInfo tDerivedArray = model.GetType("Il2CppTests.TestSources.Derived`1[System.Int32][]");
|
||||||
|
Assert.That(tDerivedArray, Is.EqualTo(tDerived_closed.MakeArrayType()));
|
||||||
|
|
||||||
TypeInfo tT = tBase.GenericTypeParameters[0];
|
TypeInfo tT = tBase.GenericTypeParameters[0];
|
||||||
TypeInfo tU = tBase.GenericTypeParameters[1];
|
TypeInfo tU = tBase.GenericTypeParameters[1];
|
||||||
@@ -74,6 +77,7 @@ namespace Il2CppInspector
|
|||||||
(tBase, "Base`2[T,U]", true, true, true, false, -1),
|
(tBase, "Base`2[T,U]", true, true, true, false, -1),
|
||||||
(tDerived, "Derived`1[V]", true, true, true, false, -1),
|
(tDerived, "Derived`1[V]", true, true, true, false, -1),
|
||||||
(tDerivedBase, "Base`2[System.String,V]", true, false, true, false, -1),
|
(tDerivedBase, "Base`2[System.String,V]", true, false, true, false, -1),
|
||||||
|
(tDerivedBase_closed, "Base`2[System.String,System.Int32]", true, false, false, false, -1),
|
||||||
(tDerivedArray, "Derived`1[System.Int32][]", false, false, false, false, -1),
|
(tDerivedArray, "Derived`1[System.Int32][]", false, false, false, false, -1),
|
||||||
(tT, "T", false, false, true, true, 0),
|
(tT, "T", false, false, true, true, 0),
|
||||||
(tU, "U", false, false, true, true, 1),
|
(tU, "U", false, false, true, true, 1),
|
||||||
@@ -103,6 +107,8 @@ namespace Il2CppInspector
|
|||||||
Assert.That(t.IsGenericTypeDefinition, Is.EqualTo(check.Item4));
|
Assert.That(t.IsGenericTypeDefinition, Is.EqualTo(check.Item4));
|
||||||
Assert.That(t.ContainsGenericParameters, Is.EqualTo(check.Item5));
|
Assert.That(t.ContainsGenericParameters, Is.EqualTo(check.Item5));
|
||||||
Assert.That(t.IsGenericParameter, Is.EqualTo(check.Item6));
|
Assert.That(t.IsGenericParameter, Is.EqualTo(check.Item6));
|
||||||
|
if (t.IsGenericParameter)
|
||||||
|
Assert.That(t.GenericParameterPosition, Is.EqualTo(check.Item7));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var check in methodChecks) {
|
foreach (var check in methodChecks) {
|
||||||
|
|||||||
Reference in New Issue
Block a user