mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
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
146 lines
4.6 KiB
C#
146 lines
4.6 KiB
C#
#nullable enable
|
|
// Copyright (c) 2014 Daniel Grunwald
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
// software and associated documentation files (the "Software"), to deal in the Software
|
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all copies or
|
|
// substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
|
|
using ICSharpCode.Decompiler.TypeSystem;
|
|
|
|
namespace ICSharpCode.Decompiler.IL
|
|
{
|
|
/// <summary>
|
|
/// Unconditional branch. <c>goto target;</c>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When jumping to the entrypoint of the current block container, the branch represents a <c>continue</c> statement.
|
|
/// Will implicitly execute finally blocks when jumping out of a try-block.
|
|
/// </remarks>
|
|
partial class Branch : SimpleInstruction, IBranchOrLeaveInstruction
|
|
{
|
|
readonly int targetILOffset;
|
|
Block? targetBlock;
|
|
|
|
public Branch(int targetILOffset) : base(OpCode.Branch)
|
|
{
|
|
this.targetILOffset = targetILOffset;
|
|
}
|
|
|
|
public Branch(Block targetBlock) : base(OpCode.Branch)
|
|
{
|
|
this.targetBlock = targetBlock ?? throw new ArgumentNullException(nameof(targetBlock));
|
|
this.targetILOffset = targetBlock.StartILOffset;
|
|
}
|
|
|
|
public int TargetILOffset {
|
|
get { return targetBlock != null ? targetBlock.StartILOffset : targetILOffset; }
|
|
}
|
|
|
|
public Block TargetBlock {
|
|
get {
|
|
// HACK: We treat TargetBlock as non-nullable publicly, because it's only null inside
|
|
// the ILReader, and becomes non-null once the BlockBuilder has run.
|
|
return targetBlock!;
|
|
}
|
|
set {
|
|
if (targetBlock != null && IsConnected)
|
|
targetBlock.IncomingEdgeCount--;
|
|
targetBlock = value;
|
|
if (targetBlock != null && IsConnected)
|
|
targetBlock.IncomingEdgeCount++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the BlockContainer that contains the target block.
|
|
/// </summary>
|
|
public BlockContainer TargetContainer {
|
|
get { return (BlockContainer)targetBlock?.Parent!; }
|
|
}
|
|
|
|
protected override void Connected()
|
|
{
|
|
base.Connected();
|
|
if (targetBlock != null)
|
|
targetBlock.IncomingEdgeCount++;
|
|
}
|
|
|
|
protected override void Disconnected()
|
|
{
|
|
base.Disconnected();
|
|
if (targetBlock != null)
|
|
targetBlock.IncomingEdgeCount--;
|
|
}
|
|
|
|
public string TargetLabel {
|
|
get { return targetBlock != null ? targetBlock.Label : string.Format("IL_{0:x4}", TargetILOffset); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether this branch executes at least one finally block before jumping to the target block.
|
|
/// </summary>
|
|
public bool TriggersFinallyBlock {
|
|
get {
|
|
return GetExecutesFinallyBlock(this, TargetContainer);
|
|
}
|
|
}
|
|
|
|
internal static bool GetExecutesFinallyBlock(ILInstruction? inst, BlockContainer? container)
|
|
{
|
|
for (; inst != container && inst != null; inst = inst.Parent)
|
|
{
|
|
if (inst.Parent is TryFinally && inst.SlotInfo == TryFinally.TryBlockSlot)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
|
|
{
|
|
base.CheckInvariant(phase, compilation);
|
|
if (phase > ILPhase.InILReader)
|
|
{
|
|
Debug.Assert(targetBlock?.Parent is BlockContainer);
|
|
Debug.Assert(this.IsDescendantOf(targetBlock!.Parent!));
|
|
Debug.Assert(targetBlock!.Parent!.Children[targetBlock.ChildIndex] == targetBlock);
|
|
}
|
|
}
|
|
|
|
protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options)
|
|
{
|
|
WriteILRange(output, options);
|
|
output.Write(OpCode);
|
|
output.Write(' ');
|
|
output.WriteLocalReference(TargetLabel, (object?)targetBlock ?? TargetILOffset);
|
|
}
|
|
}
|
|
|
|
interface IBranchOrLeaveInstruction
|
|
{
|
|
/// <summary>
|
|
/// The block container that directly contains the jump target.
|
|
/// </summary>
|
|
BlockContainer TargetContainer { get; }
|
|
|
|
/// <summary>
|
|
/// Gets whether this branch executes at least one finally block before reaching the jump target.
|
|
/// </summary>
|
|
bool TriggersFinallyBlock { get; }
|
|
}
|
|
}
|