Seperating pre-and post-base or chained constructor invocation statements for c # constructor

In order for the project to add mixins to C # using code weaving, I clone the code from the source type's mixin's parameterless instance constructor for the constructors on the target's type. To do this, I divide the constructor into three conceptual parts, and about this I ask for help.

Here are three parts:

  • Field initialization, which is performed before calling the base or chained constructor.
  • Basic or chained constructor invocation, including loading arguments onto the stack.
  • The actual constructor code, compiled from the source code written in the constructor body.

The basic idea is to multiplex the source constructor into these parts. The mux step also included checking local variables (stloc * and ldloc *), so it is important that the command separation is correct. Those target constructors that are called in the base constructors are the targets for cloning the code. Each of these will have the source section 1 cloned into its section 1, and will have a method call added to its section 3 that will invoke a new method containing the source 3 constructor section code in the target type. (It is put into its own method primarily because of the possibility of multiple exit points.)

I've read the constructor section of the C # spec instances, but other than confirming the intentional existence of the three sections that I see, I don't find it helpful. I've had some promising false starts on this one, and instead of trying another bad strategy that passes my test cases and then chokes as soon as it hits something I didn't think about, I hope I can get some the best input from someone with the best experience.

My current "next" thought is to loop through the instructions that ldarg.0 looks for and then find the next method call. If the next method call is a base or chained constructor, I can call this section 2 with the instructions described above as in section 1 and instructions after section 3. However, I am concerned that the instructions may not always have such a clean separation, and I not sure how I could be sure of this.

Another thought is that, since the specification specifically states that variable initialization instructions before a basic or chained constructor call, it may be more reliable to look for the end of instructions setting local fields. Unfortunately, I'm not sure what would be the best way to do this.

Here is an example of the target type and conceptual partitioning I'm looking for for constructors.

public class MultipleConstructorsTarget : MultipleConstructorsTargetBase
{
    public MultipleConstructorsTarget()
    {
        var values = Tuple.Create(783535, "KNion wineofn oianweiof nqiognui ndf", new UriBuilder { Host = "j.k.l" });

        this.OriginalUninitializedInt = values.Item1;
        this.OriginalUninitializedString = values.Item2;
        this.OriginalUninitializedObject =  values.Item3;
    }

    public MultipleConstructorsTarget(int i) : this(i, "A iuohiogfniouhe uihui iu.", new UriBuilder { Host = "g.h.i" }) { }

    public MultipleConstructorsTarget(int i, string j) : this(i, j, new UriBuilder { Host = "d.e.f" }) { }

    public MultipleConstructorsTarget(int i, string j, UriBuilder k)
        : base(i)
    {
        this.OriginalUninitializedInt = i;
        this.OriginalUninitializedString = j;
        this.OriginalUninitializedObject = k;
    }

    public int OriginalInitializedInt = 48685;
    public string OriginalInitializedString = "Tion3lao ehiuawh iuh buib ld";
    public UriBuilder OriginalInitializedObject = new UriBuilder { Host = "a.b.c" };

    public int OriginalUninitializedInt;
    public string OriginalUninitializedString;
    public UriBuilder OriginalUninitializedObject;
}

      

For MultipleConstructorsTarget()

Section 1

  IL_0000:  ldarg.0
  IL_0001:  ldc.i4     0xbe2d
  IL_0006:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedInt
  IL_000b:  ldarg.0
  IL_000c:  ldstr      "Tion3lao ehiuawh iuh buib ld"
  IL_0011:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedString
  IL_0016:  ldarg.0
  IL_0017:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_001c:  stloc.2
  IL_001d:  ldloc.2
  IL_001e:  ldstr      "a.b.c"
  IL_0023:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0028:  ldloc.2
  IL_0029:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedObject

      

Section 2

  IL_002e:  ldarg.0
  IL_002f:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTargetBase::.ctor()

      

Section 3

  IL_0034:  ldc.i4     0xbf4af
  IL_0039:  ldstr      "KNion wineofn oianweiof nqiognui ndf"
  IL_003e:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_0043:  stloc.1
  IL_0044:  ldloc.1
  IL_0045:  ldstr      "j.k.l"
  IL_004a:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_004f:  ldloc.1
  IL_0050:  call       class [mscorlib]System.Tuple`3<!!0,!!1,!!2> [mscorlib]System.Tuple::Create<int32,string,class [System]System.UriBuilder>(!!0, !!1, !!2)
  IL_0055:  stloc.0
  IL_0056:  ldarg.0
  IL_0057:  ldloc.0
  IL_0058:  callvirt   instance !0 class [mscorlib]System.Tuple`3<int32,string,class [System]System.UriBuilder>::get_Item1()
  IL_005d:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedInt
  IL_0062:  ldarg.0
  IL_0063:  ldloc.0
  IL_0064:  callvirt   instance !1 class [mscorlib]System.Tuple`3<int32,string,class [System]System.UriBuilder>::get_Item2()
  IL_0069:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedString
  IL_006e:  ldarg.0
  IL_006f:  ldloc.0
  IL_0070:  callvirt   instance !2 class [mscorlib]System.Tuple`3<int32,string,class [System]System.UriBuilder>::get_Item3()
  IL_0075:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedObject
  IL_007a:  ret

      

For MultipleConstructorsTarget(int i)

Section 1
(Empty)

Section 2

  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "A iuohiogfniouhe uihui iu."
  IL_0007:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_000c:  stloc.0
  IL_000d:  ldloc.0
  IL_000e:  ldstr      "g.h.i"
  IL_0013:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0018:  ldloc.0
  IL_0019:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::.ctor(int32, string, class [System]System.UriBuilder)

      

Section 3

  IL_001e:  ret

      

For MultipleConstructorsTarget(int i, string j)

Section 1
(Empty)

Section 2

  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  ldstr      "d.e.f"
  IL_000f:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0014:  ldloc.0
  IL_0015:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::.ctor(int32, string, class [System]System.UriBuilder)

      

Section 3

  IL_001a:  ret

      

For MultipleConstructorsTarget(int i, string j, UriBuilder k)

Section 1

  IL_0000:  ldarg.0
  IL_0001:  ldc.i4     0xbe2d
  IL_0006:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedInt
  IL_000b:  ldarg.0
  IL_000c:  ldstr      "Tion3lao ehiuawh iuh buib ld"
  IL_0011:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedString
  IL_0016:  ldarg.0
  IL_0017:  newobj     instance void [System]System.UriBuilder::.ctor()
  IL_001c:  stloc.0
  IL_001d:  ldloc.0
  IL_001e:  ldstr      "a.b.c"
  IL_0023:  callvirt   instance void [System]System.UriBuilder::set_Host(string)
  IL_0028:  ldloc.0
  IL_0029:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalInitializedObject

      

Section 2

  IL_002e:  ldarg.0
  IL_002f:  ldarg.1
  IL_0030:  call       instance void Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTargetBase::.ctor(int32)

      

Section 3

  IL_0035:  ldarg.0
  IL_0036:  ldarg.1
  IL_0037:  stfld      int32 Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedInt
  IL_003c:  ldarg.0
  IL_003d:  ldarg.2
  IL_003e:  stfld      string Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedString
  IL_0043:  ldarg.0
  IL_0044:  ldarg.3
  IL_0045:  stfld      class [System]System.UriBuilder Bix.Mixers.Fody.TestMixinTargets.MultipleConstructorsTarget::OriginalUninitializedObject
  IL_004a:  ret

      

I use Mono.Cecil for all my reading and writing. The Bix.Mixers project code can be found at https://github.com/rileywhite/Bix.Mixers.Fody if you're interested. The specific file this question pertains to is at https://github.com/rileywhite/Bix.Mixers.Fody/blob/master/src/Bix.Mixers/Fody/ILCloning/ConstructorMultiplexer.cs .

+3


source to share


1 answer


The strategy that seems to work is to list the constructor instructions, grouping them as follows:

  • If the instruction is not ldarg.0, it is put into a group by itself.
  • If the instruction is ldarg.0, then all of the following instructions are grouped together with it until one of the following conditions is met:
    • There are call commands, virtcall or calli.
    • An instruction is the last instruction before another ldarg.0.
    • All instructions are listed.

Once the group has been identified, the last command in the group is checked. If it is a call instruction and the operand is a base or chained constructor, then the group is identified as section 2, which means that the previous instructions are section 1 and the remaining instructions are section 3.

Here is the code that, given the index to start the search, identifies a group of instructions based on these rules.



public static bool TryGetNext(IList<Instruction> sourceInstructions, int firstIndex, out InstructionGroup instructionGroup)
{
    Contract.Requires(sourceInstructions != null);

    if (firstIndex < 0 || firstIndex >= sourceInstructions.Count)
    {
        instructionGroup = null;
        return false;
    }

    var instructions = new List<Instruction>();
    var instruction = sourceInstructions[firstIndex];
    instructions.Add(instruction);


    int lastIndex;
    if (instruction.OpCode.Code != Code.Ldarg_0) { lastIndex = firstIndex; }
    else
    {
        int i;
        // calls into base and chained constructors start like this
        // so we'll look for the next call instruction or any instruction where the next instruction is another ldarg.0
        // meaning that the stack was cleared at some point
        // there is no assumption that this grouping is generally useful, but the hope is that it will catch constructor calls in this specific case
        var isLastInstructionFound = false;
        for (i = firstIndex + 1; !isLastInstructionFound && i < sourceInstructions.Count; i++)
        {
            instruction = sourceInstructions[i];
            instructions.Add(instruction);
            if (instruction.OpCode.Code == Code.Call ||
                instruction.OpCode.Code == Code.Callvirt ||
                instruction.OpCode.Code == Code.Calli ||
                (instruction.Next != null && instruction.Next.OpCode.Code == Code.Ldarg_0))
            {
                isLastInstructionFound = true;
            }
        }

        lastIndex = i - 1;
    }

    instructionGroup = new InstructionGroup(firstIndex, lastIndex, instructions);
    return true;
}

      

If you're interested, you can see the full code at https://github.com/rileywhite/Bix.Mixers.Fody/blob/0.1.7/src/Bix.Mixers/Fody/ILCloning/ConstructorMultiplexer.cs .

Even though this seems to work, I won't pick this as the definitive answer because it's another heuristic. I would like to get a real answer from someone with more experience.

0


source







All Articles