Browse Source

Return an enum from the sequential store offset matcher

The bool-plus-out-abortTransform contract encoded three outcomes in two
flags, so false meant either 'sequence ended' or 'reject the transform'
depending on the flag. A three-value enum names each outcome at the
return site, and passing minExpectedOffset by ref makes visible that
only the binary.add path advances the expected offset (the bare ldloc
path previously echoed it back through an out parameter).

Assisted-by: Claude:claude-fable-5:Claude Code
master
Siegfried Pammer 24 hours ago committed by Siegfried Pammer
parent
commit
ca307f5886
  1. 45
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

45
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -462,13 +462,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
break; break;
} }
if (!TryGetSequentialStoreOffset(target, store, elementType, minExpectedOffset, out var offset, out var abortTransform)) var match = GetSequentialStoreOffset(target, store, elementType, ref minExpectedOffset);
{ if (match == SequentialStoreMatch.Abort)
if (abortTransform) return false;
return false; if (match != SequentialStoreMatch.Matched)
break; break;
}
minExpectedOffset = offset;
if (values == null) if (values == null)
{ {
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true); var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true);
@ -529,34 +527,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
static bool TryGetSequentialStoreOffset(ILInstruction target, ILVariable store, IType elementType, long minExpectedOffset, out long offset, out bool abortTransform) enum SequentialStoreMatch
{ {
offset = 0; // The store belongs to the initializer sequence.
abortTransform = false; Matched,
// The store is not part of the sequence; it ends the scan, keeping what was matched.
SequenceEnd,
// The store's shape is unexpected; reject the whole transform.
Abort,
}
static SequentialStoreMatch GetSequentialStoreOffset(ILInstruction target, ILVariable store, IType elementType, ref long minExpectedOffset)
{
// stobj T(ldloc store, value) writes at the current expected offset, initially 0. // stobj T(ldloc store, value) writes at the current expected offset, initially 0.
if (target.MatchLdLoc(store)) if (target.MatchLdLoc(store))
{ return SequentialStoreMatch.Matched;
offset = minExpectedOffset;
return true;
}
// stobj T(binary.add(ldloc store, offset), value) // stobj T(binary.add(ldloc store, offset), value)
// The offset is either sizeof(T) or an element index multiplied by sizeof(T). // The offset is either sizeof(T) or an element index multiplied by sizeof(T).
if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
{ return SequentialStoreMatch.Abort;
abortTransform = true;
return false;
}
if (!left.MatchLdLoc(store)) if (!left.MatchLdLoc(store))
return false; return SequentialStoreMatch.SequenceEnd;
var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow); var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow);
if (offsetInst == null) if (offsetInst == null)
{ return SequentialStoreMatch.Abort;
abortTransform = true; if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset)
return false; return SequentialStoreMatch.SequenceEnd;
} minExpectedOffset = offset;
return offsetInst.MatchLdcI(out offset) && offset >= 0 && offset >= minExpectedOffset; return SequentialStoreMatch.Matched;
} }
ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type) ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type)

Loading…
Cancel
Save