Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitek Karas <vitek.karas@microsoft.com>2021-08-18 17:20:54 +0300
committerGitHub <noreply@github.com>2021-08-18 17:20:54 +0300
commite9403e206860e9569ea59af8f3241922564c8c2b (patch)
tree8327f75b7e10e4751de1a191c7b6852c3ac5cf56
parent5b2391c2c56af47350a5789375e8dbddc692e67f (diff)
Implement a custom ILProcessor and make it auto-fix IL corruptions (#2213)
Cecil doesn't handle IL editing well. It doesn't patch scopes and branches. Added a linker-private IL processor which does this. Added tests for the IL corruption when removing branches around code which has exception handlers. I also removed now unnecessary test attribute, since in my previous change the exception handlers are now validated within the instruction stream, so no need for a second attribute to validate these. Co-authored-by: Sven Boemer <sbomer@gmail.com>
-rw-r--r--src/linker/Linker.Steps/CodeRewriterStep.cs12
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs29
-rw-r--r--src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs8
-rw-r--r--src/linker/Linker/LinkerILProcessor.cs127
-rw-r--r--test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs14
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs1
-rw-r--r--test/Mono.Linker.Tests.Cases/UnreachableBlock/Dependencies/EndScopeOnMethod.il40
-rw-r--r--test/Mono.Linker.Tests.Cases/UnreachableBlock/EndScopeOnMethoEnd.cs28
-rw-r--r--test/Mono.Linker.Tests.Cases/UnreachableBlock/ReplacedReturns.cs1
-rw-r--r--test/Mono.Linker.Tests.Cases/UnreachableBlock/TryCatchBlocks.cs93
-rw-r--r--test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFilterBlocks.cs2
-rw-r--r--test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFinallyBlocks.cs109
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs23
13 files changed, 372 insertions, 115 deletions
diff --git a/src/linker/Linker.Steps/CodeRewriterStep.cs b/src/linker/Linker.Steps/CodeRewriterStep.cs
index aa2279291..bd730eb64 100644
--- a/src/linker/Linker.Steps/CodeRewriterStep.cs
+++ b/src/linker/Linker.Steps/CodeRewriterStep.cs
@@ -39,7 +39,7 @@ namespace Mono.Linker.Steps
void AddFieldsInitializations (TypeDefinition type)
{
Instruction ret;
- ILProcessor processor;
+ LinkerILProcessor processor;
var cctor = type.Methods.FirstOrDefault (MethodDefinitionExtensions.IsStaticConstructor);
if (cctor == null) {
@@ -51,13 +51,13 @@ namespace Mono.Linker.Steps
type.Methods.Add (method);
- processor = method.Body.GetILProcessor ();
+ processor = method.Body.GetLinkerILProcessor ();
ret = Instruction.Create (OpCodes.Ret);
processor.Append (ret);
} else {
ret = cctor.Body.Instructions.Last (l => l.OpCode.Code == Code.Ret);
var body = cctor.Body;
- processor = cctor.Body.GetILProcessor ();
+ processor = cctor.Body.GetLinkerILProcessor ();
for (int i = 0; i < body.Instructions.Count; ++i) {
var instr = body.Instructions[i];
@@ -122,7 +122,7 @@ namespace Mono.Linker.Steps
MethodBody CreateThrowLinkedAwayBody (MethodDefinition method)
{
var body = new MethodBody (method);
- var il = body.GetILProcessor ();
+ var il = body.GetLinkerILProcessor ();
MethodReference ctor;
// Makes the body verifiable
@@ -151,7 +151,7 @@ namespace Mono.Linker.Steps
if (method.HasParameters && method.Parameters.Any (l => l.IsOut))
throw new NotSupportedException ($"Cannot replace body of method '{method.GetDisplayName ()}' because it has an out parameter.");
- var il = body.GetILProcessor ();
+ var il = body.GetLinkerILProcessor ();
if (method.IsInstanceConstructor () && !method.DeclaringType.IsValueType) {
var baseType = Context.Resolve (method.DeclaringType.BaseType);
if (baseType is null)
@@ -184,7 +184,7 @@ namespace Mono.Linker.Steps
return body;
}
- static void StubComplexBody (MethodDefinition method, MethodBody body, ILProcessor il)
+ static void StubComplexBody (MethodDefinition method, MethodBody body, LinkerILProcessor il)
{
switch (method.ReturnType.MetadataType) {
case MetadataType.MVar:
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index ad2de5f04..ec69753c1 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -490,41 +490,14 @@ namespace Mono.Linker.Steps
continue;
Instruction instr = item.Instr;
- ILProcessor ilProcessor = item.Body.GetILProcessor ();
+ LinkerILProcessor ilProcessor = item.Body.GetLinkerILProcessor ();
ilProcessor.InsertAfter (instr, Instruction.Create (OpCodes.Ldnull));
Instruction new_instr = Instruction.Create (OpCodes.Pop);
ilProcessor.Replace (instr, new_instr);
- UpdateBranchTarget (item.Body, instr, new_instr);
_context.LogMessage ($"Removing typecheck of '{type.FullName}' inside {item.Body.Method.GetDisplayName ()} method");
}
-
- static void UpdateBranchTarget (MethodBody body, Instruction oldTarget, Instruction newTarget)
- {
- foreach (var instr in body.Instructions) {
- switch (instr.OpCode.FlowControl) {
- case FlowControl.Branch:
- case FlowControl.Cond_Branch:
- if (instr.Operand == oldTarget)
- instr.Operand = newTarget;
- break;
- }
- }
-
- foreach (var handler in body.ExceptionHandlers) {
- if (handler.TryStart == oldTarget)
- handler.TryStart = newTarget;
- if (handler.TryEnd == oldTarget)
- handler.TryEnd = newTarget;
- if (handler.HandlerStart == oldTarget)
- handler.HandlerStart = newTarget;
- if (handler.HandlerEnd == oldTarget)
- handler.HandlerEnd = newTarget;
- if (handler.FilterStart == oldTarget)
- handler.FilterStart = newTarget;
- }
- }
}
void ProcessQueue ()
diff --git a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs
index 17be828b5..cd9713f98 100644
--- a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs
+++ b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs
@@ -662,7 +662,7 @@ namespace Mono.Linker.Steps
RemoveUnreachableInstructions (reachableInstrs);
if (nopInstructions != null) {
- ILProcessor processor = Body.GetILProcessor ();
+ LinkerILProcessor processor = Body.GetLinkerILProcessor ();
foreach (var instr in nopInstructions)
processor.Remove (instr);
@@ -673,7 +673,7 @@ namespace Mono.Linker.Steps
void RemoveUnreachableInstructions (BitArray reachable)
{
- ILProcessor processor = Body.GetILProcessor ();
+ LinkerILProcessor processor = Body.GetLinkerILProcessor ();
int removed = 0;
for (int i = 0; i < reachable.Count; ++i) {
@@ -1114,7 +1114,7 @@ namespace Mono.Linker.Steps
readonly BitArray reachable;
readonly List<ExceptionHandler> unreachableExceptionHandlers;
readonly LinkContext context;
- ILProcessor ilprocessor;
+ LinkerILProcessor ilprocessor;
public BodySweeper (MethodBody body, BitArray reachable, List<ExceptionHandler> unreachableEH, LinkContext context)
{
@@ -1155,7 +1155,7 @@ namespace Mono.Linker.Steps
}
}
- ilprocessor = body.GetILProcessor ();
+ ilprocessor = body.GetLinkerILProcessor ();
return true;
}
diff --git a/src/linker/Linker/LinkerILProcessor.cs b/src/linker/Linker/LinkerILProcessor.cs
new file mode 100644
index 000000000..a2ee2c049
--- /dev/null
+++ b/src/linker/Linker/LinkerILProcessor.cs
@@ -0,0 +1,127 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace Mono.Linker
+{
+ class LinkerILProcessor
+ {
+ readonly ILProcessor _ilProcessor;
+
+ Collections.Generic.Collection<Instruction> Instructions => _ilProcessor.Body.Instructions;
+
+ internal LinkerILProcessor (MethodBody body)
+ {
+ _ilProcessor = body.GetILProcessor ();
+ }
+
+ public void Emit (OpCode opcode) => Append (_ilProcessor.Create (opcode));
+ public void Emit (OpCode opcode, TypeReference type) => Append (_ilProcessor.Create (opcode, type));
+ public void Emit (OpCode opcode, MethodReference method) => Append (_ilProcessor.Create (opcode, method));
+ public void Emit (OpCode opcode, VariableDefinition variable) => Append (_ilProcessor.Create (opcode, variable));
+
+ public void Emit (OpCode opcode, string value) => Append (_ilProcessor.Create (opcode, value));
+ public void InsertBefore (Instruction target, Instruction instruction)
+ {
+ // When inserting before, all pointers have to be updated to the new "before" instruction.
+ RedirectScopeStart (target, instruction);
+ ReplaceInstructionReference (target, instruction);
+ _ilProcessor.InsertBefore (target, instruction);
+ }
+
+ // When inserting after, no redirection is necessary since it will naturally be "appended" to the same blocks
+ // as the target instruction.
+ public void InsertAfter (Instruction target, Instruction instruction)
+ {
+ RedirectScopeEnd (target, instruction);
+ _ilProcessor.InsertAfter (target, instruction);
+ }
+
+ public void Append (Instruction instruction)
+ {
+ Instruction lastInstruction = Instructions.Count == 0 ? null : Instructions[Instructions.Count - 1];
+ RedirectScopeEnd (lastInstruction, instruction);
+ _ilProcessor.Append (instruction);
+ }
+
+ public void Replace (Instruction target, Instruction instruction)
+ {
+ RedirectScopeStart (target, instruction);
+ RedirectScopeEnd (target, instruction);
+ ReplaceInstructionReference (target, instruction);
+ _ilProcessor.Replace (target, instruction);
+ }
+
+ public void Replace (int index, Instruction instruction) => Replace (_ilProcessor.Body.Instructions[index], instruction);
+
+ public void Remove (Instruction instruction)
+ {
+ int index = _ilProcessor.Body.Instructions.IndexOf (instruction);
+ if (index == -1)
+ throw new ArgumentOutOfRangeException (nameof (instruction));
+
+ Instruction nextInstruction = Instructions.Count == index + 1 ? null : Instructions[index + 1];
+ Instruction previousInstruction = index == 0 ? null : Instructions[index - 1];
+
+ RedirectScopeStart (instruction, nextInstruction);
+ RedirectScopeEnd (instruction, previousInstruction);
+ ReplaceInstructionReference (instruction, nextInstruction);
+ _ilProcessor.Remove (instruction);
+ }
+
+ public void RemoveAt (int index) => Remove (Instructions[index]);
+
+ void RedirectScopeStart (Instruction oldTarget, Instruction newTarget)
+ {
+ // In Cecil "start" pointers point to the first instruction in a given scope
+ // and the "end" pointers point to the first instruction after the given block
+ // so they need to effectively point to the start of the next scope.
+ // That's why both start and end are handled in the RedirectScopeStart.
+ foreach (var handler in _ilProcessor.Body.ExceptionHandlers) {
+ if (handler.TryStart == oldTarget)
+ handler.TryStart = newTarget;
+ if (handler.TryEnd == oldTarget)
+ handler.TryEnd = newTarget;
+ if (handler.HandlerStart == oldTarget)
+ handler.HandlerStart = newTarget;
+ if (handler.HandlerEnd == oldTarget)
+ handler.HandlerEnd = newTarget;
+ if (handler.FilterStart == oldTarget)
+ handler.FilterStart = newTarget;
+ }
+ }
+
+#pragma warning disable IDE0060 // Remove unused parameter
+ static void RedirectScopeEnd (Instruction oldTarget, Instruction newTarget)
+#pragma warning restore IDE0060 // Remove unused parameter
+ {
+ // Currently Cecil treats all block boundaries as "starts"
+ // so nothing to do here.
+ }
+
+ void ReplaceInstructionReference (Instruction oldTarget, Instruction newTarget)
+ {
+ foreach (var instr in Instructions) {
+ switch (instr.OpCode.FlowControl) {
+ case FlowControl.Branch:
+ case FlowControl.Cond_Branch:
+ if (instr.Operand == oldTarget)
+ instr.Operand = newTarget;
+ break;
+ }
+ }
+ }
+ }
+
+ static class ILProcessorExtensions
+ {
+ public static LinkerILProcessor GetLinkerILProcessor (this MethodBody body)
+ {
+ return new LinkerILProcessor (body);
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs
deleted file mode 100644
index 0c9f10e4a..000000000
--- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace Mono.Linker.Tests.Cases.Expectations.Assertions
-{
- [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)]
- public class ExpectedExceptionHandlerSequenceAttribute : BaseInAssemblyAttribute
- {
- public ExpectedExceptionHandlerSequenceAttribute (string[] types)
- {
- if (types == null)
- throw new ArgumentNullException (nameof (types));
- }
- }
-} \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs
index 0a988d986..73c6820a9 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs
@@ -45,7 +45,6 @@ namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW
{
"ret"
})]
- [ExpectedExceptionHandlerSequence (new string[0])]
protected override void OnEventCommand (EventCommandEventArgs command)
{
try {
diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/Dependencies/EndScopeOnMethod.il b/test/Mono.Linker.Tests.Cases/UnreachableBlock/Dependencies/EndScopeOnMethod.il
new file mode 100644
index 000000000..fe2b7dde5
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/Dependencies/EndScopeOnMethod.il
@@ -0,0 +1,40 @@
+.assembly extern mscorlib
+{
+}
+.assembly 'library'
+{
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+.module library.dll
+
+.namespace Mono.Linker.Tests.Cases.UnreachableBlock.Dependencies
+{
+
+ .class public auto ansi beforefieldinit EndScopeOnMethod
+ extends [mscorlib]System.Object
+ {
+
+ .method public hidebysig specialname rtspecialname
+ instance default void '.ctor' () cil managed
+ {
+ IL_0000: ldarg.0
+ IL_0001: call instance void class [mscorlib]System.Object::'.ctor'()
+ IL_0006: ret
+ }
+
+ .method public static hidebysig object TryFinally() cil managed
+ {
+ .try
+ {
+ ldnull
+ ret
+ }
+ finally
+ {
+ ldnull
+ ret
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/EndScopeOnMethoEnd.cs b/test/Mono.Linker.Tests.Cases/UnreachableBlock/EndScopeOnMethoEnd.cs
new file mode 100644
index 000000000..be542f9b1
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/EndScopeOnMethoEnd.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.UnreachableBlock
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/EndScopeOnMethod.il" })]
+ public class EndScopeOnMethoEnd
+ {
+ public static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ // For now just have a method where the try/finally is the last thing in the method (no instruction after the
+ // end of finally - Roslyn doesn't seem to produce such method body.
+ Mono.Linker.Tests.Cases.UnreachableBlock.Dependencies.EndScopeOnMethod.TryFinally ();
+#endif
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/ReplacedReturns.cs b/test/Mono.Linker.Tests.Cases/UnreachableBlock/ReplacedReturns.cs
index 1ac92c851..a4728c606 100644
--- a/test/Mono.Linker.Tests.Cases/UnreachableBlock/ReplacedReturns.cs
+++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/ReplacedReturns.cs
@@ -241,7 +241,6 @@ namespace Mono.Linker.Tests.Cases.UnreachableBlock
}
[Kept]
- [ExpectedExceptionHandlerSequence (new string[0])]
[ExpectedLocalsSequence (new string[0])]
[ExpectedInstructionSequence (new[] {
"call",
diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryCatchBlocks.cs b/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryCatchBlocks.cs
index bf1f4f852..465c0bf1d 100644
--- a/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryCatchBlocks.cs
+++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryCatchBlocks.cs
@@ -10,43 +10,84 @@ namespace Mono.Linker.Tests.Cases.UnreachableBlock
{
public static void Main ()
{
- TestSimpleTryUnreachable ();
+ TryCatchInRemovedBranch.Test ();
+ TryCatchInKeptBranchBeforeRemovedBranch.Test ();
}
- [Kept]
- [ExpectedInstructionSequence (new[] {
- "call",
- "ldc.i4.6",
- "beq.s il_8",
- "ldc.i4.3",
- "ret"
- })]
- [ExpectedExceptionHandlerSequence (new string[0])]
- [ExpectedLocalsSequence (new string[0])]
- static int TestSimpleTryUnreachable ()
+ class TryCatchInRemovedBranch
{
- if (Prop != 6) {
- try {
- Unreached_1 ();
- return 1;
- } catch {
- return 2;
+ [Kept]
+ [ExpectedInstructionSequence (new[] {
+ "call",
+ "ldc.i4.6",
+ "beq.s il_8",
+ "ldc.i4.3",
+ "ret"
+ })]
+ [ExpectedLocalsSequence (new string[0])]
+ public static int Test ()
+ {
+ if (Prop != 6) {
+ try {
+ Unreached_1 ();
+ return 1;
+ } catch {
+ return 2;
+ }
}
- }
- return 3;
- }
+ return 3;
+ }
- [Kept]
- static int Prop {
[Kept]
- get {
- return 6;
+ static int Prop {
+ [Kept]
+ get {
+ return 6;
+ }
+ }
+
+ static void Unreached_1 ()
+ {
}
}
- static void Unreached_1 ()
+ class TryCatchInKeptBranchBeforeRemovedBranch
{
+ [Kept]
+ [ExpectedInstructionSequence (new[] {
+ "call",
+ "pop",
+ ".try",
+ "call",
+ "leave.s il_15",
+ ".endtry",
+ ".catch",
+ "pop",
+ "call",
+ "leave.s il_15",
+ ".endcatch",
+ "ret",
+ })]
+ public static void Test ()
+ {
+ if (Prop == 0) {
+ try { Reached (); } catch { Reached_2 (); }
+ } else {
+ Unreached ();
+ }
+ }
+
+ [Kept]
+ static int Prop { [Kept] get => 0; }
+
+ [Kept]
+ static void Reached () { }
+
+ [Kept]
+ static void Reached_2 () { }
+
+ static void Unreached () { }
}
}
} \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFilterBlocks.cs b/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFilterBlocks.cs
index 2c9c344f5..af5a20821 100644
--- a/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFilterBlocks.cs
+++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFilterBlocks.cs
@@ -35,7 +35,6 @@ namespace Mono.Linker.Tests.Cases.UnreachableBlock
"ldc.i4.2",
"ret",
})]
- [ExpectedExceptionHandlerSequence (new string[] { "filter" })]
static int TestUnreachableInsideTry ()
{
try {
@@ -70,7 +69,6 @@ namespace Mono.Linker.Tests.Cases.UnreachableBlock
"ldc.i4.3",
"ret",
})]
- [ExpectedExceptionHandlerSequence (new string[] { "filter" })]
static int TestUnreachableInsideFilterCondition ()
{
try {
diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFinallyBlocks.cs b/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFinallyBlocks.cs
index 1c1dfdabd..2a62bf523 100644
--- a/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFinallyBlocks.cs
+++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/TryFinallyBlocks.cs
@@ -10,36 +10,107 @@ namespace Mono.Linker.Tests.Cases.UnreachableBlock
{
public static void Main ()
{
- TestSimpleTry ();
+ TryFinallyInConstantProperty.Test ();
+ TryFinallyInRemovedBranch.Test ();
+ TryFinallyInKeptBranchBeforeRemovedBranch.Test ();
}
- [Kept]
- [ExpectedInstructionSequence (new[] {
- "call",
- "ldc.i4.3",
- "beq.s il_8",
- "ret"
- })]
- static void TestSimpleTry ()
+ class TryFinallyInConstantProperty
{
- if (Prop != 3)
- Unreached_1 ();
- }
+ [Kept]
+ [ExpectedInstructionSequence (new[] {
+ "call",
+ "ldc.i4.3",
+ "beq.s il_8",
+ "ret"
+ })]
+ public static void Test ()
+ {
+ if (Prop != 3)
+ Unreached_1 ();
+ }
- [Kept]
- static int Prop {
[Kept]
- get {
- try {
- return 3;
- } finally {
+ static int Prop {
+ [Kept]
+ get {
+ try {
+ return 3;
+ } finally {
+ }
}
}
+
+ static void Unreached_1 ()
+ {
+ }
}
- static void Unreached_1 ()
+ class TryFinallyInRemovedBranch
{
+ [Kept]
+ [ExpectedInstructionSequence (new[] {
+ "call",
+ "pop",
+ "call",
+ "ret",
+ })]
+ public static void Test ()
+ {
+ if (Prop == 0) {
+ Reached ();
+ } else {
+ try { Unreached (); } finally { Unreached_2 (); }
+ }
+ }
+
+ [Kept]
+ static int Prop { [Kept] get => 0; }
+
+ [Kept]
+ static void Reached () { }
+
+ static void Unreached () { }
+
+ static void Unreached_2 () { }
+ }
+
+ class TryFinallyInKeptBranchBeforeRemovedBranch
+ {
+ [Kept]
+ [ExpectedInstructionSequence (new[] {
+ "call",
+ "pop",
+ ".try",
+ "call",
+ "leave.s il_13",
+ ".endtry",
+ ".catch",
+ "call",
+ "endfinally",
+ ".endcatch",
+ "ret",
+ })]
+ public static void Test ()
+ {
+ if (Prop == 0) {
+ try { Reached (); } finally { Reached_2 (); }
+ } else {
+ Unreached ();
+ }
+ }
+
+ [Kept]
+ static int Prop { [Kept] get => 0; }
+
+ [Kept]
+ static void Reached () { }
+
+ [Kept]
+ static void Reached_2 () { }
+
+ static void Unreached () { }
}
}
} \ No newline at end of file
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
index 0bc38dc48..6b0bd75c5 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
@@ -377,7 +377,6 @@ namespace Mono.Linker.Tests.TestCasesRunner
if (!src.HasBody)
return;
- VerifyExceptionHandlers (src, linked);
VerifyInstructions (src, linked);
VerifyLocals (src, linked);
}
@@ -406,7 +405,10 @@ namespace Mono.Linker.Tests.TestCasesRunner
foreach (var exHandler in body.ExceptionHandlers) {
if (existingTryBlocks.Add ((exHandler.TryStart, exHandler.TryEnd))) {
InsertBeforeInstruction (exHandler.TryStart, ".try");
- InsertBeforeInstruction (exHandler.TryEnd, ".endtry");
+ if (exHandler.TryEnd != null)
+ InsertBeforeInstruction (exHandler.TryEnd, ".endtry");
+ else
+ Append (".endtry");
}
if (exHandler.HandlerStart != null)
@@ -414,6 +416,8 @@ namespace Mono.Linker.Tests.TestCasesRunner
if (exHandler.HandlerEnd != null)
InsertBeforeInstruction (exHandler.HandlerEnd, ".endcatch");
+ else
+ Append (".endcatch");
if (exHandler.FilterStart != null)
InsertBeforeInstruction (exHandler.FilterStart, ".filter");
@@ -423,6 +427,9 @@ namespace Mono.Linker.Tests.TestCasesRunner
void InsertBeforeInstruction (Instruction instruction, string text) =>
result.Insert (result.FindIndex (i => i.Item1 == instruction), (null, text));
+
+ void Append (string text) =>
+ result.Add ((null, text));
}
static string FormatInstruction (Instruction instr)
@@ -469,18 +476,6 @@ namespace Mono.Linker.Tests.TestCasesRunner
}
}
- static void VerifyExceptionHandlers (MethodDefinition src, MethodDefinition linked)
- {
- VerifyBodyProperties (
- src,
- linked,
- nameof (ExpectedExceptionHandlerSequenceAttribute),
- nameof (ExpectExceptionHandlersModifiedAttribute),
- "exception handlers",
- m => m.Body.ExceptionHandlers.Select (h => h.HandlerType.ToString ().ToLower ()).ToArray (),
- attr => GetStringArrayAttributeValue (attr).Select (v => v.ToLower ()).ToArray ());
- }
-
static void VerifyLocals (MethodDefinition src, MethodDefinition linked)
{
VerifyBodyProperties (