Ste*_*sen 5 .net f# il reflection.emit
我有一个递归函数emit : Map<string,LocalBuilder> -> exp -> unit,其中il : ILGenerator是全局的功能,exp是代表与情况的类型检查解析的语言判别工会InstanceCall of exp * MethodInfo * exp list * Type和Type是一个属性exp代表表达的类型.
在下面的片段中,我试图为实例调用发出IL操作码,其中instance.Type可能是也可能不是ValueType.所以我理解我可以OpCodes.Constrained灵活高效地对引用,值和枚举类型进行虚拟调用.我是Reflection.Emit和机器语言的新手,因此理解链接文档OpCodes.Constrained并不适合我.
这是我的尝试,但结果是VerificationException"操作可能会破坏运行时的稳定性.":
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)
...
Run Code Online (Sandbox Code Playgroud)
查看文档,我认为关键可能是"托管指针,ptr,被推入堆栈.ptr的类型必须是thisType的托管指针(&).请注意,这与未加前缀的情况不同callvirt指令,它需要thisType的引用."
更新
谢谢@Tomas和@desco,我现在知道何时使用OpCodes.Constrained(instance.Type是一个ValueType,但是methodInfo.DeclaringType是一个引用类型).
但事实证明我还不需要考虑那个案例,而我真正的问题是堆栈上的实例参数:我花了6个小时才知道它需要一个地址而不是值(看DLR源码)代码给了我线索,然后在一个简单的C#程序上使用ilasm.exe说清楚了).
这是我的最终工作版本:
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)
...
Run Code Online (Sandbox Code Playgroud)
我认为您在问题末尾引用的一些文档是问题的根源。我不太确定OpCodes.Constrained前缀的用途(我对文档的理解不比你好),但我尝试查看 Microsoft 如何使用它:-)。
下面是动态语言运行时源代码的一个片段,它发出一个方法调用:
// 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);
Run Code Online (Sandbox Code Playgroud)
我认为您可能想要遵循他们的行为 - 似乎前缀constrained仅用于值类型的虚拟调用。我的解释是,对于值类型,您知道实际类型是什么,因此不需要实际的(不受约束的)虚拟调用。
| 归档时间: |
|
| 查看次数: |
2473 次 |
| 最近记录: |