如何在CIL函数调用中传递参数?

时间:2011-03-21 22:24:22

标签: c# .net clr cil

我正在尝试学习CIL代码,但不能将一个函数的返回值作为参数传递给另一个函数。

我为以下函数生成了CIL代码:

  public bool TestWebPage()
  {
    WebRequest request = WebRequest.Create("http://www.costco.com");
    request.Proxy.Credentials = CredentialCache.DefaultCredentials;
  }

CIL代码:

//000021:     public void TestWebPage()
//000022:     {
    IL_0000:  /* 00   |                  */ nop
   .line 23,23 : 7,71 ''
//000023:       WebRequest request = WebRequest.Create("http://www.costco.com");
IL_0001:  /* 72   | (70)000001       */ ldstr      "http://www.costco.com" /* 70000001  */
  IL_0006:  /* 28   | (0A)000012       */ call       class [System/*23000003*/]System.Net.WebRequest/*01000016*/ [System/*23000003*/]System.Net.WebRequest/*01000016*/::Create(string) /* 0A000012 */
  IL_000b:  /* 0A   |                  */ stloc.0
.line 24,24 : 7,70 ''
//000024:       request.Proxy.Credentials = CredentialCache.DefaultCredentials;
IL_000c:  /* 06   |                  */ ldloc.0
IL_000d:  /* 6F   | (0A)000013       */ callvirt   instance class [System/*23000003*/]System.Net.IWebProxy/*01000017*/ [System/*23000003*/]System.Net.WebRequest/*01000016*/::get_Proxy() /* 0A000013 */
IL_0012:  /* 28   | (0A)000014       */ call       class [System/*23000003*/]System.Net.ICredentials/*01000019*/ [System/*23000003*/]System.Net.CredentialCache/*01000018*/::get_DefaultCredentials() /* 0A000014 */
IL_0017:  /* 6F   | (0A)000015       */ callvirt   instance void [System/*23000003*/]System.Net.IWebProxy/*01000017*/::set_Credentials(class [System/*23000003*/]System.Net.ICredentials/*01000019*/) /* 0A000015 */
IL_001c:  /* 00   |                  */ nop
.line 25,25 : 7,73 ''

具体来说,我无法理解CIL代码中的以下内容:

  1. CLR运行时如何知道set_Credentials要传递get_DefaultCredentials返回的值,因为除了注释部分“/ 01000019 /”之外似乎没有任何链接

  2. CLR如何在System.Net.WebRequest的当前实例上调用get_Proxy,即CIL代码中是否有指向实例编号的指针?

3 个答案:

答案 0 :(得分:2)

通常,它们会在堆栈上传递 - 但请注意,这主要是一个实现细节; p

1:构建堆栈,使目标首先在堆栈上=具体来说:

  • ldloc.0将请求加载到堆栈
  • callvirt get_Proxy()使用请求(自虚拟)并将代理保留在堆栈上(从返回值开始)
  • 调用get_DefaultCredentials()不消耗任何内容,并附加默认凭据
  • callvirt set_Credentials消耗2个值;第一个(代理)用作实例(自虚拟);第二个(凭证)用作第一个参数值

2:在这种情况下,实例保存在方法的“本地”中(即相对于堆栈帧的保留时隙);在这种情况下,loc 0.在发布版本中,我实际上希望删除loc 0,这一切都只是没有保留的插槽处理;最有可能使用“dup”(根据需要复制引用)而不是stloc / ldloc。

答案 1 :(得分:1)

  1. IL_0012上的call指令将get_DefaultCredentials的返回值推送到堆栈上,下一行的set_Credentials方法作为参数传递给评估栈。
  2. request方法返回的WebRequest.Create变量存储为索引0(stloc.0)的局部变量,然后由ldloc.0加载,因此{{1在此局部变量变量上调用行get_Proxy上的方法,该变量已加载到评估堆栈中。

答案 2 :(得分:0)

这是基于堆栈的。发生的是调用返回值get_DefaultCredentials的方法的结果被推送到评估堆栈。

没有神奇的链接,这个值恰好位于堆栈顶部。

只要看一下构造函数调用,首先发生的是字符串"http://www.costco.com"被推到评估堆栈的顶部。然后调用构造函数。然后构造函数弹出评估堆栈的值。推到堆栈顶部的最后一个值是最后一个参数。 ldstr只在堆栈顶部推送一个值。然后弹出构造函数调用的结果,现在是 top ,并且该引用将被弹出并存储在位置0 stloc.0(局部变量)中。

调用实例方法略有不同,在这种情况下,第一个参数始终是对象,因此您在此处看到的第一个调用是ldloc.0。这会将WebRequest对象实例推送到评估堆栈的顶部。从这里我们调用get_Proxy方法,它使用ldloc.0放置的评估堆栈顶部的值,并返回返回的对象实例。这实际上取代了评估堆栈顶部的值。但是现在,堆栈的顶部不再是ldloc.0放在那里的值。

没有任何东西真正被传递,它只是被推,弹出,关闭和堆叠。