1

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.

  • 5
    Have you tried just writing the code you want in C# and then using ILDasm to decompile it see what the compiler generates? – Sean Feb 17 '20 at 14:10
  • So what exactly are your needs? Currently we only see some not working code - for whatever reason. But not what this code should actually do and what it does instead. – MakePeaceGreatAgain Feb 17 '20 at 14:14
  • @Sean I would not be able to generate the code I need via C#, but there are many things that can be done in the CIL that C# can't generate, so I don't think that's the showstopper. I'll edit the question to add some info. – Francesco Bertolaccini Feb 17 '20 at 14:17
  • 1
    This can't be done with verifiable managed code unless you fancy generating a call to `FieldInfo.SetValue` (which is no better than you could do with C#). Setting a field for a managed object isn't that trivial, which is why there's no general, simple way to do it indirectly, unless you fancy the methods described [in this question](https://stackoverflow.com/q/30817924/4137916). My advice is to not do this and to generate specific code for specific fields, removing the delegate overhead by packing together all the setters you need in one call. – Jeroen Mostert Feb 17 '20 at 15:06
  • @JeroenMostert the linked question made me think that maybe I can solve this by pinning an object and obtaining a pointer to the field. I'll try it and report back if it works! – Francesco Bertolaccini Feb 17 '20 at 15:08
  • 1
    Even if you end up with some code that works, keep in mind that it may not perform very well compared to generated static field assignments, because such exercises essentially circumvent any optimizations the JIT itself can do (potentially defeating inlining, for example) and getting the nitty-gritty details correct (like respecting `volatile` and alignment) is not trivial either. It makes a little more sense to have types specifically geared for this (like flat structs); less so to try and do this for any arbitrary type and field. The result may be a case of "too clever by half". – Jeroen Mostert Feb 17 '20 at 15:19
  • @JeroenMostert "Pure performance" is something I already had to put to the side. The program I'm writing is a sort of "recompiler", so I don't have much choice on how the instructions are going to be executed, and I can only do my best to map them sensibly to CLI code. Your suggestion about flat structs is interesting and I will keep in mind should I hit a roadblock. – Francesco Bertolaccini Feb 17 '20 at 15:25
  • Could you create a [mcve] that is missing the crucial bit, so that we can safely understand *exactly* what it is that you're trying to accomplish? – Lasse V. Karlsen Feb 17 '20 at 15:29
  • @LasseV.Karlsen I'm not sure I can provide a _reproducible_ example, since I don't know how to do what I'm trying to do in the first place. I'll try and expand the scenario to see if I can make the question clearer. – Francesco Bertolaccini Feb 17 '20 at 15:32
  • The thing is that it doesn't sound to me that you know *what* you want to do. In order to store a value into a field, you must know **something** about that field, like the name of it. Even if you can't write legal C# code, I would very much be interested in seeing pseudo-code for what you want to do, because I still don't understand it. You cannot use `stfld` with anything but a already known field. Period. If you want to be able to store a value into a "dynamic" field, you must have a way to obtain the FieldInfo value, and then call SetValue on that, that's all you have. – Lasse V. Karlsen Feb 17 '20 at 15:35
  • ... or, you will have to create a double "compiler", a dynamic il-emit piece of code that emits more code that will eventually set the right field. – Lasse V. Karlsen Feb 17 '20 at 15:37
  • ie. if your C# "code" would be: `instance.FieldVariable = 42;`, then no, that is reflection, or `dynamic`, which isn't easier to emit code for. – Lasse V. Karlsen Feb 17 '20 at 15:38
  • @LasseV.Karlsen I _do_ have access to the FieldInfo of all the fields that the code might refer to, it's only that that FieldInfo can only be decided at runtime, not at compile time. Think of it like accessing structs in C via pointer arithmetic. – Francesco Bertolaccini Feb 17 '20 at 15:39
  • Again, to be clear, "pop a FieldInfo for the next stfld", this concept does not exist. `stfld` need the FieldInfo when the code is emitted. If you only can get to the actual FieldInfo at runtime, you're left with runtime reflection or runtime additional code generation. – Lasse V. Karlsen Feb 17 '20 at 15:39
  • If your il-emit code is running at runtime, and at runtime you already have the FieldInfo, then I don't see what the problem is. `il.Emit(OpCodes.Stfld, fieldInfoVariable);` should be all you need. – Lasse V. Karlsen Feb 17 '20 at 15:40
  • @LasseV.Karlsen I guess the issue here is conveying _which_ run time I'm referring to :) Referring to the example bytecode I've posted, imagine having a conditional jump after the first `PushFloat` that might push a reference to `someField2` instead of `someField`: in that case, I'd be able to know the FieldInfo of both fields, but I'd not be able to generate an instruction to assign to the correct one. – Francesco Bertolaccini Feb 17 '20 at 15:46
  • You can, through reflection. That is, you will have to emit code that calls reflection in order to do it. In other words, you would use the `FieldInfo` object on the stack to call its `SetValue` in order to assign a value to the corresponding field on an instance. – Lasse V. Karlsen Feb 17 '20 at 15:49
  • Yeah, I was trying to live that as an absolute last resort, but I guess it _is_ the simplest way. – Francesco Bertolaccini Feb 17 '20 at 15:52

1 Answers1

1

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.