为什么调用指令会导致运行时不稳定?

发布于 2024-10-03 08:46:09 字数 1910 浏览 0 评论 0原文

以下代码产生一个异常,表明它可能会破坏运行时的稳定。

var method = new DynamicMethod("CallObjectToString", typeof(string),new[]{ typeof(object)});
var ilgen =method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString"));
ilgen.Emit(OpCodes.Ret);
var @delegate = method.CreateDelegate(typeof(Func<object, string>)) as Func<object,string>;
@delegate(new object());

Opcodes.Call 更改为 Opcodes.CallVirt 可以解决该问题。 这一切都很好,但我可以使用 typebuilder 创建一个动态类型,该类型具有使用 (MethodBuilder) 构建的静态方法,该方法具有完全相同相同的 IL,然后使用 CreateDelegate,它不会抛出此错误例外。事实上,使用 methodbuilder,您可以调用完全不同的对象的虚拟方法,甚至不是该类型继承的虚拟方法。

更令人费解的是,如果我执行 new object().ToString() 之类的操作,它还会向 IL 发出一条 call 指令,而不是 callvirt< /代码>。

如果使用 call 调用虚拟方法是非法的,除非在实例重写中调用基方法的上下文中,那么为什么 CLR 允许在不同类型上静态创建这样的方法?或者允许针对已知的非空实体发出 call 指令?

或者这只是通过 DynamicMethod 生成的代码的问题?

编辑: 我不是从性能的角度来问,因为我真的不关心它。下面的代码虽然稍微复杂一些,但创建了一个委托,该委托执行不稳定操作而不破坏运行时的稳定。这就是问题的全部内容。为什么一个是合法的,另一个是非法的?

var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("SomeAssembly"), AssemblyBuilderAccess.Run) ;
var moduleBuilder = assemblyBuilder.DefineDynamicModule("SomeModule");
var typeBuilder = moduleBuilder.DefineType("SomeType");
var methodBuilder = typeBuilder.DefineMethod("SomeStaticMethod",MethodAttributes.Public | MethodAttributes.Static,typeof(string),new[]{typeof(object)});
var ilgen = methodBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString"));
ilgen.Emit(OpCodes.Ret);
var newType = typeBuilder.CreateType();
var @delegate = Delegate.CreateDelegate(typeof(Func<object, string>),newType.GetMethod("SomeStaticMethod")) as Func<object,string>;
@delegate(new object());

The following code yields an exception that says that it may destabalize the runtime.

var method = new DynamicMethod("CallObjectToString", typeof(string),new[]{ typeof(object)});
var ilgen =method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString"));
ilgen.Emit(OpCodes.Ret);
var @delegate = method.CreateDelegate(typeof(Func<object, string>)) as Func<object,string>;
@delegate(new object());

Changing the Opcodes.Call to Opcodes.CallVirt fixes the problem.
This is all well and good, but I can use typebuilder create a dynamic type that has a static method built using (MethodBuilder) that has exactly the same IL, then use CreateDelegate, and it will not throw this exception. In fact using methodbuilder you can make something does a call on a completely different object's virtual methods, not even one that that the type inherits from.

What's even more puzzling that if I do something like new object().ToString() it also emits a call instruction to the IL, rather than a callvirt.

If it's illegal to call a virtual method using a call except in the context of calling the base method in an instance override, then why does the CLR allow for such a method to be made staticly on a different type? Or Allow call instructions to be emitted against known not null entities?

Or is this only an issue for code generated through DynamicMethod?

Edit:
I'm not asking from a performance standpoint as I really don't care about it. The below code while slightly more involved create a delegate that performs the destabilizing action without destabilizing the runtime. This is the entirety of the question. Why is one legal, and the other illegal?

var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("SomeAssembly"), AssemblyBuilderAccess.Run) ;
var moduleBuilder = assemblyBuilder.DefineDynamicModule("SomeModule");
var typeBuilder = moduleBuilder.DefineType("SomeType");
var methodBuilder = typeBuilder.DefineMethod("SomeStaticMethod",MethodAttributes.Public | MethodAttributes.Static,typeof(string),new[]{typeof(object)});
var ilgen = methodBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString"));
ilgen.Emit(OpCodes.Ret);
var newType = typeBuilder.CreateType();
var @delegate = Delegate.CreateDelegate(typeof(Func<object, string>),newType.GetMethod("SomeStaticMethod")) as Func<object,string>;
@delegate(new object());

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

盛装女皇 2024-10-10 08:46:09

它试图保护您免于做错事。这里的调用是错误的,因为无法知道对象的具体类型。合法地知道该对象不能是它所看到的以外的任何东西(例如,它看到一个密封类,或者它只是实例化该对象)的编译器可以逃脱,因为它知道它是安全的,但在你的情况下,你看到的都是是“对象”作为参数传递,所以这种转换不安全。在“new object().ToString()”的情况下,对象的类型绝对具体地知道是对象,因此不需要虚拟调用,但是在您的情况下,对象作为参数出现,因此你完全无法知道它的具体类型是什么。不过你不应该太担心。当涉及到 JIT 这段代码时,JITer 可能比你更了解,并且无论如何都可能使 callvirt 变成直接调用,但它又可以做相反的事情并强制调用变成间接调用,所以确实有非常多的问题。没什么可担心的。

It is trying to protect you from doing something wrong. A call here is wrong because the concrete type of the object cannot be known. A compiler that legally knows that the object cannot be anything other than what it sees (for example it sees a sealed class, or it just instantiated the object) can get away with because it knows it is safe, but in your case all you see is "object" passed as an argument so this transformation is not safe. In the case of "new object().ToString()" then the type of the object is absolutely concretely known to be object, so there is no need for a virtual call, however in your case the object comes in as an argument so you have absolutely no way of knowing what its concrete type is. You should not fret too much though. The JITer might know even better than you do when it comes to JIT this code, and might make the callvirt into a direct call anyway, but then again it could do the reverse and force the call into an indirect call, so there is really very little to worry about.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文