Browse Source

Pass the compilation to ILInstruction.CheckInvariant

Invariants that involve types (stack type of a variable against its IType,
the element type of an array access, the operand types of a comparison)
need a type system to resolve them against, and the only correct one is the
type system the instruction tree was decoded with. Until now CheckInvariant
took only the phase, so such a check had no compilation to use:
DeconstructInstruction.CheckInvariant called IsAssignment with a null type
system, which only held up because the targets it sees are ldloc, whose
InferType never touches the compilation; a ldflda-wrapped or pointer target
would have failed inside the invariant instead of reporting a violation.

Every call site already has that type system in scope: the ILReader's
compilation, the ILTransformContext of the running transform, or the
decompiler's own IDecompilerTypeSystem. It is now passed explicitly and the
base implementation asserts it is present, so a future invariant can rely
on it without re-plumbing the callers.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/4085/head
Siegfried Pammer 2 weeks ago
parent
commit
cd212ea05f
  1. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 4
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  3. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  4. 2
      ICSharpCode.Decompiler/IL/ILReader.cs
  5. 76
      ICSharpCode.Decompiler/IL/Instructions.cs
  6. 4
      ICSharpCode.Decompiler/IL/Instructions.tt
  7. 4
      ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs
  8. 8
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  9. 5
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  10. 6
      ICSharpCode.Decompiler/IL/Instructions/Branch.cs
  11. 4
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
  12. 4
      ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs
  13. 4
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  14. 4
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  15. 4
      ICSharpCode.Decompiler/IL/Instructions/Conv.cs
  16. 6
      ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs
  17. 8
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  18. 11
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  19. 6
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  20. 6
      ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs
  21. 6
      ICSharpCode.Decompiler/IL/Instructions/Leave.cs
  22. 10
      ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs
  23. 6
      ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs
  24. 9
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  25. 6
      ICSharpCode.Decompiler/IL/Instructions/StLoc.cs
  26. 4
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  27. 5
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  28. 6
      ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs
  29. 2
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  30. 2
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  31. 2
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  32. 2
      ICSharpCode.Decompiler/IL/Transforms/StatementTransform.cs
  33. 2
      doc/DecompilerArchitecture.html

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -2389,7 +2389,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2389,7 +2389,7 @@ namespace ICSharpCode.Decompiler.CSharp
return;
}
function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken);
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, typeSystem);
AddAnnotationsToDeclaration(method, entityDecl, function, parameterOffset);

4
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -143,9 +143,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -143,9 +143,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
InlineBodyOfMoveNext(function);
function.CheckInvariant(ILPhase.InAsyncAwait);
function.CheckInvariant(ILPhase.InAsyncAwait, context.TypeSystem);
CleanUpBodyOfMoveNext(function);
function.CheckInvariant(ILPhase.InAsyncAwait);
function.CheckInvariant(ILPhase.InAsyncAwait, context.TypeSystem);
AnalyzeStateMachine(function);
DetectAwaitPattern(function);

2
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid
// (though some may point to now-deleted blocks)
newBody.SortBlocks(deleteUnreachableBlocks: true);
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, context.TypeSystem);
try
{

2
ICSharpCode.Decompiler/IL/ILReader.cs

@ -543,7 +543,7 @@ namespace ICSharpCode.Decompiler.IL @@ -543,7 +543,7 @@ namespace ICSharpCode.Decompiler.IL
var inst = decodedInstruction.Instruction;
if (inst.ResultType == StackType.Unknown && inst.OpCode != OpCode.InvalidBranch && inst.OpCode != OpCode.InvalidExpression)
Warn("Unknown result type (might be due to invalid IL or missing references)");
inst.CheckInvariant(ILPhase.InILReader);
inst.CheckInvariant(ILPhase.InILReader, compilation);
int end = reader.Offset;
inst.AddILRange(new Interval(start, end));
if (!decodedInstruction.PushedOnExpressionStack)

76
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1038,9 +1038,9 @@ namespace ICSharpCode.Decompiler.IL @@ -1038,9 +1038,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as PinnedRegion;
return o != null && variable == o.variable && this.init.PerformMatch(o.init, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
DebugAssert(Variable.Kind == VariableKind.PinnedRegionLocal);
@ -1940,9 +1940,9 @@ namespace ICSharpCode.Decompiler.IL @@ -1940,9 +1940,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LockInstruction;
return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(onExpression.ResultType == StackType.O);
}
}
@ -2084,9 +2084,9 @@ namespace ICSharpCode.Decompiler.IL @@ -2084,9 +2084,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as UsingInstruction;
return o != null && variable == o.variable && this.resourceExpression.PerformMatch(o.resourceExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
DebugAssert(resourceExpression.ResultType == StackType.O);
@ -2363,9 +2363,9 @@ namespace ICSharpCode.Decompiler.IL @@ -2363,9 +2363,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLoc;
return o != null && variable == o.variable;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
}
@ -2437,9 +2437,9 @@ namespace ICSharpCode.Decompiler.IL @@ -2437,9 +2437,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLoca;
return o != null && variable == o.variable;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
}
@ -3567,9 +3567,9 @@ namespace ICSharpCode.Decompiler.IL @@ -3567,9 +3567,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Cpblk;
return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(destAddress.ResultType == StackType.I || destAddress.ResultType == StackType.Ref);
DebugAssert(sourceAddress.ResultType == StackType.I || sourceAddress.ResultType == StackType.Ref);
DebugAssert(size.ResultType == StackType.I4);
@ -3718,9 +3718,9 @@ namespace ICSharpCode.Decompiler.IL @@ -3718,9 +3718,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Initblk;
return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(address.ResultType == StackType.I || address.ResultType == StackType.Ref);
DebugAssert(value.ResultType == StackType.I4);
DebugAssert(size.ResultType == StackType.I4);
@ -4081,9 +4081,9 @@ namespace ICSharpCode.Decompiler.IL @@ -4081,9 +4081,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdObj;
return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
}
}
@ -4191,9 +4191,9 @@ namespace ICSharpCode.Decompiler.IL @@ -4191,9 +4191,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdObjIfRef;
return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
}
}
@ -4330,9 +4330,9 @@ namespace ICSharpCode.Decompiler.IL @@ -4330,9 +4330,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StObj;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
DebugAssert(value.ResultType == type.GetStackType());
CheckTargetSlot();
@ -4856,9 +4856,9 @@ namespace ICSharpCode.Decompiler.IL @@ -4856,9 +4856,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLen;
return o != null && this.array.PerformMatch(o.array, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(array.ResultType == StackType.O);
}
}
@ -5218,9 +5218,9 @@ namespace ICSharpCode.Decompiler.IL @@ -5218,9 +5218,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as GetPinnableReference;
return o != null && this.argument.PerformMatch(o.argument, ref match) && object.Equals(method, o.method);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@ -5307,9 +5307,9 @@ namespace ICSharpCode.Decompiler.IL @@ -5307,9 +5307,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StringToInt;
return o != null && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@ -5831,9 +5831,9 @@ namespace ICSharpCode.Decompiler.IL @@ -5831,9 +5831,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicConvertInstruction;
return o != null && type.Equals(o.type) && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@ -5919,9 +5919,9 @@ namespace ICSharpCode.Decompiler.IL @@ -5919,9 +5919,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicGetMemberInstruction;
return o != null && this.target.PerformMatch(o.target, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.O);
}
}
@ -6024,9 +6024,9 @@ namespace ICSharpCode.Decompiler.IL @@ -6024,9 +6024,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicSetMemberInstruction;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.O);
}
}
@ -6467,9 +6467,9 @@ namespace ICSharpCode.Decompiler.IL @@ -6467,9 +6467,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicIsEventInstruction;
return o != null && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@ -6608,9 +6608,9 @@ namespace ICSharpCode.Decompiler.IL @@ -6608,9 +6608,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as MatchInstruction;
return o != null && variable == o.variable && object.Equals(method, o.method) && this.IsDeconstructCall == o.IsDeconstructCall && this.IsDeconstructTuple == o.IsDeconstructTuple && this.CheckType == o.CheckType && this.CheckNotNull == o.CheckNotNull && this.testedOperand.PerformMatch(o.testedOperand, ref match) && Patterns.ListMatch.DoMatch(this.SubPatterns, o.SubPatterns, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
AdditionalInvariants();
@ -6984,9 +6984,9 @@ namespace ICSharpCode.Decompiler.IL @@ -6984,9 +6984,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DeconstructResultInstruction;
return o != null && this.Argument.PerformMatch(o.Argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
AdditionalInvariants();
}
}

4
ICSharpCode.Decompiler/IL/Instructions.tt

@ -455,9 +455,9 @@ namespace <#=opCode.Namespace#> @@ -455,9 +455,9 @@ namespace <#=opCode.Namespace#>
}
<# } #>
<# if (opCode.Invariants.Count > 0) { #>
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
<# foreach (var invariant in opCode.Invariants) {#>
<#=invariant#>
<# } #>

4
ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs

@ -129,9 +129,9 @@ namespace ICSharpCode.Decompiler.IL @@ -129,9 +129,9 @@ namespace ICSharpCode.Decompiler.IL
get => IsLifted ? StackType.O : resultType;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (!IsLifted)
{
Debug.Assert(LeftInputType == Left.ResultType);

8
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -103,9 +103,9 @@ namespace ICSharpCode.Decompiler.IL @@ -103,9 +103,9 @@ namespace ICSharpCode.Decompiler.IL
return clone;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
for (int i = 0; i < Instructions.Count - 1; i++)
{
// only the last instruction may have an unreachable endpoint
@ -358,14 +358,14 @@ namespace ICSharpCode.Decompiler.IL @@ -358,14 +358,14 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public void RunTransforms(IEnumerable<IBlockTransform> transforms, BlockTransformContext context)
{
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
foreach (var transform in transforms)
{
context.CancellationToken.ThrowIfCancellationRequested();
Debug.Assert(context.IndexOfFirstAlreadyTransformedInstruction <= this.Instructions.Count);
context.StepStartGroup(transform.GetType().Name);
transform.Run(this, context);
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
context.StepEndGroup();
}
}

5
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -24,6 +24,7 @@ using System.Diagnostics.CodeAnalysis; @@ -24,6 +24,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL
{
@ -191,9 +192,9 @@ namespace ICSharpCode.Decompiler.IL @@ -191,9 +192,9 @@ namespace ICSharpCode.Decompiler.IL
return BlockSlot;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Blocks.Count > 0 && EntryPoint == Blocks[0]);
Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1);
Debug.Assert(Parent is ILFunction || !ILRangeIsEmpty);

6
ICSharpCode.Decompiler/IL/Instructions/Branch.cs

@ -20,6 +20,8 @@ @@ -20,6 +20,8 @@
using System;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
@ -108,9 +110,9 @@ namespace ICSharpCode.Decompiler.IL @@ -108,9 +110,9 @@ namespace ICSharpCode.Decompiler.IL
return false;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (phase > ILPhase.InILReader)
{
Debug.Assert(targetBlock?.Parent is BlockContainer);

4
ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs

@ -68,9 +68,9 @@ namespace ICSharpCode.Decompiler.IL @@ -68,9 +68,9 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType => FunctionPointerType.ReturnType.GetStackType();
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Arguments.Count == FunctionPointerType.ParameterTypes.Length + (IsInstance ? 1 : 0));
}

4
ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs

@ -121,9 +121,9 @@ namespace ICSharpCode.Decompiler.IL @@ -121,9 +121,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
int firstArgument = (OpCode != OpCode.NewObj && !Method.IsStatic) ? 1 : 0;
Debug.Assert(Method.Parameters.Count + firstArgument == Arguments.Count);
if (firstArgument == 1)

4
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -164,9 +164,9 @@ namespace ICSharpCode.Decompiler.IL @@ -164,9 +164,9 @@ namespace ICSharpCode.Decompiler.IL
public bool IsLifted => LiftingKind != ComparisonLiftingKind.None;
public StackType UnderlyingResultType => StackType.I4;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (LiftingKind == ComparisonLiftingKind.None)
{
Debug.Assert(Left.ResultType == InputType);

4
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -79,9 +79,9 @@ namespace ICSharpCode.Decompiler.IL @@ -79,9 +79,9 @@ namespace ICSharpCode.Decompiler.IL
CheckValidTarget();
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
CheckValidTarget();
}

4
ICSharpCode.Decompiler/IL/Instructions/Conv.cs

@ -169,9 +169,9 @@ namespace ICSharpCode.Decompiler.IL @@ -169,9 +169,9 @@ namespace ICSharpCode.Decompiler.IL
this.IsLifted = isLifted;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
// Debug.Assert(Kind != ConversionKind.Invalid); // invalid conversion can happen with invalid IL/missing references
Debug.Assert(Argument.ResultType == (IsLifted ? StackType.O : InputType));
Debug.Assert(!(IsLifted && Kind == ConversionKind.StopGCTracking));

6
ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs

@ -296,9 +296,9 @@ namespace ICSharpCode.Decompiler.IL @@ -296,9 +296,9 @@ namespace ICSharpCode.Decompiler.IL
return null;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
var patternVariables = new HashSet<ILVariable>();
var conversionVariables = new HashSet<ILVariable>();
@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.IL @@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var inst in assignments.Instructions)
{
if (!(IsAssignment(inst, typeSystem: null, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
if (!(IsAssignment(inst, compilation, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
throw new InvalidOperationException("inst is not an assignment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
}

8
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -230,7 +230,7 @@ namespace ICSharpCode.Decompiler.IL @@ -230,7 +230,7 @@ namespace ICSharpCode.Decompiler.IL
this.kind = kind;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
switch (kind)
{
@ -263,7 +263,7 @@ namespace ICSharpCode.Decompiler.IL @@ -263,7 +263,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(Variables[i].IndexInFunction == i);
Variables[i].CheckInvariant();
}
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
}
void CloneVariables()
@ -401,7 +401,7 @@ namespace ICSharpCode.Decompiler.IL @@ -401,7 +401,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public void RunTransforms(IEnumerable<IILTransform> transforms, ILTransformContext context)
{
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
bool traceTransforms = DecompilerEventSource.Log.IsTransformTracingEnabled();
foreach (var transform in transforms)
{
@ -421,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL @@ -421,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL
transform.Run(this, context);
if (traceTransforms)
DecompilerEventSource.Log.ILTransformExecuted(transform, this, traceStart);
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
context.StepEndGroup(keepIfEmpty: true);
}
}

11
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -81,9 +81,16 @@ namespace ICSharpCode.Decompiler.IL @@ -81,9 +81,16 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(b, msg);
}
/// <summary>
/// Verifies the invariants of this instruction and its descendants. Only active in debug builds.
/// </summary>
/// <param name="phase">Which set of invariants applies; they tighten as the pipeline progresses.</param>
/// <param name="compilation">The compilation the instruction tree was decoded against, so that
/// invariants involving types can be resolved against the same type system the decompiler uses.</param>
[Conditional("DEBUG")]
internal virtual void CheckInvariant(ILPhase phase)
internal virtual void CheckInvariant(ILPhase phase, ICompilation compilation)
{
Debug.Assert(compilation != null);
foreach (var child in Children)
{
Debug.Assert(child.Parent == this);
@ -92,7 +99,7 @@ namespace ICSharpCode.Decompiler.IL @@ -92,7 +99,7 @@ namespace ICSharpCode.Decompiler.IL
// exception: nested ILFunctions (lambdas)
Debug.Assert(this is ILFunction || child.flags != invalidFlags || this.flags == invalidFlags);
Debug.Assert(child.IsConnected == this.IsConnected);
child.CheckInvariant(phase);
child.CheckInvariant(phase, compilation);
}
Debug.Assert((this.DirectFlags & ~this.Flags) == 0, "All DirectFlags must also appear in this.Flags");
}

6
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
@ -53,9 +55,9 @@ namespace ICSharpCode.Decompiler.IL @@ -53,9 +55,9 @@ namespace ICSharpCode.Decompiler.IL
return new IfInstruction(lhs, new LdcI4(1), rhs);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(condition.ResultType == StackType.I4);
Debug.Assert(trueInst.ResultType == falseInst.ResultType
|| trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)

6
ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs

@ -19,13 +19,15 @@ @@ -19,13 +19,15 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
public sealed partial class LdFlda
{
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
switch (field.DeclaringType.IsReferenceType)
{
case true:

6
ICSharpCode.Decompiler/IL/Instructions/Leave.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
@ -106,9 +108,9 @@ namespace ICSharpCode.Decompiler.IL @@ -106,9 +108,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(targetContainer!));
Debug.Assert(phase <= ILPhase.InILReader || phase == ILPhase.InAsyncAwait || value.ResultType == targetContainer!.ResultType);
}

10
ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
// Note: The comp instruction also supports three-valued logic via ComparisonLiftingKind.ThreeValuedLogic.
@ -29,9 +31,9 @@ namespace ICSharpCode.Decompiler.IL @@ -29,9 +31,9 @@ namespace ICSharpCode.Decompiler.IL
bool ILiftableInstruction.IsLifted => true;
StackType ILiftableInstruction.UnderlyingResultType => StackType.I4;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O);
}
}
@ -41,9 +43,9 @@ namespace ICSharpCode.Decompiler.IL @@ -41,9 +43,9 @@ namespace ICSharpCode.Decompiler.IL
bool ILiftableInstruction.IsLifted => true;
StackType ILiftableInstruction.UnderlyingResultType => StackType.I4;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O);
}
}

6
ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
@ -63,9 +65,9 @@ namespace ICSharpCode.Decompiler.IL @@ -63,9 +65,9 @@ namespace ICSharpCode.Decompiler.IL
this.FallbackInst = fallbackInst;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(valueInst.ResultType == StackType.O); // lhs is reference type or nullable type
Debug.Assert(fallbackInst.ResultType == StackType.O || Kind == NullCoalescingKind.NullableWithValueFallback);
Debug.Assert(ResultType == UnderlyingResultType || Kind == NullCoalescingKind.Nullable);

9
ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs

@ -21,6 +21,7 @@ using System.Diagnostics; @@ -21,6 +21,7 @@ using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
@ -76,9 +77,9 @@ namespace ICSharpCode.Decompiler.IL @@ -76,9 +77,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (this.RefInput)
{
Debug.Assert(Argument.ResultType == StackType.Ref, "nullable.unwrap expects reference to nullable type as input");
@ -108,9 +109,9 @@ namespace ICSharpCode.Decompiler.IL @@ -108,9 +109,9 @@ namespace ICSharpCode.Decompiler.IL
partial class NullableRewrap
{
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Argument.HasFlag(InstructionFlags.MayUnwrapNull));
}

6
ICSharpCode.Decompiler/IL/Instructions/StLoc.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
partial class StLoc
@ -36,9 +38,9 @@ namespace ICSharpCode.Decompiler.IL @@ -36,9 +38,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
internal bool ILStackWasEmpty;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
Debug.Assert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
Debug.Assert(value.ResultType == variable.StackType);

4
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -152,9 +152,9 @@ namespace ICSharpCode.Decompiler.IL @@ -152,9 +152,9 @@ namespace ICSharpCode.Decompiler.IL
this.resultType = resultType;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
bool expectNullSection = this.IsLifted;
LongSet sets = LongSet.Empty;
foreach (var section in Sections)

5
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -21,6 +21,7 @@ using System; @@ -21,6 +21,7 @@ using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL
@ -140,9 +141,9 @@ namespace ICSharpCode.Decompiler.IL @@ -140,9 +141,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
partial class TryCatchHandler
{
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Parent is TryCatch);
Debug.Assert(filter.ResultType == StackType.I4);
Debug.Assert(this.IsDescendantOf(variable.Function!));

6
ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
partial class BitNot : ILiftableInstruction
@ -43,9 +45,9 @@ namespace ICSharpCode.Decompiler.IL @@ -43,9 +45,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(IsLifted == (ResultType == StackType.O));
Debug.Assert(IsLifted || ResultType == UnderlyingResultType);
}

2
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Embed the lambda into the parent function's ILAst, so that "Show steps" can show
// how the lambda body is being transformed.
value.ReplaceWith(function);
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, context.TypeSystem);
var contextPrefix = targetMethod.Name;
foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter))

2
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// If possible, transforms plain ILAst loops into while (condition), do-while and for-loops.
/// For the invariants of the transforms <see cref="BlockContainer.CheckInvariant(ILPhase)"/>.
/// For the invariants of the transforms <see cref="BlockContainer.CheckInvariant(ILPhase, TypeSystem.ICompilation)"/>.
/// </summary>
public class HighLevelLoopTransform : IILTransform
{

2
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -487,7 +487,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -487,7 +487,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (hasBody)
{
function.DeclarationScope = (BlockContainer)rootFunction.Body;
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, context.TypeSystem);
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
function.DeclarationScope = null;

2
ICSharpCode.Decompiler/IL/Transforms/StatementTransform.cs

@ -139,7 +139,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -139,7 +139,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
transform.Run(block, pos, ctx);
#if DEBUG
block.Instructions[pos].CheckInvariant(ILPhase.Normal);
block.Instructions[pos].CheckInvariant(ILPhase.Normal, context.TypeSystem);
for (int i = Math.Max(0, pos - 100); i < pos; ++i)
{
if (block.Instructions[i].IsDirty)

2
doc/DecompilerArchitecture.html

@ -751,7 +751,7 @@ matching is cheap. (See also <span class="filecite">doc/ILAst Pattern Matching.m @@ -751,7 +751,7 @@ matching is cheap. (See also <span class="filecite">doc/ILAst Pattern Matching.m
<h3>5.6 Invariants</h3>
<p><code>CheckInvariant(ILPhase)</code> verifies parent/child consistency, flag correctness, and
<p><code>CheckInvariant(ILPhase, ICompilation)</code> verifies parent/child consistency, flag correctness, and
connectedness. The phase parameter exists because invariants tighten over time: in
<code>ILPhase.InILReader</code>, branches may still point at offsets; from
<code>ILPhase.Normal</code> on, the full rules apply. <code>ILFunction.RunTransforms</code> checks the

Loading…
Cancel
Save