Friday, 15 July 2011

Separating pre- and post- base or chained constructor call instructions for a C# constructor -



Separating pre- and post- base or chained constructor call instructions for a C# constructor -

for project add together mixins c# using code weaving, cloning code source mixin type's parameterless instance constructor constructors in target type. purposes of this, split constructor 3 conceptual parts, , asking help with.

here 3 parts:

field initialization runs before base of operations or chained constructor call. base or chained constructor call, including loading of arguments onto stack. actual constructor code compiled source code written in constructor body.

the basic thought multiplex source constructor these pieces. multiplexing step involved checking local variables (stloc* , ldloc*), it's of import instruction separation correct. target constructors phone call base of operations constructors code cloning targets. each 1 have source's section 1 cloned section 1 , have method phone call added section 3 invoke new method contains source constructor's section 3 code within target type. (it's set own method because of possibility of multiple exit points.)

i've read through c# spec's instance constructor section, other confirming intentional existence of 3 sections i'm seeing, don't find helpful. i've had couple of promising false starts on this, , rather seek yet bad strategy passes test cases , chokes hits didn't think of, i'm hoping can improve input improve experience.

my current "next" thought cycle through instructions looking ldarg.0, , observe next method call. if next method phone call base of operations or chained constructor, can phone call section 2, instructions before section 1 , instructions after section 3. i'm concerned, though, instructions may not have such clean separation, , i'm not sure how of such thing.

another thought because spec states variable initialization instructions come before base of operations or chained constructor call, might more reliable end of instructions set local fields. unfortunately, i'm not best way go that.

here's illustration of target type , conceptual breakdown i'm looking of 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: phone 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: phone 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: phone 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: phone 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: phone 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'm using mono.cecil of il reading , writing. can find bix.mixers project code @ https://github.com/rileywhite/bix.mixers.fody if interested. specific file question in regards @ https://github.com/rileywhite/bix.mixers.fody/blob/master/src/bix.mixers/fody/ilcloning/constructormultiplexer.cs.

a strategy seems work enumerate through constructor instructions, grouping them follows:

if instruction not ldarg.0, set grouping itself. if instruction ldarg.0, next instructions grouped until 1 of next conditions met: a call, virtcall, or calli instructions encountered. an instruction lastly instruction before ldarg.0. all instructions have been enumerated.

once grouping has been identified, lastly instruction in grouping examined. if phone call instruction , operand base of operations or chained constructor, grouping identified section 2, meaning preceding instructions section 1 , remaining instructions section 3.

here's code that, given index @ start looking, identifies grouping 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; homecoming 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 base of operations , chained constructors start // we'll next phone call instruction or instruction next instruction ldarg.0 // meaning stack cleared @ point // there no assumption grouping useful, hope grab constructor calls in specific case var islastinstructionfound = false; (i = firstindex + 1; !islastinstructionfound && < 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 = - 1; } instructiongroup = new instructiongroup(firstindex, lastindex, instructions); homecoming true; }

if you're interested, can see total code @ https://github.com/rileywhite/bix.mixers.fody/blob/0.1.7/src/bix.mixers/fody/ilcloning/constructormultiplexer.cs.

even though seems work, not select final reply because it's still heuristic. i'd love real reply more experience.

c# constructor mixins cil mono.cecil

No comments:

Post a Comment