Emit call to a generic method for a given set of type parameters

Topics: PE Writer
Feb 10, 2011 at 3:19 AM

What is the proper mechanism for using CCI to emit a call to a generic method?  For example, given an IMethodDefinition for the UseValue<T> method below:

class Foo
{
  public void UseValue<T>(T value)
  {
  }

How can a mutator/rewriter emit a call to UseValue for a given type T (eg: System.Double)?  Naive attempts to instantiate a GenericMethodInstance using the generic method definition using a type reference for [mscorlib]System.Double subsequently results in a KeyNotFound exception in PeWriter.GetMethodDefIndex.

-Mike

Coordinator
Feb 10, 2011 at 3:25 PM

I would expect the naive way of doing things to actually work. The kind of error that you are seeing most often would be the result of the generic method itself not being included in the assembly you are writing out.

Feb 10, 2011 at 11:54 PM

But I think I'm missing a step.  And I probably did a poor job of phrasing the question.

The assembly I'm working with defines a generic method (eg: UseValue<T>).  During rewriting, I'm trying to inject a call to a specialized instance of that method (eg: UseValue<double>).  The original input assembly doesn't have any existing calls to UseValue<double>.  The naive thing I've done is get my hands on the IMethodDefinition for UseValue<T>, and then used it to instantiate a GenericInstanceMethod, which is then used as the target of a call instruction.  But when the PE file is being written (in the call path below PeWriter.SerializeMethodBody), a MethodSpecComparer's GetHashCode method is called, which looks like so (in PeWriter.cs):

public int GetHashCode(IGenericMethodInstanceReference methodInstanceReference) {
  return (int)((this.peWriter.GetMethodDefOrRefCodedIndex(methodInstanceReference.GenericMethod) << 2) ^
    this.peWriter.GetMethodInstanceSignatureIndex(methodInstanceReference));
}

At this point, methodInstanceReference points to the GenericInstanceMethod I constructed for UseValue<double>, with the GenericMethod property pointing to the method def for UseValue<T>.  The call to GetMethodDefOrRefCodedIndex is the one that's incurring the exception (GetMethodInstanceSignatureIndex works).  The specific location of the exception happens at this point in PeWriter:

private uint GetMethodDefIndex(IMethodDefinition method) {
  return this.methodDefIndex[method];    // <<< KeyNotFoundException
}

When this happens, methodis pointing to the generic method def for UseValue<T>.

So it seems I'm not quite going about specializing a generic method correctly.  Merely instantiating a GenericInstanceMethod is either insufficient or completely wrong.  I've looked around unsuccessfully for some sort of Specialize or Construct method that would allow me to pass in the concrete types to be used for specialization.

Hence my question about what the proper set of steps should be to invoke a specialized/constructed version of a generic method given (a) the generic method definition (eg: UseValue<T>) and (b) a type definition for a concrete type that is suitable for T (eg: double)?  Although at this point, I'm willing to believe this is all a red herring, and the problem lies elsewhere.

Coordinator
Feb 11, 2011 at 4:22 AM

My guess is that the method that is not being found belongs to the assembly you are reading, rather than the assembly you are writing. My recommendation is that you first make a mutable copy of the assembly, then find UseValue<T>, then make the instance and modify the copy.

Feb 11, 2011 at 5:05 PM

I think you're right, but am having some trouble following your advice.

I was using the apparently obsolete MetadataMutator class as the base for my visitor.  I had not noticed before that it was commented as being obsolete.  So I switched to using MutatingVisitor as my base class (and overriding Mutate methods instead of Visit methods), but keep running into places where the node being visited was mutable.  The comment on MetadataMutator indicates to use MutatingVisitor  and MetadataCopier, but I'm not clear on how to use MetadataCopier to make a deep/mutable copy of the assembly before attempting to do any rewriting.

Given that I was using an obsolete visitor base class, I may also be going about loading the assembly the wrong way.  I've been using IMetadataHost.LoadUnitFrom to initially load the assembly that I want to rewrite.  As of my recent switch to use MutatingVisitor, I'm now doing the following to get a mutable Assembly to work with:

var asm = new Assembly(); // MutableCodeModel.Assembly
asm.Copy(asmLoadedUsingLoadUnitFrom, base.host.InternFactory);

This results in a mutable assembly that has some immutable nodes below it.  I'm guessing this is where MetadataCopier comes in.  What is the proper sequence to load an assembly that results in a mutable deep copy of every namespace/type/member/etc defined within that assembly.

Thanks for your help.

Coordinator
Feb 11, 2011 at 6:07 PM

Could you have a look at the peTope sample for how to make a mutable copy of an assembly?

Feb 11, 2011 at 8:00 PM

Wow - there's a major disconnect somewhere.  I fail to see how the peTope sample answers my question.  That sample slurps up an assembly and writes it back out again to a different file.  I've got a system based on the ILMutator example, which slurps up an assembly, fiddles with it in memory, and writes the mutated version out to another file.  It works in all cases except when trying to inject a call to a specialized instance of a generic method as described above.  The peTope sample doesn't appear to do any modification of the input assembly on its way to be written to the new output assembly location.  The ILMutator example seems to be the more appropriate example, but is based on the apparently outdated MetadataMutator base class that I've been using.  Is there an example somewhere demonstrating how to use the MutatingVisitor base class?

Coordinator
Feb 11, 2011 at 8:33 PM

Sorry, I was looking at an older, internal, version of the sample that is a bit more complicated than the one in CodePlex. No wonder we are are disconnected. Here is the relevant part of it:

           if (decompile)
            //Construct a Code Model from the Metadata model via decompilation
            module = Decompiler.GetCodeModelFromMetadataModel(host, module, pdbReader);

          //Create a mutator and run it over the module, producing a copy that could be different if the mutator
          //were a subclass that made changes during the copy process.
          MutatingVisitor mutator;
          MetadataCopier copier;
          List<INamedTypeDefinition> list;
          if (decompile) {
            copier = new CodeCopier(host, pdbReader, module, out list);
            mutator = new CodeMutatingVisitor(host, pdbReader);
          } else {
            copier = new MetadataCopier(host, module, out list);
            mutator = new MutatingVisitor(host);
          }

          module = copier.Substitute(module);
          module = mutator.Visit(module);

I'll be checking in many changes shortly and there will many more examples of using mutating visitors after that.

 

 

 


Feb 11, 2011 at 9:03 PM

Ah - that did it.  I'm over the hump now.  Thanks for hanging in there with me.  Much appreciated.