diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
index 6a0390bad..e3b4418c6 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
@@ -17,6 +17,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ICSharpCode.Decompiler.TypeSystem;
@@ -467,6 +468,24 @@ namespace ICSharpCode.Decompiler.IL
}
}
+ ///
+ /// For == Equality, matches 'comp(arg == ldc.i4 0)' or 'comp.unsigned(arg <= ldc.i4 0)'.
+ /// For == Inequality, matches 'comp(arg != ldc.i4 0)' or 'comp.unsigned(arg > ldc.i4 0)'.
+ ///
+ public bool MatchCompUnsignedZero(ComparisonKind kind, [NotNullWhen(true)] out ILInstruction? arg)
+ {
+ Debug.Assert(kind.IsEqualityOrInequality());
+ var unsignedKind = kind == ComparisonKind.Equality ? ComparisonKind.LessThanOrEqual : ComparisonKind.GreaterThan;
+ if (this is Comp { IsLifted: false } comp && comp.Right.MatchLdcI4(0)
+ && (comp.Kind == kind || (comp.Kind == unsignedKind && comp.Sign == Sign.Unsigned)))
+ {
+ arg = comp.Left;
+ return true;
+ }
+ arg = null;
+ return false;
+ }
+
public bool MatchLdFld([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IField? field)
{
if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile)
diff --git a/ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs b/ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs
index 2cc1ba5ef..9b6d7e67b 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs
@@ -72,15 +72,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
PropagateExceptionVariable(context, catchBlock);
}
else if (catchBlock.Filter is BlockContainer filterContainer
- && MatchTypeTestConjunction(catchBlock.Variable, filterContainer,
- out var conjunctionType, out var typeTest)
- && conjunctionType.GetStackType() == catchBlock.Variable.StackType)
+ && MatchVBOnErrorCatchFilter(context, catchBlock.Variable, filterContainer, out exceptionType, out var typeTest)
+ && exceptionType.GetStackType() == catchBlock.Variable.StackType)
{
- context.Step($"Detected catch-when type test for {catchBlock.Variable.Name}", typeTest);
- catchBlock.Variable.Type = conjunctionType;
- var remainder = typeTest.Right;
- typeTest.ReplaceWith(remainder);
- context.EndStep(remainder);
+ context.Step($"Detected catch-when for {catchBlock.Variable.Name} (bit.and)", typeTest);
+ catchBlock.Variable.Type = exceptionType;
+ var condition = typeTest.Right;
+ typeTest.ReplaceWith(condition);
+ context.EndStep(condition);
PropagateExceptionVariable(context, catchBlock);
}
@@ -90,16 +89,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// BlockContainer {
/// Block entryPoint (incoming: 1) {
- /// leave container(bit.and(bit.and(comp(isinst T(ldloc exceptionVar) != ldnull), cond1), cond2))
+ /// leave container(bit.and(bit.and(comp(isinst System.Exception(ldloc exceptionVar) > ldnull), comp.unsigned(ldloc activeHandler > ldc.i4 0)), logic.not(ldloc resumeTarget)))
/// }
/// }
- /// The Visual Basic compiler emits the whole filter as one non-short-circuiting
- /// expression rather than the block chain csc emits, so the type test sits in a
- /// conjunction instead of a branch. On a match, typeTest is the innermost `bit.and`,
- /// whose right operand is the filter that remains once the test moves to the catch type.
+ /// Only emitted for On Error Resume Next/GoTo.
///
- bool MatchTypeTestConjunction(ILVariable exceptionVar, BlockContainer container,
- out IType exceptionType, out BinaryNumericInstruction typeTest)
+ bool MatchVBOnErrorCatchFilter(ILTransformContext context, ILVariable exceptionVar, BlockContainer container, out IType exceptionType, out BinaryNumericInstruction typeTest)
{
exceptionType = null;
typeTest = null;
@@ -108,38 +103,31 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!entryPoint.Instructions[0].MatchLeave(container, out var condition))
return false;
- // Once the test moves to the catch type, the other conjuncts stop running for a
- // non-matching exception, which is only invisible if they have no side effects.
- if (!SemanticHelper.IsPure(condition.Flags))
+ if (condition is not BinaryNumericInstruction { Operator: BinaryNumericOperator.BitAnd, Left: BinaryNumericInstruction { Operator: BinaryNumericOperator.BitAnd } bitAnd } outer)
return false;
- // The test is the leftmost operand of a left-nested chain of `&`.
- while (condition is BinaryNumericInstruction { Operator: BinaryNumericOperator.BitAnd } and)
- {
- if (MatchTypeTest(and.Left, exceptionVar, out exceptionType))
- {
- typeTest = and;
- return true;
- }
- condition = and.Left;
- }
- return false;
+ if (!outer.Right.MatchCompUnsignedZero(ComparisonKind.Equality, out var resumeTarget) || !resumeTarget.MatchLdLoc(out _))
+ return false;
+ if (!bitAnd.Right.MatchCompUnsignedZero(ComparisonKind.Inequality, out var activeHandler) || !activeHandler.MatchLdLoc(out _))
+ return false;
+ if (bitAnd.Left is not Comp comp)
+ return false;
+ EarlyExpressionTransforms.FixComparisonKindLdNull(comp, context);
+ if (!MatchIsInstNotNull(comp, exceptionVar, out _, out exceptionType) || !exceptionType.IsKnownType(KnownTypeCode.Exception))
+ return false;
+ typeTest = bitAnd;
+ return true;
}
///
- /// comp(isinst T(ldloc exceptionVar) != ldnull), however the compiler spelled the null test.
+ /// comp(isinst exceptionType(ldloc exceptionVar) != ldnull)
///
- static bool MatchTypeTest(ILInstruction condition, ILVariable exceptionVar, out IType exceptionType)
+ static bool MatchIsInstNotNull(ILInstruction condition, ILVariable exceptionVar, out ILInstruction exceptionSlot, out IType exceptionType)
{
+ exceptionSlot = null;
exceptionType = null;
- if (condition is not Comp comp || !comp.Right.MatchLdNull())
- return false;
- // `cgt.un x, null` is how both compilers spell `x != null` for a reference.
- if (comp.Kind != ComparisonKind.Inequality
- && !(comp.Kind == ComparisonKind.GreaterThan && comp.InputType == StackType.O))
- return false;
- if (!comp.Left.MatchIsInst(out var argument, out exceptionType))
- return false;
- return argument.MatchLdLoc(exceptionVar);
+ return condition.MatchCompNotEqualsNull(out var arg)
+ && arg.MatchIsInst(out exceptionSlot, out exceptionType)
+ && exceptionSlot.MatchLdLoc(exceptionVar);
}
///
@@ -263,18 +251,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// br falseBlock
if (!entryPoint.Instructions[0].MatchIfInstruction(out var condition, out var branch))
return false;
- if (!condition.MatchCompNotEquals(out var left, out var right))
+ if (!MatchIsInstNotNull(condition, exceptionVar, out exceptionSlot, out exceptionType))
return false;
if (!entryPoint.Instructions[1].MatchBranch(out var falseBlock) || !MatchFalseBlock(container, falseBlock, out var returnVar, out var exitBlock))
return false;
- if (!left.MatchIsInst(out exceptionSlot, out exceptionType))
- return false;
- if (!exceptionSlot.MatchLdLoc(exceptionVar))
- return false;
- if (right.MatchLdNull())
- {
- return branch.MatchBranch(out whenConditionBlock);
- }
+ return branch.MatchBranch(out whenConditionBlock);
}
return false;
}