Search code examples
c#cilreflection.emitfieldinfo

Dynamically assign a field


I'm trying to generate code for dynamically assigning field values to a dynamic class. Basically, what I'd ideally be able to do is something to this effect:

il.Emit(OpCodes.Ldarg_0); // Object instance
il.Emit(OpCodes.Ldc_I4_0); // Value to assign to the field
il.Emit(OpCodes.Ld???, fieldInfo); // FieldInfo to assign the value to
il.Emit(OpCodes.Stfld); // Some kind of Stfld that pops the field and the value and assigns it

I was not able to find any instruction that would suit my needs. Another idea I had was to generate a setter method for each field and invoke that method, but I did not find a method to do that without converting it into a Delegate, which generated a lot of boilerplate code.

Does anyone have a better solution?

EDIT: The issue is that the field that needs to be assigned has to be found on the stack, and somehow pop'd when the time to assign it comes. Unfortunately, none of the CIL instructions support popping a fieldInfo to assign to it, but maybe there are other solutions I had not thought about.

EDIT2: I'll give a bit more of the surrounding scenario, hopefully the context will make things clearer.

I'm trying to make a sort of "recompiler" from a stack-based vm bytecode to CIL. The bytecode in question does not access fields in a struct like the CIL does it, i.e. statically. Instead, the reference to the field to be accessed is pushed on the stack, and the store instruction takes care of the rest.

Here's a sample of what this bytecode might look like:

PushFloat 0.0
PushField [someField]
SetField

What I'd like to obtain would be something like the code I've written above, but the CIL only supports assigning to fields which are known at codegen time.


Solution

  • I've solved this by using ldflda and stind:

    il.Emit(OpCodes.Ldarg_0); // Object instance
    il.Emit(OpCodes.Ldflda, fieldInfo); // Loads reference to field
    il.Emit(OpCodes.Conv_U); // Converts to pointer
    il.Emit(OpCodes.Ldc_I4_0); // Something to put in the field
    il.Emit(OpCodes.Stind_I4); // Put the value in the field
    

    Which appears to be doing roughly what I had initially set out to do.