jbevain / cecil

Cecil is a library to inspect, modify and create .NET programs and libraries.
MIT License
2.77k stars 630 forks source link

failed to add arguments to attributes that accepts objects #928

Closed Muitsonz closed 1 year ago

Muitsonz commented 1 year ago

I want to add a custom attribute by cecil, which accepts objects and whose ctor is defined as MyAttribute(object obj). When I tried the following code:

var ca = new CustomAttribute(...);
ca.ConstructorArguments.Add(new(
    module.ImportReference(typeof(object)),
    100));

System.InvalidCastException is thrown later that says 'Unable to cast object of type 'System.Int32' to type 'Mono.Cecil.CustomAttributeArgument'.' with the following stacktrace:

   at Mono.Cecil.SignatureWriter.WriteCustomAttributeElement(TypeReference type, CustomAttributeArgument argument)
   at Mono.Cecil.SignatureWriter.WriteCustomAttributeFixedArgument(TypeReference type, CustomAttributeArgument argument)
   at Mono.Cecil.SignatureWriter.WriteCustomAttributeConstructorArguments(CustomAttribute attribute)
   at Mono.Cecil.MetadataBuilder.GetCustomAttributeSignature(CustomAttribute attribute)
   at Mono.Cecil.MetadataBuilder.AddCustomAttributes(ICustomAttributeProvider owner)
   at Mono.Cecil.MetadataBuilder.AddParameter(UInt16 sequence, ParameterDefinition parameter, ParamTable table)
   at Mono.Cecil.MetadataBuilder.AddParameters(MethodDefinition method)
   at Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method)
   at Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type)
   at Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type)
   at Mono.Cecil.MetadataBuilder.AddTypes()
   at Mono.Cecil.MetadataBuilder.BuildTypes()
   at Mono.Cecil.MetadataBuilder.BuildModule()
   at Mono.Cecil.MetadataBuilder.BuildMetadata()
   at Mono.Cecil.ModuleWriter.BuildMetadata(ModuleDefinition module, MetadataBuilder metadata)
   at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
   at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
   at Mono.Cecil.ModuleDefinition.Write(Stream stream, WriterParameters parameters)
   at Mono.Cecil.AssemblyDefinition.Write(Stream stream, WriterParameters parameters)
   at Mono.Cecil.AssemblyDefinition.Write(Stream stream)

But if I used the specific data type instead of object on the attribute ctor, my code was able to run as expected. I wonder if the exception is thrown from https://github.com/jbevain/cecil/blob/56d4409b8a0165830565c6e3f96f41bead2c418b/Mono.Cecil/AssemblyWriter.cs#L2976C1-L2976C1, where a casting happens.

Can someone tell me the proper way of adding an object argument to custom attributes?

Zastai commented 1 year ago

If you specify "object" as the parameter type, there still needs to be a trace of the actual object type in the assembly. So the value there needs to be an instance of CustomAttributeArgument, specifying the actual type (here probably Int32) plus the value.

For future reference: if you want to know what you need to create, just compile something that includes it and look at how Cecil represents them. Then you know exactly what to create.

Note: for builtin types, it's easier(*) to go via TypeSystem; so module.TypeSystem.Object and module.TypeSystem.Int32.

(*) and also safer - otherwise you might accidentally emit a reference to the wrong type; if your code is running in net6.0 and you are working on a net472 assembly, importing a reference to typeof(object) may cause it to add a reference to the System.Runtime or System.Private.CoreLib instead of to mscorlib