Когда я вмешиваюсь в сборку, почему я не могу удалить оригинальные инструкции?
Чтобы иметь возможность тестировать унаследованный код, основанный на 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
делает.