Skip to content

Commit

Permalink
Support Type Parameters in Call Analysis (SamboyCoding#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
ds5678 authored Jan 1, 2024
1 parent a53e72d commit c0eccb1
Show file tree
Hide file tree
Showing 16 changed files with 334 additions and 59 deletions.
30 changes: 30 additions & 0 deletions Cpp2IL.Core/Model/Contexts/ArrayTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using Cpp2IL.Core.Utils;
using LibCpp2IL.BinaryStructures;

namespace Cpp2IL.Core.Model.Contexts;

public class ArrayTypeAnalysisContext : WrappedTypeAnalysisContext
{
public ArrayTypeAnalysisContext(TypeAnalysisContext elementType, int rank, AssemblyAnalysisContext referencedFrom) : base(elementType, referencedFrom)
{
Rank = rank;
}

public ArrayTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom)
: this(referencedFrom.ResolveIl2CppType(rawType.GetArrayElementType()), rawType.GetArrayRank(), referencedFrom)
{
}

public override Il2CppTypeEnum Type => Il2CppTypeEnum.IL2CPP_TYPE_ARRAY;

public override string DefaultName => $"{ElementType.Name}[{Rank}]";

public int Rank { get; }

public override TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
return ElementType.ToTypeSignature(parentModule).MakeArrayType(Rank);
}
}
28 changes: 28 additions & 0 deletions Cpp2IL.Core/Model/Contexts/ByRefTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using LibCpp2IL.BinaryStructures;

namespace Cpp2IL.Core.Model.Contexts;

public class ByRefTypeAnalysisContext : WrappedTypeAnalysisContext
{
public ByRefTypeAnalysisContext(TypeAnalysisContext elementType, AssemblyAnalysisContext referencedFrom) : base(elementType, referencedFrom)
{
}

public ByRefTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom)
: this(default(TypeAnalysisContext)!, referencedFrom)
{
}

public override Il2CppTypeEnum Type => Il2CppTypeEnum.IL2CPP_TYPE_BYREF;

public override string DefaultName => $"{ElementType.Name}&";

protected override TypeAnalysisContext ElementType => base.ElementType ?? throw new("TODO Support TYPE_BYREF");

public override TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
return ElementType.ToTypeSignature(parentModule).MakeByReferenceType();
}
}
53 changes: 45 additions & 8 deletions Cpp2IL.Core/Model/Contexts/ConcreteGenericMethodAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Reflection;
using Cpp2IL.Core.Utils;
using LibCpp2IL;

namespace Cpp2IL.Core.Model.Contexts;
Expand All @@ -6,27 +8,62 @@ public class ConcreteGenericMethodAnalysisContext : MethodAnalysisContext
{
public readonly AssemblyAnalysisContext DeclaringAsm;
public readonly Cpp2IlMethodRef MethodRef;
public TypeAnalysisContext BaseTypeContext;
public MethodAnalysisContext BaseMethodContext;
public readonly MethodAnalysisContext BaseMethodContext;

public sealed override ulong UnderlyingPointer => MethodRef.GenericVariantPtr;

public override bool IsStatic => BaseMethodContext.IsStatic;

public override bool IsVoid => BaseMethodContext.IsVoid;

public override string DefaultName => BaseMethodContext.DefaultName;

public ConcreteGenericMethodAnalysisContext(Cpp2IlMethodRef methodRef, ApplicationAnalysisContext context) : base(context)
public override MethodAttributes Attributes => BaseMethodContext.Attributes;

public override AssemblyAnalysisContext CustomAttributeAssembly => BaseMethodContext.CustomAttributeAssembly;

public ConcreteGenericMethodAnalysisContext(Cpp2IlMethodRef methodRef, ApplicationAnalysisContext context)
: this(methodRef, ResolveDeclaringAssembly(methodRef, context))
{
}

private ConcreteGenericMethodAnalysisContext(Cpp2IlMethodRef methodRef, AssemblyAnalysisContext declaringAssembly)
: base(null, ResolveDeclaringType(methodRef, declaringAssembly))
{
MethodRef = methodRef;
DeclaringAsm = context.GetAssemblyByName(methodRef.DeclaringType.DeclaringAssembly!.Name!) ?? throw new($"Unable to resolve declaring assembly {methodRef.DeclaringType.DeclaringAssembly.Name} for generic method {methodRef}");
BaseTypeContext = DeclaringAsm!.GetTypeByFullName(methodRef.DeclaringType.FullName!) ?? throw new($"Unable to resolve declaring type {methodRef.DeclaringType.FullName} for generic method {methodRef}");
BaseMethodContext = BaseTypeContext.GetMethod(methodRef.BaseMethod) ?? throw new($"Unable to resolve base method {methodRef.BaseMethod} for generic method {methodRef}");
DeclaringAsm = declaringAssembly;
BaseMethodContext = ResolveBaseMethod(methodRef, declaringAssembly.GetTypeByDefinition(methodRef.DeclaringType)!);

//TODO: Do we want to update this to populate known generic parameters based on the generic arguments?
Parameters.AddRange(BaseMethodContext.Parameters);

if(UnderlyingPointer != 0)
RawBytes = AppContext.InstructionSet.GetRawBytesForMethod(this, false);
}
}

private static AssemblyAnalysisContext ResolveDeclaringAssembly(Cpp2IlMethodRef methodRef, ApplicationAnalysisContext context)
{
return context.GetAssemblyByName(methodRef.DeclaringType.DeclaringAssembly!.Name!)
?? throw new($"Unable to resolve declaring assembly {methodRef.DeclaringType.DeclaringAssembly.Name} for generic method {methodRef}");
}

private static TypeAnalysisContext ResolveDeclaringType(Cpp2IlMethodRef methodRef, AssemblyAnalysisContext declaringAssembly)
{
var baseType = declaringAssembly.AppContext.ResolveContextForType(methodRef.DeclaringType)
?? throw new($"Unable to resolve declaring type {methodRef.DeclaringType.FullName} for generic method {methodRef}");

var genericParams = new TypeAnalysisContext[methodRef.TypeGenericParams.Length];
for (var i = 0; i < methodRef.TypeGenericParams.Length; i++)
{
genericParams[i] = methodRef.TypeGenericParams[i].ToContext(declaringAssembly)
?? throw new($"Unable to resolve generic parameter {methodRef.TypeGenericParams[i]} for generic method {methodRef}");
}
return new GenericInstanceTypeAnalysisContext(baseType, genericParams, declaringAssembly);
}

private static MethodAnalysisContext ResolveBaseMethod(Cpp2IlMethodRef methodRef, TypeAnalysisContext declaringType)
{
return declaringType.GetMethod(methodRef.BaseMethod)
?? throw new($"Unable to resolve base method {methodRef.BaseMethod} for generic method {methodRef}");
}
}
26 changes: 24 additions & 2 deletions Cpp2IL.Core/Model/Contexts/GenericInstanceTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using Cpp2IL.Core.Utils;
using LibCpp2IL.BinaryStructures;

Expand All @@ -9,7 +12,11 @@ public class GenericInstanceTypeAnalysisContext : ReferencedTypeAnalysisContext
{
protected override TypeAnalysisContext ElementType { get; }

public GenericInstanceTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom) : base(rawType, referencedFrom)
public override string DefaultName => $"{ElementType.Name}<{string.Join(", ", GenericArguments.Select(a => a.Name))}>";

public override Il2CppTypeEnum Type => Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST;

public GenericInstanceTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom) : base(referencedFrom)
{
//Element type has to be a type definition
var gClass = rawType.GetGenericClass();
Expand All @@ -18,6 +25,21 @@ public GenericInstanceTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisCo
GenericArguments.AddRange(gClass.Context.ClassInst.Types.Select(referencedFrom.ResolveIl2CppType)!);
}

public GenericInstanceTypeAnalysisContext(TypeAnalysisContext elementType, IEnumerable<TypeAnalysisContext> genericArguments, AssemblyAnalysisContext referencedFrom) : base(referencedFrom)
{
ElementType = elementType;
GenericArguments.AddRange(genericArguments);
OverrideBaseType = elementType.BaseType;
}

public override TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
var elementType = ElementType.ToTypeSignature(parentModule).ToTypeDefOrRef();
var genericArguments = GenericArguments.Select(a => a.ToTypeSignature(parentModule)).ToArray();

return new GenericInstanceTypeSignature(elementType, IsValueType, genericArguments);
}

public override string GetCSharpSourceString()
{
var sb = new StringBuilder();
Expand Down
37 changes: 32 additions & 5 deletions Cpp2IL.Core/Model/Contexts/GenericParameterTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
using LibCpp2IL.BinaryStructures;
using System;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using LibCpp2IL.BinaryStructures;
using LibCpp2IL.Metadata;

namespace Cpp2IL.Core.Model.Contexts;

public class GenericParameterTypeAnalysisContext : ReferencedTypeAnalysisContext
{
public override string DefaultName { get; }

public int Index { get; }

public override Il2CppTypeEnum Type { get; }

protected override TypeAnalysisContext ElementType => throw new("Attempted to get element type of a generic parameter");

public GenericParameterTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom) : base(rawType, referencedFrom)
public GenericParameterTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom)
: this(rawType.GetGenericParameterDef(), rawType.Type, referencedFrom)
{
}

public GenericParameterTypeAnalysisContext(Il2CppGenericParameter genericParameter, Il2CppTypeEnum type, AssemblyAnalysisContext referencedFrom)
: this(genericParameter.Name ?? "T", genericParameter.genericParameterIndexInOwner, type, referencedFrom)
{
if(rawType.Type is not Il2CppTypeEnum.IL2CPP_TYPE_VAR and not Il2CppTypeEnum.IL2CPP_TYPE_MVAR)
throw new($"Generic parameter type is not a generic parameter, but {rawType.Type}");
}

public GenericParameterTypeAnalysisContext(string name, int index, Il2CppTypeEnum type, AssemblyAnalysisContext referencedFrom) : base(referencedFrom)
{
if (type is not Il2CppTypeEnum.IL2CPP_TYPE_VAR and not Il2CppTypeEnum.IL2CPP_TYPE_MVAR)
throw new ArgumentException($"Generic parameter type is not a generic parameter, but {type}", nameof(type));

GenericParameter = rawType.GetGenericParameterDef();
DefaultName = name;
Index = index;
Type = type;
}

public override TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
return new GenericParameterSignature(Type == Il2CppTypeEnum.IL2CPP_TYPE_VAR ? GenericParameterType.Type : GenericParameterType.Method, Index);
}
}
2 changes: 0 additions & 2 deletions Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ public class MethodAnalysisContext : HasCustomAttributesAndName, IMethodInfoProv

/// <summary>
/// The analysis context for the declaring type of this method.
///
/// Null iff this is a subclass.
/// </summary>
public readonly TypeAnalysisContext? DeclaringType;

Expand Down
27 changes: 27 additions & 0 deletions Cpp2IL.Core/Model/Contexts/PointerTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using Cpp2IL.Core.Utils;
using LibCpp2IL.BinaryStructures;

namespace Cpp2IL.Core.Model.Contexts;

public class PointerTypeAnalysisContext : WrappedTypeAnalysisContext
{
public PointerTypeAnalysisContext(TypeAnalysisContext elementType, AssemblyAnalysisContext referencedFrom) : base(elementType, referencedFrom)
{
}

public PointerTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom)
: this(referencedFrom.ResolveIl2CppType(rawType.GetEncapsulatedType()), referencedFrom)
{
}

public override Il2CppTypeEnum Type => Il2CppTypeEnum.IL2CPP_TYPE_PTR;

public override string DefaultName => $"{ElementType.Name}*";

public override TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
return ElementType.ToTypeSignature(parentModule).MakePointerType();
}
}
26 changes: 4 additions & 22 deletions Cpp2IL.Core/Model/Contexts/ReferencedTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LibCpp2IL.BinaryStructures;
using LibCpp2IL.Metadata;

namespace Cpp2IL.Core.Model.Contexts;

Expand All @@ -11,28 +8,14 @@ namespace Cpp2IL.Core.Model.Contexts;
/// </summary>
public abstract class ReferencedTypeAnalysisContext : TypeAnalysisContext
{
public Il2CppType RawType { get; }
public abstract Il2CppTypeEnum Type { get; } //Must be set by derived classes

protected abstract TypeAnalysisContext ElementType { get; } //Must be set by derived classes

protected List<TypeAnalysisContext> GenericArguments { get; } = new();

protected Il2CppGenericParameter? GenericParameter { get; set; }

public override string DefaultNs => ElementType.Namespace;

public override string DefaultName => RawType.Type switch
{
Il2CppTypeEnum.IL2CPP_TYPE_PTR => $"{ElementType.Name}*",
Il2CppTypeEnum.IL2CPP_TYPE_BYREF => $"{ElementType.Name}&",
Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY => $"{ElementType.Name}[]",
Il2CppTypeEnum.IL2CPP_TYPE_ARRAY => $"{ElementType.Name}[{RawType.GetArrayRank()}]",
Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST => $"{ElementType.Name}<{string.Join(", ", GenericArguments.Select(a => a.Name))}>",
Il2CppTypeEnum.IL2CPP_TYPE_VAR => GenericParameter!.Name!,
Il2CppTypeEnum.IL2CPP_TYPE_MVAR => GenericParameter!.Name!,
_ => throw new ArgumentOutOfRangeException(),
};

protected override int CustomAttributeIndex => -1;

public sealed override bool IsGenericInstance => GenericArguments.Count > 0;
Expand All @@ -41,14 +24,13 @@ public abstract class ReferencedTypeAnalysisContext : TypeAnalysisContext

public override AssemblyAnalysisContext CustomAttributeAssembly => DeclaringAssembly;

protected ReferencedTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom) : base(null, referencedFrom)
protected ReferencedTypeAnalysisContext(AssemblyAnalysisContext referencedFrom) : base(null, referencedFrom)
{
RawType = rawType;
}

public override string ToString()
{
return $"{DefaultName}";
return DefaultName;
}

public override string GetCSharpSourceString()
Expand Down
27 changes: 27 additions & 0 deletions Cpp2IL.Core/Model/Contexts/SzArrayTypeAnalysisContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using Cpp2IL.Core.Utils;
using LibCpp2IL.BinaryStructures;

namespace Cpp2IL.Core.Model.Contexts;

public class SzArrayTypeAnalysisContext : WrappedTypeAnalysisContext
{
public SzArrayTypeAnalysisContext(TypeAnalysisContext elementType, AssemblyAnalysisContext referencedFrom) : base(elementType, referencedFrom)
{
}

public SzArrayTypeAnalysisContext(Il2CppType rawType, AssemblyAnalysisContext referencedFrom)
: this(referencedFrom.ResolveIl2CppType(rawType.GetEncapsulatedType()), referencedFrom)
{
}

public override Il2CppTypeEnum Type => Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY;

public override string DefaultName => $"{ElementType.Name}[]";

public override TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
return ElementType.ToTypeSignature(parentModule).MakeSzArrayType();
}
}
9 changes: 9 additions & 0 deletions Cpp2IL.Core/Model/Contexts/TypeAnalysisContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Linq;
using System.Reflection;
using System.Text;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures.Types;
using Cpp2IL.Core.Api;
using Cpp2IL.Core.Utils;
using LibCpp2IL.BinaryStructures;
Expand Down Expand Up @@ -141,6 +143,13 @@ public TypeAnalysisContext(Il2CppTypeDefinition? il2CppTypeDefinition, AssemblyA
public List<MethodAnalysisContext> GetConstructors() => Methods.Where(m => m.Definition!.Name == ".ctor").ToList();

public override string ToString() => $"Type: {Definition?.FullName}";

public virtual TypeSignature ToTypeSignature(ModuleDefinition parentModule)
{
var typeDefinition = GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {FullName}");
return parentModule.DefaultImporter.ImportType(typeDefinition).ToTypeSignature();
}

public virtual string GetCSharpSourceString()
{
if (Definition != null)
Expand Down
Loading

0 comments on commit c0eccb1

Please sign in to comment.