3

The problem

I'm trying to get this IL instruction using strictly Mono.Cecil. Right now, the only solution I found involves importing every references assembly and their exported types, call MakeGenericInstanceType(), and Module.Import().

Wanted IL instruction

The code it produces is:

call instance class [mscorlib]System.Threading.Tasks.Task`1<!!0> class [mscorlib]System.Threading.Tasks.Task`1<string>::ContinueWith<class MyClass`1<valuetype [mscorlib]System.DateTime>>(class [mscorlib]System.Func`2<class [mscorlib]System.Threading.Tasks.Task`1<!0>, !!0>)

What I can get (using only references)

I'm trying to do the same thing without importing everything (hence using only references), but can't do it. The two closest things I've done (and don't work) are:

Test 1

call instance class [mscorlib]System.Threading.Tasks.Task`1<!1> class [mscorlib]System.Threading.Tasks.Task`1<string>::ContinueWith<class MyClass`1<valuetype [mscorlib]System.DateTime>>(class [mscorlib]System.Func`2<class [mscorlib]System.Threading.Tasks.Task`1<!0>, !1>)

Problem: !!0 is replaced by !1.

Test 2

call instance class [mscorlib]System.Threading.Tasks.Task`1<class MyClass`1<valuetype [mscorlib]System.DateTime>> class [mscorlib]System.Threading.Tasks.Task`1<string>::ContinueWith<class MyClass`1<valuetype [mscorlib]System.DateTime>>(class [mscorlib]System.Func`2<class [mscorlib]System.Threading.Tasks.Task`1<string>, class MyClass`1<valuetype [mscorlib]System.DateTime>>)

Problem: all generics explicitly defined:

  • !0 => string
  • !!0 => class MyClass`1<valuetype [mscorlib]System.DateTime>

The question

Any idea how to get the wanted instruction, using only references?

The successful (but very hack-y) code

var task = module.GetEverySingleType().First(...);
var returnType = module.GetEverySingleType().First(...);

var continueWith = module.Import((from m in task.GetMethods()
                                  where m.Name == "ContinueWith"
                                  let p = m.GetParameters()
                                  where p.Length == 1 && p[0].ParameterType.Name == "Func`2"
                                  select m.MakeGenericMethod(returnType)).First());
Instruction.Create(OpCodes.Call, continueWith);

GetEverySingleType() imports every Assembly, loads it in memory, and enumerates its types.

The non-successful code (Test 2)

var func = Module.Import(typeof(Func<,>)).MakeGenericType(taskType, returnType);

var continueWith = new GenericInstanceMethod(new MethodReference("ContinueWith", Module.Import(typeof(Task<>)).MakeGenericInstanceType(returnType), taskType) { HasThis = true });
continueWith.GenericArguments.Add(returnType);
continueWith.Parameters.Add(new ParameterDefinition(Module.Import(typeof(Func<,>)).MakeGenericType(taskType, returnType)));

Instruction.Create(OpCodes.Call, continueWith);

Any idea?

svick
  • 236,525
  • 50
  • 385
  • 514
Greg
  • 131
  • 3
  • 8

1 Answers1

0

You don't have any generic parameters in continueWith in your second example, that's why the resulting method reference doesn't use any. The method signature you are aiming for uses two, !0 and !!0. You have to keep the generic parameters and the return/parameter types must use them. You seem to have no problems importing stuff from system assemblies, so try this:

var bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ;

// import the *generic method* from the *generic type*
// can be done once per module
var cwgg = module.Import (typeof (Task<>).GetMethods (bf).Where (_ =>
  _.Name == "ContinueWith"    &&
  _.IsGenericMethodDefinition &&
  _.GetParameters ().Length == 1).Single ()) ;

// close Task<>, keep ContinueWith generic parameter open
var cwgi     = new MethodReference (cwgg.Name, cwgg.ReturnType, taskType) ;
cwgi.HasThis = true ;
cwgi.GenericParameters.Add (new GenericParameter    ("TNewResult", cwgi)) ;
cwgi.Parameters.Add        (new ParameterDefinition (
    cwgg.Parameters[0].ParameterType)) ;

// close ContinueWith
var continueWith = new GenericInstanceMethod (cwgi) ;
continueWith.GenericArguments.Add      (returnType) ;
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56