Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some helper method to help instantiate generic types and methods #679

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions Mono.Cecil/ModuleDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,106 @@ public TypeReference ImportReference (TypeReference type, IGenericParameterProvi
return MetadataImporter.ImportReference (type, context);
}

/// <summary>
/// Create a GenericInstanceType for the given generic type instantiated with the given generic arguments.
/// </summary>
/// <param name="genericType">The generic type to instantiate.</param>
/// <param name="genericArgs">The generic arguments needed to instantiate the generic type.</param>
/// <returns></returns>
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;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="genericMethod">A generic method to instantiate.</param>
/// <param name="genericArgs">The combined generic arguments for the declaring type (if it is generic) and for the method.</param>
/// <returns></returns>
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)
{
Expand Down