2011-07-30 18 views
5

ihtiyaç MethodInfo ve örnek Tipi sahip verilmiş OpCodes.Callvirt ile OpCodes.Constrained dışarı verir Nasıl il : ILGenerator işlevine geneldir ve exp tip-kontrol çözümlü dili temsil eden bir ayırt edici birliktir emit : Map<string,LocalBuilder> -> exp -> unit dava ile InstanceCall of exp * MethodInfo * exp list * Type ve Type ifade Tipi temsil exp bir özelliktir.Ben özyinelemeli fonksiyonu var yandan

Aşağıdaki parçada, instance.Type'un bir ValueType olabilir veya olmayabilir bir örnek çağrı için IL opcodes yayımlamaya çalışıyorum. Bu yüzden, referans, değer ve enum türlerinde sanal aramaları esnek ve verimli bir şekilde yapmak için OpCodes.Constrained'u kullanabileceğimi anlıyorum. Reflection.Emit ve makine dilleri için yeni biriyim, bu yüzden OpCodes.Constrained için bağlantılı belgeleri anlamak benim için güçlü değil.

İşte benim girişimi, ama bir VerificationException sonuçlanır "Operasyon Çalışma zamanını bozabilir.":

let rec emit lenv ast = 
    match ast with 
    ... 
    | InstanceCall(instance,methodInfo,args,_) -> 
     instance::args |> List.iter (emit lenv) 
     il.Emit(OpCodes.Constrained, instance.Type) 
     il.Emit(OpCodes.Callvirt, methodInfo) 
    ... 

docs bakınca anahtar olabilir düşünüyorum "A, işaretçi, ptr yönetilen yığını üzerine itilir. ptr türü thisType bir yönetilen işaretçi (&) olması gerekir. Bu thisType bir referans beklediği bir öneksiz callvirt talimat durumunda, farklı olduğuna dikkat edin."

Güncelleme size @Tomas ederiz ve OpCodes.Constrained ne zaman kullanılacağına @desco, şimdi (instance.Type bir ValueType olmakla methodInfo.DeclaringType bir referans türüdür) anlıyoruz.

Ama şu anda bu davayı ele almam gerekmiyor ve benim asıl problemim yığındaki örnek argümanıydı: sadece değer yerine bir adrese ihtiyacı olduğunu öğrenmek için 6 saatimi aldı DLR kaynak kodunda bana ipuçları verdi ve sonra basit bir C# programında ilasm.exe kullanarak temizleyin). Sana soru sonunda aktardığı belgelerin bit sorunun kaynağı olduğunu düşünüyorum

let rec emit lenv ast = 
    match ast with 
    | Int32(x,_) -> 
     il.Emit(OpCodes.Ldc_I4, x) 
    ... 
    | InstanceCall(instance,methodInfo,args,_) -> 
     emit lenv instance 
     //if value type, pop, put in field, then load the field address 
     if instance.Type.IsValueType then 
      let loc = il.DeclareLocal(instance.Type) 
      il.Emit(OpCodes.Stloc, loc) 
      il.Emit(OpCodes.Ldloca, loc) 

     for arg in args do emit lenv arg 

     if instance.Type.IsValueType then 
      il.Emit(OpCodes.Call, methodInfo) 
     else 
      il.Emit(OpCodes.Callvirt, methodInfo) 
     ... 

cevap

1

:

İşte benim nihai çalışan sürümüdür. Ben (Senden daha belgelerine iyi anlamıyorum) OpCodes.Constrained önek için ne tam emin değilim, ama Microsoft :-) tarafından nasıl kullanıldığını aradık. İşte

bir yöntem çağrısı yayar source code of Dynamic Language Runtime bir pasajı: Ben muhtemelen davranışlarını takip etmek isteyeceksiniz düşünüyorum

// Emit arguments 
List<WriteBack> wb = EmitArguments(mi, args); 

// Emit the actual call 
OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call; 
if (callOp == OpCodes.Callvirt && objectType.IsValueType) { 
    // This automatically boxes value types if necessary. 
    _ilg.Emit(OpCodes.Constrained, objectType); 
} 
// The method call can be a tail call if [...] 
if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && 
    !MethodHasByRefParameter(mi)) { 
    _ilg.Emit(OpCodes.Tailcall); 
} 
if (mi.CallingConvention == CallingConventions.VarArgs) { 
    _ilg.EmitCall(callOp, mi, args.Map(a => a.Type)); 
} else { 
    _ilg.Emit(callOp, mi); 
} 

// Emit writebacks for properties passed as "ref" arguments 
EmitWriteBack(wb); 

- constrained önek yalnızca değer türleri üzerinde sanal aramalar için kullanıldığı gözükmektedir . Benim yorumum, değer türleri için gerçek türün ne olduğunu biliyorsunuz, bu yüzden gerçek (kısıtlanmamış) sanal aramaya ihtiyacınız yok.

2

Temelde ben Tomas katılıyorum: derleme zamanında tam tipi biliyorsanız, o zaman doğru çağrı talimatı kendiniz yayabilir. Kısıtlı önek genellikle jenerik kod

için kullanılan Fakat dokümantasyon da diyor edilmektedir: Bir değer türü olan

kısıtlı işlemkodu IL derleyiciler olsun ptr bağımsız tek tip bir şekilde sanal bir işleve çağrı yapmak için izin verir veya bir referans tipi.Bu türün genel bir tür değişkeni olduğu durum için tasarlanmasına rağmen, kısıtlı önek nongenerik türler için de çalışır ve değer türleri ile referans türleri arasındaki farkı gizleyen dillerdeki sanal çağrıları oluşturmanın karmaşıklığını azaltabilir. ...

Kısıtlı öneki kullanmak, değer türleriyle olası sürüm oluşturma sorunlarını da önler. Kısıtlanmış önek kullanılmazsa, bir değer türünün System.Object yöntemini geçersiz kılıp kılmadığına bağlı olarak farklı IL yayımlanmalıdır. Örneğin, V bir değer Object.ToString() yöntemini geçersiz kılarsa, bir V.ToString() komutu yayımlanır; yoksa, bir kutu talimatı ve bir callvirt Object.ToString() komutu yayınlanır. Önceki durumda, geçersiz kılma daha sonra kaldırılırsa ve sonraki durumda bir geçersiz kılma daha sonra eklenirse, bir sürüm oluşturma sorunu ortaya çıkabilir.

Küçük gösteri (bana utanç, benim netbook F # yok):

using System; 
using System.Reflection; 
using System.Reflection.Emit; 

public struct EvilMutableStruct 
{ 
    int i; 
    public override string ToString() 
    { 
      i++; 
      return i.ToString(); 
    } 
} 

class Program 
{ 
    public static void Main() 
    { 
      var intToString = Make<int>(); 
      var stringToString = Make<string>(); 
      var structToString = Make<EvilMutableStruct>(); 
      Console.WriteLine(intToString(5)); 
      Console.WriteLine(stringToString("!!!")); 
      Console.WriteLine(structToString (new EvilMutableStruct())); 
    } 

    static MethodInfo ToStringMethod = new Func<string>(new object().ToString).Method; 
    static MethodInfo ConcatMethod = new Func<string, string, string>(String.Concat).Method; 

    // x => x.ToString() + x.ToString() 
    private static Func<T, string> Make<T>() 
    { 
      var dynamicMethod = new DynamicMethod("ToString", typeof(string), new[] {typeof(T)}); 
      var il = dynamicMethod.GetILGenerator(); 

      il.Emit(OpCodes.Ldarga_S, 0); 
      il.Emit(OpCodes.Constrained, typeof(T)); 
      il.Emit(OpCodes.Callvirt, ToStringMethod); 

      il.Emit(OpCodes.Ldarga_S, 0); 
      il.Emit(OpCodes.Constrained, typeof(T)); 
      il.Emit(OpCodes.Callvirt, ToStringMethod); 

      il.Emit(OpCodes.Call, ConcatMethod); 

      il.Emit(OpCodes.Ret); 
      return (Func<T, string>)dynamicMethod.CreateDelegate(typeof(Func<T, string>)); 
    } 
} 

Çıktı:

55 
!!!!!! 
12 
İlgili konular