我正在尝试学习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代码中的以下内容:


CLR运行时如何知道应该将get_DefaultCredentials返回的值传递给set_Credentials,因为除了注释部分“ / 01000019 /”外似乎没有任何链接?
CLR如何在System.Net.WebRequest的当前实例上调用get_Proxy,即CIL代码中是否有一个指向实例编号的指针?

最佳答案

通常,它们在堆栈上传递-但请注意,这很大程度上是实现细节; p

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


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


2:在这种情况下,实例保存在该方法的“本地”位置(即相对于堆栈帧的预留插槽);在这种情况下,位置0。最有可能使用“ dup”(根据需要复制参考),而不是使用stloc / ldloc。

10-02 01:36