• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 17101091060

20 Aug 2025 02:16PM UTC coverage: 30.421% (-3.9%) from 34.352%
17101091060

Pull #481

github

web-flow
Merge 42542dde2 into d5260685f
Pull Request #481: Decompiler

1804 of 7551 branches covered (23.89%)

Branch coverage included in aggregate %.

102 of 1827 new or added lines in 35 files covered. (5.58%)

40 existing lines in 6 files now uncovered.

4095 of 11840 relevant lines covered (34.59%)

165714.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Cpp2IL.Core/Actions/StackAnalyzer.cs
1
using System.Collections.Generic;
2
using System.Diagnostics;
3
using System.Linq;
4
using Cpp2IL.Core.Graphs;
5
using Cpp2IL.Core.ISIL;
6
using Cpp2IL.Core.Model.Contexts;
7

8
namespace Cpp2IL.Core.Actions;
9

10
public class StackAnalyzer : IAction
11
{
12
    [DebuggerDisplay("Size = {Size}")]
13
    private class StackState
14
    {
15
        public int Size;
NEW
16
        public StackState Copy() => new() { Size = this.Size };
×
17
    }
18

NEW
19
    private Dictionary<Block, StackState> _inComingState = [];
×
NEW
20
    private Dictionary<Block, StackState> _outGoingState = [];
×
NEW
21
    private Dictionary<Instruction, StackState> _instructionState = [];
×
22

23
    /// <summary>
24
    /// Max allowed count of blocks to visit (-1 for no limit).
25
    /// </summary>
NEW
26
    public int MaxBlockVisitCount = -1;
×
27

28
    public void Apply(MethodAnalysisContext method)
29
    {
NEW
30
        var graph = method.ControlFlowGraph!;
×
NEW
31
        graph.RemoveUnreachableBlocks(); // Without this indirect jumps (in try catch i think) cause some weird stuff
×
32

NEW
33
        _inComingState = new Dictionary<Block, StackState> { { graph.EntryBlock, new StackState() } };
×
NEW
34
        _outGoingState.Clear();
×
NEW
35
        _instructionState.Clear();
×
36

NEW
37
        TraverseGraph(graph.EntryBlock);
×
38

NEW
39
        var outDelta = _outGoingState[graph.ExitBlock];
×
NEW
40
        if (outDelta.Size != 0)
×
41
        {
NEW
42
            var outText = outDelta.Size < 0 ? "-" + (-outDelta.Size).ToString("X") : outDelta.Size.ToString("X");
×
NEW
43
            method.AddWarning($"Method ends with non empty stack ({outText}), the output could be wrong!");
×
44
        }
45

NEW
46
        CorrectOffsets(graph);
×
NEW
47
        ReplaceStackWithRegisters(method);
×
48

NEW
49
        graph.RemoveNops();
×
NEW
50
        graph.RemoveEmptyBlocks();
×
NEW
51
    }
×
52

53
    private void CorrectOffsets(ISILControlFlowGraph graph)
54
    {
NEW
55
        foreach (var block in graph.Blocks)
×
56
        {
NEW
57
            foreach (var instruction in block.Instructions)
×
58
            {
NEW
59
                if (instruction is { OpCode: OpCode.ShiftStack })
×
60
                {
61
                    // Nop the shift stack instruction
NEW
62
                    instruction.OpCode = OpCode.Nop;
×
NEW
63
                    instruction.Operands = [];
×
64
                }
65

66
                // Correct offset for stack operands.
NEW
67
                for (var i = 0; i < instruction.Operands.Count; i++)
×
68
                {
NEW
69
                    var op = instruction.Operands[i];
×
70

NEW
71
                    if (op is StackOffset offset)
×
72
                    {
NEW
73
                        var state = _instructionState[instruction].Size;
×
NEW
74
                        var actual = state + offset.Offset;
×
NEW
75
                        instruction.Operands[i] = new StackOffset(actual);
×
76
                    }
77
                }
78
            }
79
        }
NEW
80
    }
×
81

82
    // Traverse the graph and calculate the stack state for each block and instruction
83
    private void TraverseGraph(Block block, int visitedBlockCount = 0)
84
    {
85
        // Copy current state
NEW
86
        var incomingState = _inComingState[block];
×
NEW
87
        var currentState = incomingState.Copy();
×
88

89
        // Process instructions
NEW
90
        foreach (var instruction in block.Instructions)
×
91
        {
NEW
92
            _instructionState[instruction] = currentState;
×
93

NEW
94
            if (instruction.OpCode == OpCode.ShiftStack)
×
95
            {
NEW
96
                var offset = (int)instruction.Operands[0];
×
NEW
97
                currentState = currentState.Copy();
×
NEW
98
                currentState.Size += offset;
×
99
            }
NEW
100
            else if (block.Instructions[block.Instructions.Count - 1] == instruction && block.BlockType == BlockType.TailCall)
×
101
            {
102
                // Tail calls clear stack
NEW
103
                currentState = currentState.Copy();
×
NEW
104
                currentState.Size = 0;
×
105
            }
106
        }
107

108
        // Tail calls clear stack
NEW
109
        if (block.BlockType == BlockType.TailCall)
×
NEW
110
            currentState.Size = 0;
×
111

NEW
112
        _outGoingState[block] = currentState;
×
113

NEW
114
        visitedBlockCount++;
×
115

NEW
116
        if (MaxBlockVisitCount != -1 && visitedBlockCount > MaxBlockVisitCount)
×
NEW
117
            throw new DecompilerException($"Stack state not settling! ({MaxBlockVisitCount} blocks already visited)");
×
118

119
        // Visit successors
NEW
120
        foreach (var successor in block.Successors)
×
121
        {
122
            // Already visited
NEW
123
            if (_inComingState.TryGetValue(successor, out var existingState))
×
124
            {
NEW
125
                if (existingState.Size != currentState.Size)
×
126
                {
NEW
127
                    _inComingState[successor] = currentState.Copy();
×
NEW
128
                    TraverseGraph(successor, visitedBlockCount + 1);
×
129
                }
130
            }
131
            else
132
            {
133
                // Set incoming delta and add to queue
NEW
134
                _inComingState[successor] = currentState.Copy();
×
NEW
135
                TraverseGraph(successor, visitedBlockCount + 1);
×
136
            }
137
        }
NEW
138
    }
×
139

140
    private static void ReplaceStackWithRegisters(MethodAnalysisContext method)
141
    {
NEW
142
        var instructions = method.ControlFlowGraph!.Instructions;
×
143

144
        // Replace stack offset operands
NEW
145
        foreach (var instruction in instructions)
×
146
        {
NEW
147
            for (var i = 0; i < instruction.Operands.Count; i++)
×
148
            {
NEW
149
                var operand = instruction.Operands[i];
×
150

NEW
151
                if (operand is StackOffset offset)
×
152
                {
NEW
153
                    var name = offset.Offset < 0 ? $"stack_-{-offset.Offset:X}" : $"stack_{offset.Offset:X}";
×
NEW
154
                    instruction.Operands[i] = new Register(null, name);
×
155
                }
156
            }
157
        }
158

159
        // Replace params
NEW
160
        for (var i = 0; i < method.ParameterOperands.Count; i++)
×
161
        {
NEW
162
            var parameter = method.ParameterOperands[i];
×
163

NEW
164
            if (parameter is StackOffset offset)
×
165
            {
NEW
166
                var name = offset.Offset < 0 ? $"stack_-{-offset.Offset:X}" : $"stack_{offset.Offset:X}";
×
NEW
167
                method.ParameterOperands[i] = new Register(null, name);
×
168
            }
169
        }
NEW
170
    }
×
171
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc