From 798c2a7c1f35122540d3de670b4b77896ef71ec2 Mon Sep 17 00:00:00 2001 From: Chris Lovett Date: Sun, 12 Jul 2020 17:10:09 -0700 Subject: [PATCH] Add some helper method to help instantiate generic types and methods correctly. --- Mono.Cecil/ModuleDefinition.cs | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/Mono.Cecil/ModuleDefinition.cs b/Mono.Cecil/ModuleDefinition.cs index 22daecb9b..0b60c1fce 100644 --- a/Mono.Cecil/ModuleDefinition.cs +++ b/Mono.Cecil/ModuleDefinition.cs @@ -854,6 +854,106 @@ public TypeReference ImportReference (TypeReference type, IGenericParameterProvi return MetadataImporter.ImportReference (type, context); } + /// + /// Create a GenericInstanceType for the given generic type instantiated with the given generic arguments. + /// + /// The generic type to instantiate. + /// The generic arguments needed to instantiate the generic type. + /// + public GenericInstanceType ImportGenericTypeInstance (TypeReference genericType, params TypeReference [] genericArgs) + { + TypeReference typeDef = genericType.Resolve (); + if (!typeDef.HasGenericParameters) { + throw new InvalidOperationException (string.Format ("Type {0} is not generic", genericType)); + } + + typeDef = this.ImportReference (typeDef); + var instance = new GenericInstanceType (typeDef); + for (int i = 0; i < typeDef.GenericParameters.Count; i++) { + var p = typeDef.GenericParameters [i]; + if (p.Position < genericArgs.Length) { + GenericParameter parameter = new GenericParameter (p.Name, typeDef); + instance.GenericParameters.Add (parameter); + instance.GenericArguments.Add (genericArgs [p.Position]); + } else { + throw new InvalidOperationException (string.Format ("Not enough generic arguments to instantiate type {0}", genericType)); + } + } + + return instance; + } + + /// + /// Create a GenericInstanceMethod from the given generic method and the given generic arguments. + /// Note: this can also handle the case where the DeclaringType is also generic. Simply pass the combined + /// generic args for the declaring type and the method. + /// + /// A generic method to instantiate. + /// The combined generic arguments for the declaring type (if it is generic) and for the method. + /// + public MethodReference ImportGenericMethodInstance (MethodReference genericMethod, params TypeReference [] genericArgs) + { + var methodDef = genericMethod.Resolve (); + + TypeReference typeDef = methodDef.DeclaringType; + var genericArgOffset = 0; + GenericInstanceType typeInstance = null; + + if (typeDef.HasGenericParameters) { + typeInstance = this.ImportGenericTypeInstance (typeDef, genericArgs); + typeDef = typeInstance; + genericArgOffset = typeInstance.GenericArguments.Count; + } + + TypeReference returnType = methodDef.ReturnType; + + // create a new MethodReference with the instantiated generic type as the DeclaringType. + MethodReference result = new MethodReference (genericMethod.Name, returnType, typeDef) { + HasThis = genericMethod.HasThis, + ExplicitThis = genericMethod.ExplicitThis, + CallingConvention = genericMethod.CallingConvention + }; + + GenericInstanceMethod genericMethodInstance = null; + + if (methodDef.HasGenericParameters) { + // Then we also need to instantiate the generic method! + genericMethodInstance = new GenericInstanceMethod (result); + + for (int i = 0; i < methodDef.GenericParameters.Count; i++) { + var p = methodDef.GenericParameters [i]; + var j = p.Position + genericArgOffset; + if (j > genericArgs.Length) { + throw new InvalidOperationException (string.Format ("Not enough generic arguments to instantiate method {0}", genericMethod)); + } + + GenericParameter parameter = new GenericParameter (p.Name, genericMethodInstance); + result.GenericParameters.Add (parameter); + genericMethodInstance.GenericParameters.Add (parameter); + genericMethodInstance.GenericArguments.Add (genericArgs [j]); + } + + result = genericMethodInstance; + } + + foreach (var arg in genericMethod.Parameters) { + ParameterDefinition p = new ParameterDefinition (arg.Name, arg.Attributes, + this.ImportReference (arg.ParameterType, typeDef)); + + if (arg.ParameterType is GenericParameter gp) { + if (gp.DeclaringType != null) { + p.ParameterType = typeInstance.GenericParameters [gp.Position]; + } else if (gp.DeclaringMethod != null) { + p.ParameterType = genericMethodInstance.GenericParameters [gp.Position]; + } + } + + result.Parameters.Add (p); + } + + return result; + } + [Obsolete ("Use ImportReference", error: false)] public FieldReference Import (FieldReference field) {