Hi,
Sometimes constants used to initialize fields are stored using the
wrong datatype in the corresponding metadata table. The IL then relies
on coercions to have consistent semantics as if the correct type was
used. Especially ILDasm 1.1 is prone to this error:
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 2:0:0:0
}
.assembly CostantCoercion {}
.module CostantCoercion.dll
.class private auto ansi Foo
{
.field private static literal int32 FieldI = uint32(0x80000000) // ==
int.MinValue, will be coerced
.field private static literal uint32 FieldU = int32(0x80000000) // ==
int.MinValue, will be coerced
.method public specialname rtspecialname instance void .ctor () cil
managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor ()
ret
}
}
In here, we have two literals, both initialized with a constant of the
wrong type that will get implicitly coerced. I know IL like this
should be discouraged but I do encounter this in the wild. Cecil
chokes on constructs like this because the
AssemblyWriter.WritePrimitiveValue function is working on boxed value
types that are of the type indicated in the constant metadata table.
When Cecil writes out the field, it casts the constant (e.g. uint) to
the type of the field (int) which will result in an
InvalidCastException because we're using boxed types. Using the values
in an unchecked context (the C# default) this would not result in an
error and produce the correct semantics.
Therefore, we need to unbox safely and then cast values (instead of
boxes) unchecked. A Patch with test and repro is on github (sent you a
pull request). Since I'm starting to use Cecil heavily, chances are
I'll suggest further improvements/patches. Please let me know what's
you're preferred way of accepting patches.
See:
http://github.com/JohannesRudolph/cecil/commit/568c5916a434085524278b1f2295006b15d036f8
Kind regards,
Johannes Rudolph
--
--
mono-cecil