Modifying existing attribute

441 views
Skip to first unread message

thargy

unread,
Feb 22, 2012, 2:09:45 PM2/22/12
to mono-cecil
I have a CustomAttribute that contains public properties. I want to
set these properties at compile time based on the location.

e.g.

public class AttachDataAttribute : Attribute
{
/// <summary>
/// The type this attribute was attached to (if attached to a
type).
/// </summary>
/// <remarks>
/// An attribtue that is attached to an assembly directly is
not attached to a type,
/// and so this value will be <see langword="null"/>.
/// <para>If present this value is set at compile time by the
weaver. It is entirely
/// optional in a weave. </para>
/// <para>To match the name must match AttachedType and be a
readonly field
/// of Type <see cref="Type"/>.</para></remarks>
public Type AttachedType;
}


[AttachData]
public class AttachmentDataTester
{
[AttachData]
public int Method()
{
return 0;
}
}

In this case I want to set the 'AttachedType' property automatically
to the 'AttachmentDataTester' for the two cases.

I have the CustomAttributes, and I believe I need to use
Constructor.Paramter.Add(), however, Constructor.Parameter is null in
the case there are no parameters already.

Do I have to remove the existing attribute and re-add it or is it
possible to add the extra parameters (i.e.
AttachedType=typeof(AttachmentDataTester) to the existing attribute,
if so how?

I've used Type as an example as this has been mentioned before, I
believe you suggest the TypeReference should be set to System.Type
(can do) and the value should be TypeReference.FullName (i.e. a
string) - is this right?

Thanks in anticipation of your help.

Jb Evain

unread,
Feb 22, 2012, 2:24:51 PM2/22/12
to mono-...@googlegroups.com
Hey,

On Wed, Feb 22, 2012 at 8:09 PM, thargy <uncle...@googlemail.com> wrote:
> I have the CustomAttributes, and I believe I need to use
> Constructor.Paramter.Add(), however, Constructor.Parameter is null in
> the case there are no parameters already.

If you have a CustomAttribute.ConstructorParameters collection, it
means that you're using an old version of Mono.Cecil. I strongly
suggest you use a recent one from http://github.com/jbevain/cecil.

If you have a CustomAttribute.ConstructorArguments collections, you're good.

And if you want to set properties, you can not use constructor
arguments. Remember that a custom attributes is mapped to a
constructor invocation, plus setting some fields and properties. So in
your example, AttachDataAttribute has default constructor with no
parameter, and thus, no argument is passed to instantiate it.

What you want to do, representing [AttachData(Type =
typeof(Foo.Bar))], is setting a value through a property of a custom
attribute.

You can do it with something like:

var module = ... as ModuleDefinition;

var attachData = new CustomAttribute (module.Import (theAttachDataConstructor));

var value = module.Import (fooBar); // create a reference to Foo.Bar
scoped for module
var type = module.Import (typeof (Type)); // create a reference to
System.Type scoped for module

/*
the issue with using typeof (Type) is that it will create a reference
to the version of System.Type your CLR is running on
so if you're instrumenting a 2.0 assembly from a 4.0 program, you
better pass a Cecil representation of the 2.0 System.Type
*/

attachData.Properties.Add (new CustomAttributeNamedArgument ("Type",
new CustomAttributeArgument (type, value)));

The code is not guaranteed to be exact and is written from memory.

Jb

thargy

unread,
Feb 22, 2012, 3:53:11 PM2/22/12
to mono-cecil
Thanks for the quick response as always.

> If you have a CustomAttribute.ConstructorParameters collection, it
> means that you're using an old version of Mono.Cecil. I strongly
> suggest you use a recent one fromhttp://github.com/jbevain/cecil.
>
> If you have a CustomAttribute.ConstructorArguments collections, you're good.

I am using the Nuget published on 12/2/2012 v. 0.9.5.2.

I have both CustomAttribute.ConstructorArguments AND
CustomAttribute.Constructor.Parameters, a quick check of the source on
GitHub reveals that is still the case on the latest branch. Perhaps
you misread my comment and missed the dot between the CustomAttribute
and Parameters?

> And if you want to set properties, you can not use constructor
> arguments. Remember that a custom attributes is mapped to a
> constructor invocation, plus setting some fields and properties. So in
> your example, AttachDataAttribute has default constructor with no
> parameter, and thus, no argument is passed to instantiate it.

Yes, I get that. I don't want to use constructor arguments
deliberately, the idea is that the properties are set automatically
without using the constructor, as it the example given.

> What you want to do, representing [AttachData(Type =
> typeof(Foo.Bar))], is setting a value through a property of a custom
> attribute.
>
> You can do it with something like:
>
> var module = ... as ModuleDefinition;
>
> var attachData = new CustomAttribute (module.Import (theAttachDataConstructor));
> var value = module.Import (fooBar); // create a reference to Foo.Bar
> scoped for module

I'm not trying to add a new Attribute, I'm trying to modify an
existing attribute in the assembly (as per example).

> var type = module.Import (typeof (Type)); // create a reference to
> System.Type scoped for module
> /*
> the issue with using typeof (Type) is that it will create a reference
> to the version of System.Type your CLR is running on
> so if you're instrumenting a 2.0 assembly from a 4.0 program, you
> better pass a Cecil representation of the 2.0 System.Type
> */

Further to previous question, I get the type by resolving the
module.TypeSystem.CorLib to an AssemblyDefinition and using GetType on
the MainModule, this guarantees I get the type from the right
framework library and avoid creating a dependency on the Type in the
framework currently being used.

> attachData.Properties.Add (new CustomAttributeNamedArgument ("Type",
> new CustomAttributeArgument (type, value)));

Which is fine if adding a new attribute, however I'm trying to modify
an existing attribute. Properties.Add works fine but has no effect on
the resulting assembly (I tried with both Fields & Properties, as I
want to support both).

To come back to my problem, is the only way to remove the existing
attribute and add a new one, or is it possible to do this on an
existing attribute?

If I have to remove the attribute first, how would I do that?

Obviously I'm hoping it is possible to do with the existing attribute.

> The code is not guaranteed to be exact and is written from memory.
>
> Jb

Thanks for your quick response!

Jb Evain

unread,
Feb 22, 2012, 4:05:59 PM2/22/12
to mono-...@googlegroups.com
On Wed, Feb 22, 2012 at 9:53 PM, thargy <uncle...@googlemail.com> wrote:
>> If you have a CustomAttribute.ConstructorParameters collection, it
> I have both CustomAttribute.ConstructorArguments AND
> CustomAttribute.Constructor.Parameters, a quick check of the source on
> GitHub reveals that is still the case on the latest branch.  Perhaps
> you misread my comment and missed the dot between the CustomAttribute
> and Parameters?

I actually did. Cecil 0.6 has a ConstructorParameters collection that
was renamed in 0.9, hence the confusion.
CustomAttribute.Constructor.Parameters actually point to the
parameters of the constructors. You don't want to modify that unless
you want to modify the constructor of say, AttachDataAttribute.

> I'm not trying to add a new Attribute, I'm trying to modify an
> existing attribute in the assembly (as per example).

The principle is the same, instead of creating a new one, you can just
modify an existing one. You can add a CustomAttributeNamedArgument in
the Properties, and if everything is correct, it will be serialized at
Write time.

> Further to previous question, I get the type by resolving the
> module.TypeSystem.CorLib to an AssemblyDefinition and using GetType on
> the MainModule, this guarantees I get the type from the right
> framework library and avoid creating a dependency on the Type in the
> framework currently being used.

This work too.

> To come back to my problem, is the only way to remove the existing
> attribute and add a new one, or is it possible to do this on an
> existing attribute?

It shouldn't be a problem to modify an existing one.

Jb

thargy

unread,
Feb 22, 2012, 4:21:20 PM2/22/12
to mono-cecil
Thank you again!

The code I had had a stupid unrelated bug in it, and I thought I'd
seen the Properties & Fields collections being updated, when in fact
they weren't being.

Your feedback made me look again and I spotted my mistake (I was
skipping population if the collections were initially empty, DOH!)

I'm really cooking on gas now and should be able to complete my Open
Source Weaver this week :)! Thanks so much.

I have a working ModuleInitializer weaver (allows methods to be called
when an Assembly is loaded - brilliant for automatic assembly
configuration loading!), I now have a working attribute property
populater (auto populates location information on marked attributes).
My challenge for tomorrow is going to be the toughest - here's a
hint :)


[Export("<Aspect>")]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field |
AttributeTargets.Event | AttributeTargets.Property |
AttributeTargets.Class)]
public class MethodInterceptionAttribute : Attribute
{
[Export("<InterceptBegin>")]
private bool MethodStart3([NotNull]dynamic parameters)
{
parameters.myFirstParam = "Test";
parameters.anotherParam = 3;
parameters.ExtraRandomData = Guid.NewGuid();

// Return true to continue, or set
parameters.@ReturnValue@ and return false.
return true;
}

[Export("<InterceptEnd>")]
private T MethodEnd2<T>([NotNull]dynamic parameters)
{
Contract.Assert(parameters.ExtraRandomData.GetType() ==
typeof (Guid));
return default(T);
}
}

Basically simple interception with no dependencies on compile time
assemblies :) (So no more NuGet dependency hell for me)


Thank you so much for your continuing help!!!
Reply all
Reply to author
Forward
0 new messages