首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果我手头有所需的OpCodes.Constrained和实例类型,那么如何使用OpCodes.Callvirt发出MethodInfo

如果我手头有所需的OpCodes.Constrained和实例类型,那么如何使用OpCodes.Callvirt发出MethodInfo
EN

Stack Overflow用户
提问于 2011-07-30 15:30:46
回答 1查看 2.9K关注 0票数 5

我有一个递归函数emit : Map<string,LocalBuilder> -> exp -> unit,其中il : ILGenerator是该函数的全局函数,exp是一个判别联合,代表一种类型检查的解析语言,case InstanceCall of exp * MethodInfo * exp list * TypeTypeexp上的一个属性,表示表达式的类型。

在下面的片段中,我试图为一个实例调用发出IL操作码,其中instance.Type可能是ValueType,也可能不是ValueType。因此,我知道我可以使用OpCodes.Constrained灵活有效地对引用、值和枚举类型进行虚拟调用。一般来说,我对Reflection.Emit和机器语言都很陌生,所以我对OpCodes.Constrained的链接文档的理解并不强。

这是我的尝试,但它的结果是一个VerificationException,“操作可能破坏运行时的稳定”:

代码语言:javascript
复制
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)
    ...

查看文档,我认为键可能是“将托管指针ptr推到堆栈上。ptr的类型必须是指向thisType的托管指针(&)。请注意,这与没有前缀的callvirt指令的情况不同,后者需要thisType的引用。”

更新

谢谢@Tomas和@desco,我现在明白何时使用OpCodes.Constrained (instance.Type是ValueType,但methodInfo.DeclaringType是引用类型)。

但事实证明,我还不需要考虑这种情况,我真正的问题是堆栈上的实例参数:我只花了6个小时就知道它需要一个地址,而不是值(查看DLR源代码给了我线索,然后在一个简单的C#程序上使用C#说明了这一点)。

这是我的最后工作版本:

代码语言:javascript
复制
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)
        ...
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-07-30 16:04:53

我认为你在问题末尾引用的一点文件是问题的根源。我不太清楚OpCodes.Constrained前缀是用来做什么的(我并不比您更了解文档),但我试着查看了微软如何使用它:-)。

下面是来自动态语言运行时源代码的一个片段,它发出一个方法调用:

代码语言:javascript
复制
// 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前缀只用于对值类型的虚拟调用。我的解释是,对于值类型,您知道什么是实际类型,因此不需要实际(无约束)虚拟调用。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6884149

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档