Replacing the body of a property setter using the CodeRewriter

Nov 29, 2011 at 12:56 AM
Edited Nov 29, 2011 at 12:58 AM

I am new to this. I am trying to change the body of a property using the CodeRewriter.

I get the following error: Unable to cast object of type 'Microsoft.Cci.MutableCodeModel.ReturnStatement' to type 'Microsoft.Cci.IExpression'.

This happens at some point after my method has completed during a call to the following method in CodeRewriter: 

public virtual void RewriteChildren(MethodCall methodCall)

where methodCall is the call to SetValue<T> that I generated (see code snippet below).

Any help will be greatly appreciated.

    // This is an example of a class I am trying to rewrite that exists in some assembly. 
    class Entity : EntityBase
    {
        string test;
        public string Test
        {
            set
            {
                test = value;
            }
            // *** I would like to turn the above setter into the following: 
            //set
            //{
            //    base.SetValue<string>(ref test, "Test", value);
            //}
        }
    }

 

class MyPropertySetterRewriter : CodeRewriter

{
                …
            /// <summary>

        /// Replaces the body of the property setter. The new body will call the SetValue method on the base class.
        /// </summary>
        //  The SetValue signature is "protected virtual void SetValue<T>(ref T field, string propertyName, T newValue)"
        public override IMethodDefinition Rewrite(IMethodDefinition method)
        {
            const string propertySetterPrefix = "set_";
 
            if (method.IsSpecialName && method.Name.Value.StartsWith(propertySetterPrefix))
            {
                string commonMethodName = method.Name.Value.Remove(0,propertySetterPrefix.Length);
                var methodDefinition = (MethodDefinition)method;
                var body = new SourceMethodBody(host, nullnull) { Block = new BlockStatement() };
                
                // Get the SetValue<T> method from the base class.
                var baseSetValue = (MethodDefinition)entityBaseTypeReference.Methods.Single(typeDef => typeDef.Name.Value == "SetValue");
                
                // Get the backing field for the property by finding a field with the same name as the property.
                FieldDefinition propertyBackingFieldDefinition = (FieldDefinition)methodDefinition.ContainingTypeDefinition.Fields.SingleOrDefault(itd=>string.Compare(itd.Name.Value,commonMethodName, true) == 0);
 
                if (propertyBackingFieldDefinition == null
                    return base.Rewrite(method); // do not implement the call to SetValue<T> if their is no backing field.
 
                var call = new MethodCall()
                {
                    IsStaticCall = false,
                    MethodToCall = baseSetValue,
                    Type = host.PlatformType.SystemVoid,
                };
 
                call.Arguments.Add(new RefArgument()
                {
                    Expression = new AddressableExpression()
                    {
                        Definition = propertyBackingFieldDefinition
                    }
                });
 
                call.Arguments.Add(new CompileTimeConstant() { Type = host.PlatformType.SystemString, Value = commonMethodName });
                call.Arguments.Add(new BoundExpression { Definition = methodDefinition.Parameters.First() }); // value param of the property setter
                var callSetValue = new ExpressionStatement() { Expression = call };
 
                var block = (BlockStatement)body.Block;
                block.Statements.Add(callSetValue);
                block.Statements.Add(new ReturnStatement());
 
                methodDefinition.Body = (IMethodBody)body;                 
            }
 
            return base.Rewrite(method);
        }

}

Coordinator
Nov 29, 2011 at 1:11 AM

Could you include a call stack for the point where the invalid cast occurs?

Nov 29, 2011 at 5:26 AM

Hi Herman, 

Here it is. Thanks.

>Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IExpression expression = {Microsoft.Cci.DummyExpression}) + 0x3c bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.MethodCall methodCall = {Microsoft.Cci.MutableCodeModel.MethodCall}) + 0x57 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IMethodCall methodCall = {Microsoft.Cci.MutableCodeModel.MethodCall}) + 0x4d bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Dispatcher.Visit(Microsoft.Cci.IMethodCall methodCall = {Microsoft.Cci.MutableCodeModel.MethodCall}) + 0x33 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.MethodCall.Dispatch(Microsoft.Cci.ICodeVisitor visitor = {Microsoft.Cci.MutableCodeModel.CodeRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IExpression expression = {Microsoft.Cci.MutableCodeModel.MethodCall}) + 0x29 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.ExpressionStatement expressionStatement = {Microsoft.Cci.MutableCodeModel.ExpressionStatement}) + 0x55 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IExpressionStatement expressionStatement = {Microsoft.Cci.MutableCodeModel.ExpressionStatement}) + 0x5d bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Dispatcher.Visit(Microsoft.Cci.IExpressionStatement expressionStatement = {Microsoft.Cci.MutableCodeModel.ExpressionStatement}) + 0x34 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.ExpressionStatement.Dispatch(Microsoft.Cci.ICodeVisitor visitor = {Microsoft.Cci.MutableCodeModel.CodeRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IStatement statement = {Microsoft.Cci.MutableCodeModel.ExpressionStatement}) + 0x29 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.IStatement> statements = Count = 2) + 0x75 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.BlockStatement block = {Microsoft.Cci.MutableCodeModel.BlockStatement}) + 0x43 bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IBlockStatement block = {Microsoft.Cci.MutableCodeModel.BlockStatement}) + 0x4d bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.SourceMethodBody sourceMethodBody = {Microsoft.Cci.MutableCodeModel.SourceMethodBody}) + 0x6b bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.ISourceMethodBody sourceMethodBody = {Microsoft.Cci.MutableCodeModel.SourceMethodBody}) + 0x4d bytes   Microsoft.Cci.MutableCodeModel.dll!Microsoft.Cci.MutableCodeModel.CodeRewriter.Rewrite(Microsoft.Cci.IMethodBody methodBody = {Microsoft.Cci.MutableCodeModel.SourceMethodBody}) + 0x46 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.MethodDefinition method = {Microsoft.Cci.MutableCodeModel.MethodDefinition}) + 0x2f7 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.IMethodDefinition method = {Microsoft.Cci.MutableCodeModel.MethodDefinition}) + 0x4a bytes > WorkflowWeaver.exe!WorkflowWeaver.MyPropertySetterRewriter.Rewrite(Microsoft.Cci.IMethodDefinition method = {Microsoft.Cci.MutableCodeModel.MethodDefinition}) Line 158 + 0xb bytes C#  Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.IMethodDefinition> methods = Count = 49) + 0x7b bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.NamedTypeDefinition typeDefinition = {Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition}) + 0x2fc bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition namespaceTypeDefinition = {Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition}) + 0x28 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INamespaceTypeDefinition namespaceTypeDefinition = {Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition}) + 0x5a bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher.Visit(Microsoft.Cci.INamespaceTypeDefinition namespaceTypeDefinition = {Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition}) + 0x30 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition.Dispatch(Microsoft.Cci.IMetadataVisitor visitor = {Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INamespaceMember namespaceMember = {Microsoft.Cci.MutableCodeModel.NamespaceTypeDefinition}) + 0x29 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.INamespaceMember> namespaceMembers = Count = 26) + 0x7b bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.UnitNamespace unitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x79 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.NestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x28 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x59 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher.Visit(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x31 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.NestedUnitNamespace.Dispatch(Microsoft.Cci.IMetadataVisitor visitor = {Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INamespaceMember namespaceMember = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x29 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.INamespaceMember> namespaceMembers = Count = 1) + 0x7b bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.UnitNamespace unitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x79 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.NestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x28 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x59 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher.Visit(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x31 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.NestedUnitNamespace.Dispatch(Microsoft.Cci.IMetadataVisitor visitor = {Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INamespaceMember namespaceMember = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x29 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.INamespaceMember> namespaceMembers = Count = 1) + 0x7b bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.UnitNamespace unitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x79 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.NestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x28 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x59 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher.Visit(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x31 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.NestedUnitNamespace.Dispatch(Microsoft.Cci.IMetadataVisitor visitor = {Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INamespaceMember namespaceMember = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x29 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.INamespaceMember> namespaceMembers = Count = 1) + 0x7b bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.UnitNamespace unitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x79 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.NestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x28 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x59 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher.Visit(Microsoft.Cci.INestedUnitNamespace nestedUnitNamespace = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x31 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.NestedUnitNamespace.Dispatch(Microsoft.Cci.IMetadataVisitor visitor = {Microsoft.Cci.MutableCodeModel.MetadataRewriter.Dispatcher}) + 0x26 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.INamespaceMember namespaceMember = {Microsoft.Cci.MutableCodeModel.NestedUnitNamespace}) + 0x29 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(System.Collections.Generic.List<Microsoft.Cci.INamespaceMember> namespaceMembers = Count = 2) + 0x7b bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.UnitNamespace unitNamespace = {Microsoft.Cci.MutableCodeModel.RootUnitNamespace}) + 0x79 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.RootUnitNamespace rootUnitNamespace = {Microsoft.Cci.MutableCodeModel.RootUnitNamespace}) + 0x28 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.IRootUnitNamespace rootUnitNamespace = {Microsoft.Cci.MutableCodeModel.RootUnitNamespace}) + 0x5a bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.Module module = {Microsoft.Cci.MutableCodeModel.Assembly}) + 0x1f5 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.RewriteChildren(Microsoft.Cci.MutableCodeModel.Assembly assembly = {Microsoft.Cci.MutableCodeModel.Assembly}) + 0x177 bytes   Microsoft.Cci.MutableMetadataModel.dll!Microsoft.Cci.MutableCodeModel.MetadataRewriter.Rewrite(Microsoft.Cci.IAssembly assembly = {Microsoft.Cci.MutableCodeModel.Assembly}) + 0x5a bytes   WorkflowWeaver.exe!WorkflowWeaver.TestRewriter.Change(Microsoft.Cci.MutableCodeModel.Assembly assembly = {Microsoft.Cci.MutableCodeModel.Assembly}) Line 28 + 0xe bytes C#  WorkflowWeaver.exe!WorkflowWeaver.WorkflowRewriter.LoadAndRewriteAssembly(string assemblyName = "Focus.ApplicationServices.dll", string pdbName = "Focus.ApplicationServices.pdb", bool verificationMayFail = true) Line 43 + 0xd bytes C#  WorkflowWeaver.exe!WorkflowWeaver.Program.Main(string[] args = {string[1]}) Line 21 + 0x12 bytes C#  [Native to Managed Transition]   [Managed to Native Transition]   mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x6d bytes   Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x2a bytes   mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x63 bytes   mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool ignoreSyncCtx) + 0xb0 bytes   mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x2c bytes   mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes   [Native to Managed Transition]

Coordinator
Nov 29, 2011 at 5:42 AM

It seems that the base rewriter is choking on dummy objects. This is probably because you have not fully initialized something. In your code snippet above, it seems that you have not initialized the ThisArgument property of the method call. (The first expression in Arguments should instead be assigned to ThisArgument.) Also note that you should set IsVirtualCall to false, since this is a base call.

In terms of the expected usage pattern of the rewriter, I recommend that you you override RewriteChildren(SourceMethodBody) rather than overriding Rewrite(IMethodDefinition).

Nov 30, 2011 at 2:39 AM
Edited Nov 30, 2011 at 4:29 AM

Thanks Herman,

That resolved the exception. I am running into another problem. The rewritten assembly is corrupt.

PEVerify indicates “TaskDetails::set_Title][offset 0x0000000D] Unable to resolve token.”

I think ildasm tells me that the generic parameter for SetValue<T> is not being resolved.

I suspect the bad IL instruction is:

call instance void TaskDetails::SetValue<[1]>(!!0&,string,!!0)

but I was expecting to see:

call instance void TaskDetails::SetValue<string>(!!0&,string,!!0)

Should I be using something other than MethodCall?

Thanks for the help.

Regards,

Fernando

Coordinator
Nov 30, 2011 at 5:27 AM

I think your problem is that you are not using generic instances. In IL, all references to generic types must be to generic type instances. Likewise, all references to generic methods must be to generic method instances. For example, your code:

var baseSetValue = (MethodDefinition)entityBaseTypeReference.Methods.Single(typeDef => typeDef.Name.Value == "SetValue");

will get hold of the uninstantiated (i.e. template) generic method. Before you can call it, you need to instantiate it.

One way to learn the ropes of what your object model must look like is to first write the equivalent code in C# and then to decompile it and inspect the resulting CodeModel in the debugger.

Dec 4, 2011 at 1:56 AM

Thanks Herman. Your suggestions really helped me understand CCI better. The following code is what worked for me, although it still needs some refactoring.

 

        public override void RewriteChildren(SourceMethodBody sourceMethodBody)
        {
            
const string propertySetterPrefix = "set_";
            
const string setValueMethodName = "SetValue";
 
            
var methodDefinition = (MethodDefinition)sourceMethodBody.MethodDefinition;
 
            
if (methodDefinition.IsSpecialName && methodDefinition.Name.Value.StartsWith(propertySetterPrefix))
            {
                
string propertyName = methodDefinition.Name.Value.Remove(0, propertySetterPrefix.Length);
 
                
// Get the backing field for the property by finding a field with the same name as the property.
                
var propertyBackingFieldDefinition = (FieldDefinition)methodDefinition.ContainingTypeDefinition.Fields.SingleOrDefault(itd => string.Compare(itd.Name.Value, propertyName, true) == 0);
 
                
if (propertyBackingFieldDefinition == null)
                {
                    
base.RewriteChildren(sourceMethodBody); // do not implement the call to SetValue<T> if their is no backing field.
                    
return;
                }
 
                sourceMethodBody.Block = 
new BlockStatement();
                
var body = sourceMethodBody;
 
                
// Get the SetValue<T> method definition from the base class.
                
var baseSetValue = (MethodDefinition)EntityBaseTypeReference.Methods.Single(typeDef => typeDef.Name.Value == setValueMethodName);
                
ITypeReference genericType = methodDefinition.Parameters.First().Type;

 
                
var methodToCall = new GenericMethodInstanceReference()
                {
                    GenericMethod = baseSetValue,
                };
                methodToCall.GenericArguments = 
new List<ITypeReference>(1);
                methodToCall.GenericArguments.Add(genericType);
 
                
var call = new MethodCall()
                {
                    IsStaticCall = 
false,
                    IsVirtualCall = 
false,
                    MethodToCall = methodToCall,
                    Type = host.PlatformType.SystemVoid,
                    ThisArgument = 
new ThisReference()
                };
 
                call.Arguments.Add(
new AddressOf()
                {
                    Expression = 
new AddressableExpression()
                    {
                        Definition = propertyBackingFieldDefinition,
                        Instance = call.ThisArgument
                    },
                    Type = 
ImmutableManagedPointerType.GetManagedPointerType(genericType, host.InternFactory)
                });

 
                call.Arguments.Add(
new CompileTimeConstant() { Type = host.PlatformType.SystemString, Value = propertyName });
                call.Arguments.Add(
new BoundExpression { Definition = methodDefinition.Parameters.First() }); // value param of the property setter
                
var callSetValue = new ExpressionStatement() { Expression = call };
 
                
var block = (BlockStatement)body.Block;
                block.Statements.Add(callSetValue);
                block.Statements.Add(
new ReturnStatement());
 
                methodDefinition.Body = (
IMethodBody)body;
                RewritePropertySetter(sourceMethodBody);
            }

 
            
base.RewriteChildren(sourceMethodBody);
        }

 

After I got this working, I started creating a simple framework on top of CCI AST to make this type of code generation simpler for me. Here is what the same method looks like with my helper classes and extension methods.

 

        public override void RewriteChildren(SourceMethodBody sourceMethodBody)
        {
            
var methodDefinition = (MethodDefinition)sourceMethodBody.MethodDefinition;
 
            
if (methodDefinition.IsPropertySetter() && methodDefinition.HasBackingField())
            {
                
using (var body = new SmartMethodBody(sourceMethodBody))
                {
                    
using (var call = new SmartMethodCall("SetValue"))
                    {
                        call.Arguments.AddRef(body.PropertyName.ToCamel());
                        call.Arguments.Add(body.PropertyName);
                        call.Arguments.AddPropertySetterValue();
                    }
                }
            }
            
base.RewriteChildren(sourceMethodBody);
        }

 

Thanks again for all your help.

Fernando