c# - Getting the field a MemberRef metadata token refers to -


fair warning, may tad esoteric , tricky.

given memberref (more explanation below) extracted cil stream, how figure out field, if any, points (and fieldinfo it)?

here's i've figured out far

according ecma 335 standard, memberref metadata token lookup in table point either field metadata token or method metadata token. metadata token beginning 0x0a memberref.

enter image description here

i hadn't encountered 1 of these before, don't seem uncommon. able 1 generated using following anonymous type in method:

new {     = new datetime(1234, 5, 6, 7, 8, 9, datetimekind.utc),     b = (datetime?)null } 

when grab method body via reflection (get propertyinfo, getmethod, methodbody, get il) a's method is:

[2, 123, 79, 0, 0, 10, 42] 

which converts to:

ldarg.0 ldfld 0x0a00004f ret 

if reflect in , backing field (relying on name similarity choose <a>i__field, nothing algorithmic) see metadatatoken 0x04000056.

note tokens generated may vary between compilations.

a token starting 0x04 field: enter image description here

most of time (for non-anonymous objects in limited testing, in fact) il contains field metadata token. easy turn fieldinfo via module.resolvefield(int), figuring out memberref tripping me up.

cycling through other resolvexxx methods on module, 1 can memberref resolvesignature. when run on above memberref, returns array of [6, 19, 0]. don't know make of that.

the code i'm working on unfinished, public. error can seen running this test, causing exception thrown when field lookup fails on this line. note test unfinished, it's not expected succeed yet shouldn't die there either.

anybody have idea make of signature, or other way field's metadata token (and fieldinfo) memberref?

here's linqpad program script reproduces problem. it's pretty big, there's lot of boilerplate.

void main() {     init();      var obj =          new         {             = new datetime(1234, 5, 6, 7, 8, 9, datetimekind.utc),             b = (datetime?)null         };      var usage = propertyfieldusage(obj.gettype());     usage.dump(); }  private static dictionary<int, system.reflection.emit.opcode> onebyteops; private static dictionary<int, system.reflection.emit.opcode> twobyteops;  public static dictionary<propertyinfo, list<fieldinfo>> propertyfieldusage(type t) {   var ret = new dictionary<propertyinfo, list<fieldinfo>>();    var props = t.getproperties(bindingflags.public | bindingflags.instance | bindingflags.nonpublic).where(p => p.getmethod != null);    var module = t.module;    foreach (var prop in props)   {       var getmtd = prop.getmethod;       var mtdbody = getmtd.getmethodbody();       var il = mtdbody.getilasbytearray();        var fieldhandles = _getfieldhandles(il);        var fieldinfos =            fieldhandles               .select(                   f => module.resolvefield(f)               ).tolist();        ret[prop] = fieldinfos;   }    return ret; }  // define other methods , classes here private static list<int> _getfieldhandles(byte[] cil) {   var ret = new list<int>();    int = 0;   while (i < cil.length)   {       int? fieldhandle;       system.reflection.emit.opcode ignored;       var startsat = i;       += _readop(cil, i, out fieldhandle, out ignored);        if (fieldhandle.hasvalue)       {           ret.add(fieldhandle.value);       }   }    return ret; }  private static int _readop(byte[] cil, int ix, out int? fieldhandle, out system.reflection.emit.opcode opcode) {   const byte continueopcode = 0xfe;    int advance = 0;    byte first = cil[ix];    if (first == continueopcode)   {       var next = cil[ix + 1];        opcode = twobyteops[next];       advance += 2;   }   else   {       opcode = onebyteops[first];       advance++;   }    fieldhandle = _readfieldoperands(opcode, cil, ix, ix + advance, ref advance);    return advance; }  private static int? _readfieldoperands(system.reflection.emit.opcode op, byte[] cil, int instrstart, int operandstart, ref int advance) {   func<int, int> readint = (at) => cil[at] | (cil[at + 1] << 8) | (cil[at + 2] << 16) | (cil[at + 3] << 24);    switch (op.operandtype)   {       case system.reflection.emit.operandtype.inlinebrtarget:           advance += 4;           return null;        case system.reflection.emit.operandtype.inlineswitch:           advance += 4;           var len = readint(operandstart);           var offset1 = instrstart + len * 4;           (var = 0; < len; i++)           {               advance += 4;           }           return null;        case system.reflection.emit.operandtype.shortinlinebrtarget:           advance += 1;           return null;        case system.reflection.emit.operandtype.inlinefield:           advance += 4;           var field = readint(operandstart);           return field;        case system.reflection.emit.operandtype.inlinetok:       case system.reflection.emit.operandtype.inlinetype:       case system.reflection.emit.operandtype.inlinemethod:           advance += 4;           return null;        case system.reflection.emit.operandtype.inlinei:           advance += 4;           return null;        case system.reflection.emit.operandtype.inlinei8:           advance += 8;           return null;        case system.reflection.emit.operandtype.inlinenone:           return null;        case system.reflection.emit.operandtype.inliner:           advance += 8;           return null;        case system.reflection.emit.operandtype.inlinesig:           advance += 4;           return null;        case system.reflection.emit.operandtype.inlinestring:           advance += 4;           return null;        case system.reflection.emit.operandtype.inlinevar:           advance += 2;           return null;        case system.reflection.emit.operandtype.shortinlinei:           advance += 1;           return null;        case system.reflection.emit.operandtype.shortinliner:           advance += 4;           return null;        case system.reflection.emit.operandtype.shortinlinevar:           advance += 1;           return null;        default: throw new exception("unexpected operand type [" + op.operandtype + "]");   } }  static void init() {   var onebyte = new list<system.reflection.emit.opcode>();   var twobyte = new list<system.reflection.emit.opcode>();    foreach (var field in typeof(system.reflection.emit.opcodes).getfields(bindingflags.public | bindingflags.static))   {       var op = (system.reflection.emit.opcode)field.getvalue(null);        if (op.size == 1)       {           onebyte.add(op);           continue;       }        if (op.size == 2)       {           twobyte.add(op);           continue;       }        throw new exception("unexpected op size " + op);   }    onebyteops = onebyte.todictionary(d => (int)d.value, d => d);   twobyteops = twobyte.todictionary(d => (int)(d.value & 0xff), d => d); } 

the trick here is generic type (the second parameter resolvefield), , know getter not generic method (the final parameter resolvefield), need use resolvefield so:

var obj = new {     = new datetime(1234, 5, 6, 7, 8, 9, datetimekind.utc),     b = (datetime?)null };  parse(obj, "a"); parse(obj, "b");  static void parse(object obj, string property) {     var blob = obj.gettype().getproperty(property).getgetmethod()        .getmethodbody().getilasbytearray();     // hard-code know token @ offset 2     int token = bitconverter.toint32(blob, 2);      var field = obj.gettype().module.resolvefield(token,         obj.gettype().getgenericarguments(), null);     console.writeline(field.name);     console.writeline(field.metadatatoken); } 

in more general case, don't know type (i.e. might non-generic type) , method (although strictly speaking, property accessors never generic in own right, shows broad usage):

static memberinfo resolvemember(this methodinfo method, int metadatatoken) {     type type = method.declaringtype;     type[] typeargs = null, methodargs = null;      if (type.isgenerictype || type.isgenerictypedefinition)         typeargs = type.getgenericarguments();     if (method.isgenericmethod || method.isgenericmethoddefinition)         methodargs = method.getgenericarguments();      return type.module.resolvemember(metadatatoken, typeargs, methodargs); } 

Comments

Popular posts from this blog

java.util.scanner - How to read and add only numbers to array from a text file -

rewrite - Trouble with Wordpress multiple custom querystrings -