From 06ed21747e4a3d4a4c29ffbf2c1cead52e92650b Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Mon, 11 Nov 2019 21:48:04 +0100 Subject: [PATCH] Model and Output: Implement generic type constraints --- Il2CppDumper/Il2CppCSharpDumper.cs | 23 ++++++++-- Il2CppInspector/MetadataClasses.cs | 4 +- Il2CppInspector/Reflection/MethodBase.cs | 2 - Il2CppInspector/Reflection/TypeInfo.cs | 56 +++++++++++++++++++++--- 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/Il2CppDumper/Il2CppCSharpDumper.cs b/Il2CppDumper/Il2CppCSharpDumper.cs index 40f8554..c47a7d8 100644 --- a/Il2CppDumper/Il2CppCSharpDumper.cs +++ b/Il2CppDumper/Il2CppCSharpDumper.cs @@ -104,7 +104,16 @@ namespace Il2CppInspector @base.Insert(0, type.GetEnumUnderlyingType().CSharpName); var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty; - writer.Write($"{type.CSharpTypeDeclarationName}{baseText} // TypeDefIndex: {type.Index}\n" + prefix + "{\n"); + writer.Write($"{type.CSharpTypeDeclarationName}{baseText} // TypeDefIndex: {type.Index}\n"); + + if (type.GenericTypeParameters != null) + foreach (var gp in type.GenericTypeParameters) { + var constraint = gp.GetTypeConstraintsString(); + if (constraint != string.Empty) + writer.Write($"{prefix}\t{constraint}\n"); + } + + writer.Write(prefix + "{\n"); // Fields if (!type.IsEnum) { @@ -273,8 +282,16 @@ namespace Il2CppInspector writer.Append($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}"); else writer.Append($"{method.CSharpName}{method.ReturnType.CSharpName}"); - writer.Append("(" + method.GetParametersString()); - writer.Append(");" + (method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n"); + writer.Append("(" + method.GetParametersString() + ")"); + + if (method.GenericTypeParameters != null) + foreach (var gp in method.GenericTypeParameters) { + var constraint = gp.GetTypeConstraintsString(); + if (constraint != string.Empty) + writer.Append($"\n{prefix}\t\t{constraint}"); + } + + writer.Append(";" + (method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n"); return writer.ToString(); } diff --git a/Il2CppInspector/MetadataClasses.cs b/Il2CppInspector/MetadataClasses.cs index c73c419..3cc8bd6 100644 --- a/Il2CppInspector/MetadataClasses.cs +++ b/Il2CppInspector/MetadataClasses.cs @@ -382,8 +382,8 @@ namespace Il2CppInspector public int nameIndex; // StringIndex public short constraintsStart; // GenericParameterConstraintIndex public short constraintsCount; - public ushort num; - public ushort flags; + public ushort num; // Generic parameter position + public ushort flags; // GenericParameterAttributes } public class Il2CppCustomAttributeTypeRange diff --git a/Il2CppInspector/Reflection/MethodBase.cs b/Il2CppInspector/Reflection/MethodBase.cs index 73df973..e113961 100644 --- a/Il2CppInspector/Reflection/MethodBase.cs +++ b/Il2CppInspector/Reflection/MethodBase.cs @@ -76,8 +76,6 @@ namespace Il2CppInspector.Reflection var container = pkg.GenericContainers[Definition.genericContainerIndex]; GenericTypeParameters = pkg.GenericParameters.Skip((int)container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToList(); - - // TODO: Constraints } // Set method attributes diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index 315ea71..2e15be6 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -85,6 +85,18 @@ namespace Il2CppInspector.Reflection { // Get a field by its name public FieldInfo GetField(string name) => DeclaredFields.FirstOrDefault(f => f.Name == name); + private readonly int genericConstraintIndex; + + private readonly int genericConstraintCount; + + // Get type constraints on a generic parameter + public TypeInfo[] GetGenericParameterConstraints() { + var types = new TypeInfo[genericConstraintCount]; + for (int c = 0; c < genericConstraintCount; c++) + types[c] = Assembly.Model.GetTypeFromUsage(Assembly.Model.Package.GenericConstraintIndices[genericConstraintIndex + c], MemberTypes.TypeInfo); + return types; + } + // Get a method by its name public MethodInfo GetMethod(string name) => DeclaredMethods.FirstOrDefault(m => m.Name == name); @@ -126,6 +138,10 @@ namespace Il2CppInspector.Reflection { + (IsArray? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsPointer? "*" : ""); + public GenericParameterAttributes GenericParameterAttributes { get; } + + public int GenericParameterPosition { get; } + public List GenericTypeParameters { get; } public List GenericTypeArguments { get; } @@ -227,8 +243,6 @@ namespace Il2CppInspector.Reflection { var container = pkg.GenericContainers[Definition.genericContainerIndex]; GenericTypeParameters = pkg.GenericParameters.Skip((int) container.genericParameterStart).Take(container.type_argc).Select(p => new TypeInfo(this, p)).ToList(); - - // TODO: Constraints } // Add to global type definition list @@ -427,14 +441,23 @@ namespace Il2CppInspector.Reflection { // Same visibility attributes as declaring type Attributes = declaringType.Attributes; - // Same namespace as delcaring type + // Same namespace as declaring type Namespace = declaringType.Namespace; - // Base type of object - // TODO: This may change under constraints + // Special constraints + GenericParameterAttributes = (GenericParameterAttributes) param.flags; + + // Type constraints + genericConstraintIndex = param.constraintsStart; + genericConstraintCount = param.constraintsCount; + + // Base type of object (set by default) // Name of parameter - Name = declaringType.Assembly.Model.Package.Strings[param.nameIndex]; + Name = Assembly.Model.Package.Strings[param.nameIndex]; + + // Position + GenericParameterPosition = param.num; IsGenericParameter = true; IsGenericType = false; @@ -494,6 +517,27 @@ namespace Il2CppInspector.Reflection { return modifiers.ToString(); } + public string GetTypeConstraintsString() { + if (!IsGenericParameter) + return string.Empty; + + var typeConstraints = GetGenericParameterConstraints(); + if (GenericParameterAttributes == GenericParameterAttributes.None && typeConstraints.Length == 0) + return string.Empty; + + var constraintList = typeConstraints.Where(c => c.FullName != "System.ValueType").Select(c => c.CSharpTypeDeclarationName).ToList(); + + if ((GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) + constraintList.Add("struct"); + if ((GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) == GenericParameterAttributes.ReferenceTypeConstraint) + constraintList.Add("class"); + if ((GenericParameterAttributes & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint + && !constraintList.Contains("struct")) + constraintList.Add("new()"); + + return "where " + Name + " : " + string.Join(", ", constraintList); + } + public override string ToString() => Name; } } \ No newline at end of file