Когда я вмешиваюсь в сборку, почему я не могу удалить оригинальные инструкции?

Чтобы иметь возможность тестировать унаследованный код, основанный на SharePoint, мне нужно смоделировать некоторые объекты SharePoint. Я делаю это, изменяя сборки SharePoint, заменяя их методы моими на лету.

Это работает для некоторых случаев, но не для других. Странная ситуация, с которой я столкнулся, вот эта.

Я хочу заменить получатель SPContext.Current моей собственной реализацией; для простоты моя реализация просто вызывает исключение:

.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

При подмене оригинальной сборки, если я заменю код IL, соответствующий SPContext.Current getter, свойство больше не может быть использовано. Я даже не могу визуализировать его содержимое в ILSpy, потому что вместо этого показано:

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.<get_Body>b__2(MethodDefinition method, MetadataReader reader)
   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet& 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.<>c__DisplayClass16.<DecompileAsync>b__15()

С другой стороны, когда я вставляю свои инструкции перед исходными инструкциями, я могу успешно вызвать метод получения, а также увидеть его содержимое в ILSpy:

.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
  )

... следует инструкциям, которые я вставил:

  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

... следует по оригинальной инструкции:

  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

Что мешает загрузке кода ILSpy, когда исходные инструкции удаляются перед вставкой новых?

Заметки:

  • Фальсификация выполняется с помощью Mono.Cecil с помощью MethodDefinition.Body.Instructions коллекция (и соответствующая Insert а также Remove методы.)

  • Несколько других методов и свойств Microsoft.SharePoint сборки успешно изменены: ILSpy отображает полученный IL-код.

  • я думал так .maxstack Директива может быть проблемой (1 в исходном свойстве, 8 в прокси, 1 в результате). После нескольких тестов для отдельного проекта кажется, что это не имеет никакого эффекта.

  • Я также подозревал, что причиной могут быть исключения (исходный код генерирует исключения, отличные от нового). После нескольких тестов для отдельного проекта, похоже, что он тоже не имеет никакого эффекта.

1 ответ

Решение

Когда IL отображается в текстовом виде, блоки обработки исключений (.try, catch и т. д.) отображаются как фактические блоки инструкций IL, так же, как в C#.

Но в двоичной форме блоки обработки исключений хранятся отдельно (см. §II.25.4.6 Предложения по обработке исключений в ECMA-335) и ссылаются на инструкции IL, используя смещения. В Сесиле обработчики исключений представлены с помощью MethodBody.ExceptionHandlers имущество.

Итак, если вы заменили старый MethodBody.Instructions с вашими собственными инструкциями очень вероятно, что смещения старых обработчиков исключений теперь недопустимы, что вызывает проблемы. (Тот факт, что Сесил бросает NullReferenceException звучит как ошибка для меня, рассмотрите возможность сообщения об этом.)

Другой пример, на который вы ссылаетесь, не демонстрирует эту проблему, отличается тем, что там, где оригинальный метод не содержит обработчиков исключений, он генерирует исключение. А также throw это просто нормальная инструкция IL, она не имеет специального представления, например, например .try / catch делает.

Другие вопросы по тегам