2016-01-21 16 views
5

से विधि की एक प्रति बनाएं मैं प्रतिबिंब का उपयोग कर रनटाइम के दौरान किसी विधि की एक प्रति बनाने की कोशिश कर रहा हूं।आईएल

मेरे पास निम्न कोड है।

साझा भाषा क्रम गलत कार्यक्रम का पता चला:

public static R CopyMethod<T, R>(Func<T, R> f, T t) 
{ 
    AppDomain currentDom = Thread.GetDomain(); 
    AssemblyName asm = new AssemblyName(); 
    asm.Name = "DynamicAssembly"; 
    AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run); 
    ModuleBuilder mbl = abl.DefineDynamicModule("Module"); 
    TypeBuilder tbl = mbl.DefineType("Type"); 
    var info = f.GetMethodInfo(); 
    MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray()); 

    byte[] il = f.Method.GetMethodBody().GetILAsByteArray(); 

    mtbl.CreateMethodBody(il, il.Length); 
    Type type = tbl.CreateType(); 
    Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>; 
    return method(t); 
} 

अंतिम पंक्ति संदेश के साथ एक अपवाद फेंकता है।

क्या ऐसा करने का कोई और तरीका है? मैं सीधे आईएल का उपयोग करने के बजाय विधि के पार्स पेड़ पाने में सक्षम होना पसंद करूंगा।

संपादित करें 1:

मैं निम्नलिखित समारोह के साथ परीक्षण कर रहा हूँ।

public static int Fib(int n) 
{ 
    /*if (n < 2) 
     return 1; 
    return Fib(n - 1) + Fib(n - 2);*/ 
    return n; 
} 

निम्न पंक्ति के साथ परीक्षण।

int x = Copy.CopyMethod(Copy.Fib, 10); 

संपादित 2:

रोब के जवाब ऊपर मुद्दे के समाधान में मदद करता है। हालांकि, Fib() विधि का उपयोग करते समय थोड़ा अधिक जटिल (उदा। टिप्पणी की गई फिबोनाची विधि), प्रोग्राम निम्न संदेश से क्रैश हो जाता है।

सूचकांक नहीं मिला। (HRESULT से अपवाद: 0x80131124)

संपादित 3:

मैं टिप्पणी से कई सुझाव की कोशिश की है, लेकिन मेटाडाटा टोकन गतिशील विधानसभा के भीतर स्थित नहीं किया जा सकता।

public static R CopyMethod<T, R>(Func<T, R> f, T t) 
{ 
    AppDomain currentDom = Thread.GetDomain(); 
    AssemblyName asm = new AssemblyName("DynamicAssembly"); 
    AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run); 
    ModuleBuilder mbl = abl.DefineDynamicModule("Module"); 
    TypeBuilder tbl = mbl.DefineType("Type"); 
    MethodInfo info = f.GetMethodInfo(); 
    MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray()); 
    MethodBody mb = f.Method.GetMethodBody(); 
    byte[] il = mb.GetILAsByteArray(); 

    OpCode[] opCodes = GetOpCodes(il); 
    Globals.LoadOpCodes(); 
    MethodBodyReader mbr = new MethodBodyReader(info); 
    string code = mbr.GetBodyCode(); 
    Console.WriteLine(code); 

    ILGenerator ilg = mtbl.GetILGenerator(); 
    ilg.DeclareLocal(typeof(int[])); 
    ilg.DeclareLocal(typeof(int)); 
    for (int i = 0; i < opCodes.Length; ++i) 
    { 
     if (opCodes[i].OperandType == OperandType.InlineType) 
     { 
      int token; 
      Type tp = info.Module.ResolveType(token = BitConverter.ToInt32(il, i + 1), info.DeclaringType.GetGenericArguments(), info.GetGenericArguments()); 
      ilg.Emit(opCodes[i], tp.MetadataToken); 
      i += 4; 
      continue; 
     } 
     if (opCodes[i].FlowControl == FlowControl.Call) 
     { 
      int token; 
      MethodBase mi = info.Module.ResolveMethod(token = BitConverter.ToInt32(il, i + 1)); 
      ilg.Emit(opCodes[i], mi.MetadataToken); 
      i += 4; 
      continue; 
     } 
     ilg.Emit(opCodes[i]); 
    } 

    Type type = tbl.CreateType(); 
    Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>; 
    return method(t); 
} 

निम्नलिखित भी काम नहीं करता है।

var sigHelp = SignatureHelper.GetLocalVarSigHelper(mtbl.Module); 
mtbl.SetMethodBody(il, mb.MaxStackSize, sigHelp.GetSignature(), null, new int[] { 3 }); 

मैं बदल रहा मेटाडाटा निम्नलिखित तरीके से (मुझे लगता है कि यह सभी मामलों में काम नहीं करेगा, लेकिन मैं इसे किसी तरह से काम करने के लिए प्राप्त करने के लिए कोशिश कर रहा हूँ) टोकन द्वारा पुनरावर्ती क्रिया कॉल ठीक कर सकते हैं।

if (opCodes[i].FlowControl == FlowControl.Call) 
{ 
    ilg.Emit(opCodes[i], mtbl); 
    i += 4; 
} 

मैं एक गतिशील विधि दृष्टिकोण का उपयोग कर निर्माण कर सकते हैं संबंधित प्रश्न के उत्तर में सुझाव दिया: Reference a collection from IL constructed method। हालांकि, यहां ऐसा करने की कोशिश करते समय, यह विफल रहता है।

+0

आप इस तरह के एक विधि को प्रतिलिपि नहीं बना सकते हैं जब इसमें कोई निर्देश किसी मेटाडेटा टोकन का उपयोग करता है। – thehennyy

+0

@thehennyy क्या आप एक फिक्स का सुझाव दे सकते हैं? –

+0

@thehennyy 'GetILAsByteArray' से ली गई आईएल की जांच करते समय, टोकन के लिए कोई संदर्भ नहीं दिखता है। – Rob

उत्तर

2

मैंने टिप्पणियों में बहुत ही उपयोगी चर्चा के आधार पर पुनर्निर्माण को लागू करने में कामयाब रहे। यह सभी संभावित परिदृश्यों को संबोधित नहीं करता है, लेकिन समाधान को बहुत अच्छी तरह से दिखाता है।

public static R CopyMethod<T, R>(Func<T, R> f, T t) 
{ 
    AppDomain currentDom = Thread.GetDomain(); 
    AssemblyName asm = new AssemblyName("DynamicAssembly"); 
    AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run); 
    ModuleBuilder mbl = abl.DefineDynamicModule("Module"); 
    TypeBuilder tbl = mbl.DefineType("Type"); 
    MethodInfo info = f.GetMethodInfo(); 
    MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray()); 
    MethodBody mb = f.Method.GetMethodBody(); 
    byte[] il = mb.GetILAsByteArray(); 
    ILGenerator ilg = mtbl.GetILGenerator(); 
    foreach (var local in mb.LocalVariables) 
     ilg.DeclareLocal(local.LocalType); 
    for (int i = 0; i < opCodes.Length; ++i) 
    { 
     if (!opCodes[i].code.HasValue) 
      continue; 
     OpCode opCode = opCodes[i].code.Value; 
     if (opCode.OperandType == OperandType.InlineBrTarget) 
     { 
      ilg.Emit(opCode, BitConverter.ToInt32(il, i + 1)); 
      i += 4; 
      continue; 
     } 
     if (opCode.OperandType == OperandType.ShortInlineBrTarget) 
     { 
      ilg.Emit(opCode, il[i + 1]); 
      ++i; 
      continue; 
     } 
     if (opCode.OperandType == OperandType.InlineType) 
     { 
      Type tp = info.Module.ResolveType(BitConverter.ToInt32(il, i + 1), info.DeclaringType.GetGenericArguments(), info.GetGenericArguments()); 
      ilg.Emit(opCode, tp); 
      i += 4; 
      continue; 
     } 
     if (opCode.FlowControl == FlowControl.Call) 
     { 
      MethodInfo mi = info.Module.ResolveMethod(BitConverter.ToInt32(il, i + 1)) as MethodInfo; 
      if (mi == info) 
       ilg.Emit(opCode, mtbl); 
      else 
       ilg.Emit(opCode, mi); 
      i += 4; 
      continue; 
     } 
     ilg.Emit(opCode); 
    } 

    Type type = tbl.CreateType(); 
    Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>; 
    return method(t); 
} 

static OpCodeContainer[] GetOpCodes(byte[] data) 
{ 
    List<OpCodeContainer> opCodes = new List<OpCodeContainer>(); 
    foreach (byte opCodeByte in data) 
     opCodes.Add(new OpCodeContainer(opCodeByte)); 
    return opCodes.ToArray(); 
} 

class OpCodeContainer 
{ 
    public OpCode? code; 
    byte data; 

    public OpCodeContainer(byte opCode) 
    { 
     data = opCode; 
     try 
     { 
      code = (OpCode)typeof(OpCodes).GetFields().First(t => ((OpCode)(t.GetValue(null))).Value == opCode).GetValue(null); 
     } 
     catch { } 
    } 
} 
संबंधित मुद्दे