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

SamboyCoding / Cpp2IL / 17216182984

25 Aug 2025 05:35PM UTC coverage: 30.38% (-4.0%) from 34.352%
17216182984

Pull #481

github

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

1804 of 7561 branches covered (23.86%)

Branch coverage included in aggregate %.

100 of 1839 new or added lines in 29 files covered. (5.44%)

41 existing lines in 6 files now uncovered.

4093 of 11850 relevant lines covered (34.54%)

165575.01 hits per line

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

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

7
namespace Cpp2IL.Core.Analysis;
8

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

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

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

27
    public static void Analyze(MethodAnalysisContext method)
28
    {
NEW
29
        var analyzer = new StackAnalyzer();
×
30

NEW
31
        var graph = method.ControlFlowGraph!;
×
NEW
32
        graph.RemoveUnreachableBlocks(); // Without this indirect jumps (in try catch i think) cause some weird stuff
×
33

NEW
34
        analyzer._inComingState = new Dictionary<Block, StackState> { { graph.EntryBlock, new StackState() } };
×
35

NEW
36
        analyzer.TraverseGraph(graph.EntryBlock);
×
37

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

NEW
45
        analyzer.CorrectOffsets(graph);
×
NEW
46
        ReplaceStackWithRegisters(method);
×
47

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

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

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

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

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

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

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

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

NEW
111
        _outGoingState[block] = currentState;
×
112

NEW
113
        visitedBlockCount++;
×
114

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

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

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

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

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

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

NEW
163
            if (parameter is StackOffset offset)
×
164
            {
NEW
165
                var name = offset.Offset < 0 ? $"stack_-{-offset.Offset:X}" : $"stack_{offset.Offset:X}";
×
NEW
166
                method.ParameterOperands[i] = new Register(null, name);
×
167
            }
168
        }
NEW
169
    }
×
170
}
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

© 2025 Coveralls, Inc