title: When tampering an assembly, why can't I remove original instructions?
body: <p>In order to be able to <a href="https://sharepoint.stackexchange.com/q/151289/40736">test legacy code which relies on SharePoint</a>, I need to mock some of the objects of SharePoint. I do this by tampering with SharePoint assemblies, replacing <em>their</em> methods by mine on the fly.</p>
  
  <p>This works for some cases, but not for others. A strange situation I encountered is this one.</p>
  
  <p>I want to replace the getter of <code>SPContext.Current</code> by my own implementation; for the sake of simplicity, my implementation just throws an exception:</p>
  
  <pre><code>.property class Microsoft.SharePoint.SPContext Current()
  {
    .get class Microsoft.SharePoint.SPContext Proxy.SPContextProxy::get_Current()
  }
  
  .method public hidebysig specialname static 
    class Microsoft.SharePoint.SPContext get_Current () cil managed 
  {
    // Method begins at RVA 0x877e68
    // Code size 12 (0xc)
    .maxstack 8
  
    IL_0000: nop
    IL_0001: ldstr "Proxy don't have an effective implementation of this property."
    IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
    IL_000b: throw
  } // end of method SPContextProxy::get_Current
  </code></pre>
  
  <p>When tampering the original assembly, if I <strong>replace</strong> the IL code corresponding to <code>SPContext.Current</code> getter, the property cannot be used any longer. I can't even visualize its contents in ILSpy, because this is what is shown instead:</p>
  
  <pre class="lang-none prettyprint-override"><code>System.NullReferenceException: Object reference not set to an instance of an object.
     at Mono.Cecil.Cil.CodeReader.ReadExceptionHandlers(Int32 count, Func`1 read_entry, Func`1 read_length)
     at Mono.Cecil.Cil.CodeReader.ReadSection()
     at Mono.Cecil.Cil.CodeReader.ReadFatMethod()
     at Mono.Cecil.Cil.CodeReader.ReadMethodBody()
     at Mono.Cecil.Cil.CodeReader.ReadMethodBody(MethodDefinition method)
     at Mono.Cecil.MethodDefinition.&lt;get_Body&gt;b__2(MethodDefinition method, MetadataReader reader)
     at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet&amp; variable, TItem item, Func`3 read)
     at Mono.Cecil.MethodDefinition.get_Body()
     at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
     at ICSharpCode.ILSpy.ILLanguage.DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
     at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
     at ICSharpCode.ILSpy.TextView.DecompilerTextView.&lt;&gt;c__DisplayClass16.&lt;DecompileAsync&gt;b__15()
  </code></pre>
  
  <p>On the other hand, when I <strong>insert</strong> my instructions before the original instructions, I can call the getter successfully, as well as see its contents in ILSpy:</p>
  
  <pre><code>.property class Microsoft.SharePoint.SPContext Current()
  {
    .custom instance void [Microsoft.SharePoint.Client.ServerRuntime]Microsoft.SharePoint.Client.ClientCallableAttribute::.ctor() = (
      01 00 00 00
    )
    .get class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::get_Current()
  }
  
  .method public hidebysig specialname static 
    class Microsoft.SharePoint.SPContext get_Current () cil managed 
  {
    // Method begins at RVA 0x33e2d8
    // Code size 61 (0x3d)
    .maxstack 1
    .locals init (
      [0] class Microsoft.SharePoint.SPContext,
      [1] class [System.Web]System.Web.HttpContext,
      [2] class Microsoft.SharePoint.SPContext
    )
  </code></pre>
  
  <p>... follows by the instructions that I inserted:</p>
  
  <pre><code>  IL_0000: nop
    IL_0001: ldstr "Proxy doesn't implement this property yet."
    IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
    IL_000b: throw
  </code></pre>
  
  <p>... follows by the original instructions:</p>
  
  <pre><code>  IL_000c: ldnull
    IL_000d: stloc.0
    IL_000e: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
    IL_0013: stloc.1
    IL_0014: ldloc.1
    IL_0015: brfalse.s IL_0039
    .try
    {
      IL_0017: ldloc.1
      IL_0018: call class Microsoft.SharePoint.SPWeb Microsoft.SharePoint.WebControls.SPControl::GetContextWeb(class [System.Web]System.Web.HttpContext)
      IL_001d: brtrue.s IL_0023
  
      IL_001f: ldnull
      IL_0020: stloc.2
      IL_0021: leave.s IL_003b
  
      IL_0023: leave.s IL_002a
    } // end .try
    catch [mscorlib]System.InvalidOperationException
    {
      IL_0025: pop
      IL_0026: ldnull
      IL_0027: stloc.2
      IL_0028: leave.s IL_003b
    } // end handler
    .try
    {
      IL_002a: ldloc.1
      IL_002b: call class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::GetContext(class [System.Web]System.Web.HttpContext)
      IL_0030: stloc.0
      IL_0031: leave.s IL_0039
    } // end .try
    catch [mscorlib]System.IO.FileNotFoundException
    {
      IL_0033: pop
      IL_0034: leave.s IL_0039
    } // end handler
    catch [mscorlib]System.InvalidOperationException
    {
      IL_0036: pop
      IL_0037: leave.s IL_0039
    } // end handler
  
    IL_0039: ldloc.0
    IL_003a: ret
  
    IL_003b: ldloc.2
    IL_003c: ret
  } // end of method SPContext::get_Current
  </code></pre>
  
  <p>What prevents the code from being loaded by ILSpy when original instructions are removed before new ones are inserted?</p>
  
  <p><strong>Notes:</strong></p>
  
  <ul>
  <li><p>Tampering is done with Mono.Cecil by using <code>MethodDefinition.Body.Instructions</code> collection (and the corresponding <code>Insert</code> and <code>Remove</code> methods.)</p></li>
  <li><p>A few other methods and properties of <code>Microsoft.SharePoint</code> assembly are tampered successfully: ILSpy displays the resulting IL code.</p></li>
  <li><p>I thought that <code>.maxstack</code> directive could be a problem (1 in the original property, 8 in the proxied one, 1 in the result). After a few tests on a separate project, it appears that it has no effect.</p></li>
  <li><p>I also suspected that exceptions could be the cause (the original code throws different exceptions than the new one). After a few tests <a href="http://source.pelicandd.com/codebase/tampering/playground/MergeWithExceptions/" rel="nofollow noreferrer">on a separate project</a>, it appears that it has no effect either.</p></li>
  </ul>