• 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/LocalVariables.cs
1
using System.Collections.Generic;
2
using System.Linq;
3
using Cpp2IL.Core.ISIL;
4
using Cpp2IL.Core.Model.Contexts;
5

6
namespace Cpp2IL.Core.Analysis;
7

8
public static class LocalVariables
9
{
NEW
10
    public static int MaxTypePropagationLoopCount = 5000;
×
11

12
    public static void CreateAll(MethodAnalysisContext method)
13
    {
NEW
14
        var cfg = method.ControlFlowGraph!;
×
15

16
        // Get all registers
NEW
17
        var registers = new List<Register>();
×
NEW
18
        foreach (var instruction in cfg.Instructions)
×
NEW
19
            registers.AddRange(GetRegisters(instruction));
×
20

21
        // Remove duplicates
NEW
22
        registers = registers.Distinct().ToList();
×
23

24
        // Map those to locals
NEW
25
        var locals = new Dictionary<Register, LocalVariable>();
×
NEW
26
        for (var i = 0; i < registers.Count; i++)
×
27
        {
NEW
28
            var register = registers[i];
×
NEW
29
            locals.Add(register, new LocalVariable($"v{i}", register));
×
30
        }
31

32
        // Replace registers with locals
NEW
33
        foreach (var instruction in cfg.Instructions)
×
34
        {
NEW
35
            for (var i = 0; i < instruction.Operands.Count; i++)
×
36
            {
NEW
37
                var operand = instruction.Operands[i];
×
38

NEW
39
                if (operand is Register register)
×
NEW
40
                    instruction.Operands[i] = locals[register];
×
41

NEW
42
                if (operand is MemoryOperand memory)
×
43
                {
NEW
44
                    if (memory.Base != null)
×
45
                    {
NEW
46
                        var baseRegister = (Register)memory.Base;
×
NEW
47
                        memory.Base = locals[baseRegister];
×
48
                    }
49

NEW
50
                    if (memory.Index != null)
×
51
                    {
NEW
52
                        var index = (Register)memory.Index;
×
NEW
53
                        memory.Index = locals[index];
×
54
                    }
55

NEW
56
                    instruction.Operands[i] = memory;
×
57
                }
58
            }
59
        }
60

NEW
61
        method.Locals = locals.Select(kv => kv.Value).ToList();
×
62

63
        // Return local names
NEW
64
        var retValIndex = 0;
×
NEW
65
        for (var i = 0; i < cfg.Instructions.Count; i++)
×
66
        {
NEW
67
            var instruction = cfg.Instructions[i];
×
NEW
68
            if (instruction.OpCode != OpCode.Return || instruction.Operands.Count != 1) continue;
×
69

NEW
70
            var returnLocal = (LocalVariable)instruction.Sources[0];
×
71

NEW
72
            returnLocal.Name = $"returnVal{retValIndex + 1}";
×
NEW
73
            returnLocal.IsReturn = true;
×
NEW
74
            retValIndex++;
×
75
        }
76

77
        // Add parameter names
NEW
78
        var paramLocals = new List<LocalVariable>();
×
79

NEW
80
        var operandOffset = method.IsStatic ? 0 : 1; // 'this'
×
81

82
        // 'this' param
NEW
83
        if (!method.IsStatic && method.Locals.Count > 0)
×
84
        {
NEW
85
            var thisOperand = (Register)method.ParameterOperands[0];
×
NEW
86
            var thisLocal = method.Locals.FirstOrDefault(l => l.Register.Number == thisOperand.Number && l.Register.Version == -1);
×
87

NEW
88
            if (thisLocal != null)
×
89
            {
NEW
90
                thisLocal.Name = "this";
×
NEW
91
                thisLocal.IsThis = true;
×
NEW
92
                paramLocals.Add(thisLocal);
×
93
            }
94
            else
95
            {
NEW
96
                method.AddWarning($"'this' local not found (operand: {thisOperand})");
×
97
            }
98
        }
99

100
        // Check if method has MethodInfo*
NEW
101
        var hasMethodInfo = (method.ParameterOperands.Count - operandOffset) > method.Parameters.Count;
×
NEW
102
        var methodInfoIndex = method.ParameterOperands.Count - 1;
×
103

104
        // Add normal parameter names
NEW
105
        for (var i = 0; i < method.Parameters.Count; i++)
×
106
        {
NEW
107
            var operandIndex = i + operandOffset;
×
NEW
108
            if (hasMethodInfo && operandIndex == methodInfoIndex)
×
109
                break; // Skip MethodInfo*
110

NEW
111
            if (operandIndex >= method.ParameterOperands.Count)
×
112
                break;
113

NEW
114
            if (method.ParameterOperands[operandIndex] is not Register reg)
×
115
                continue;
116

NEW
117
            var local = method.Locals.FirstOrDefault(l => l.Register.Number == reg.Number && l.Register.Version == -1);
×
NEW
118
            if (local == null)
×
119
                continue;
120

NEW
121
            local.Name = method.Parameters[i].ParameterName;
×
NEW
122
            paramLocals.Add(local);
×
123
        }
124

125
        // Add MethodInfo*
NEW
126
        if (hasMethodInfo)
×
127
        {
NEW
128
            var methodInfoOperand = (Register)method.ParameterOperands[methodInfoIndex];
×
NEW
129
            var methodInfoLocal = method.Locals.FirstOrDefault(l => l.Register.Number == methodInfoOperand.Number && l.Register.Version == -1);
×
130

NEW
131
            if (methodInfoLocal != null)
×
132
            {
NEW
133
                methodInfoLocal.Name = "methodInfo";
×
NEW
134
                methodInfoLocal.IsMethodInfo = true;
×
NEW
135
                paramLocals.Add(methodInfoLocal);
×
136
            }
137
        }
138

NEW
139
        method.ParameterLocals = paramLocals;
×
NEW
140
    }
×
141

142
    public static void RemoveUnused(MethodAnalysisContext method)
143
    {
NEW
144
        var cfg = method.ControlFlowGraph!;
×
NEW
145
        cfg.BuildUseDefLists();
×
146

NEW
147
        for (var i = 0; i < method.Locals.Count; i++)
×
148
        {
NEW
149
            var local = method.Locals[i];
×
150

NEW
151
            if (cfg.Blocks.Any(b => b.Use.Contains(local) || b.Def.Contains(local)))
×
152
                continue;
153

NEW
154
            method.Locals.Remove(local);
×
NEW
155
            i--;
×
156
        }
NEW
157
    }
×
158

159
    private static List<Register> GetRegisters(Instruction instruction)
160
    {
NEW
161
        var registers = new List<Register>();
×
162

NEW
163
        foreach (var operand in instruction.Operands)
×
164
        {
NEW
165
            if (operand is Register register)
×
166
            {
NEW
167
                if (!registers.Contains(register))
×
NEW
168
                    registers.Add(register);
×
169
            }
170

NEW
171
            if (operand is MemoryOperand memory)
×
172
            {
NEW
173
                if (memory.Base != null)
×
174
                {
NEW
175
                    var baseRegister = (Register)memory.Base;
×
NEW
176
                    if (!registers.Contains(baseRegister))
×
NEW
177
                        registers.Add(baseRegister);
×
178
                }
179

NEW
180
                if (memory.Index != null)
×
181
                {
NEW
182
                    var index = (Register)memory.Index;
×
NEW
183
                    if (!registers.Contains(index))
×
NEW
184
                        registers.Add(index);
×
185
                }
186
            }
187
        }
188

NEW
189
        return registers;
×
190
    }
191

192
    public static void PropagateTypes(MethodAnalysisContext method)
193
    {
NEW
194
        PropagateFromReturn(method);
×
NEW
195
        PropagateFromParameters(method);
×
NEW
196
        PropagateFromCallParameters(method);
×
NEW
197
        PropagateThroughMoves(method);
×
NEW
198
    }
×
199

200
    private static void PropagateThroughMoves(MethodAnalysisContext method)
201
    {
NEW
202
        var changed = true;
×
NEW
203
        var loopCount = 0;
×
204

NEW
205
        while (changed)
×
206
        {
NEW
207
            changed = false;
×
NEW
208
            loopCount++;
×
209

NEW
210
            if (MaxTypePropagationLoopCount != -1 && loopCount > MaxTypePropagationLoopCount)
×
NEW
211
                throw new DecompilerException($"Type propagation through moves not settling! (looped {MaxTypePropagationLoopCount} times)");
×
212

NEW
213
            foreach (var instruction in method.ControlFlowGraph!.Instructions)
×
214
            {
NEW
215
                if (instruction.OpCode != OpCode.Move)
×
216
                    continue;
217

NEW
218
                if (instruction.Operands[0] is LocalVariable destination && instruction.Operands[1] is LocalVariable source)
×
219
                {
220
                    // Move ??, local
NEW
221
                    if (destination.Type == null && source.Type != null)
×
222
                    {
NEW
223
                        destination.Type = source.Type;
×
NEW
224
                        changed = true;
×
225
                    }
226
                    // Move local, ??
NEW
227
                    else if (source.Type == null && destination.Type != null)
×
228
                    {
NEW
229
                        source.Type = destination.Type;
×
NEW
230
                        changed = true;
×
231
                    }
232
                }
233

NEW
234
                if (instruction.Operands[0] is LocalVariable destination2 && instruction.Operands[1] is TypeAnalysisContext source2)
×
235
                {
236
                    // Move ??, type
NEW
237
                    if (destination2.Type == null)
×
238
                    {
NEW
239
                        destination2.Type = source2;
×
NEW
240
                        changed = true;
×
241
                    }
242
                }
243
            }
244
        }
NEW
245
    }
×
246

247
    private static void PropagateFromCallParameters(MethodAnalysisContext method)
248
    {
NEW
249
        foreach (var instruction in method.ControlFlowGraph!.Instructions)
×
250
        {
NEW
251
            if (!instruction.IsCall)
×
252
                continue;
253

NEW
254
            if (instruction.Operands[0] is not MethodAnalysisContext calledMethod)
×
255
                continue;
256

257
            // Constructor, set return variable type
NEW
258
            if (calledMethod.Name == ".ctor" || calledMethod.Name == ".cctor")
×
259
            {
NEW
260
                if (instruction.Destination is LocalVariable constructorReturn)
×
261
                {
NEW
262
                    constructorReturn.Type = calledMethod.DeclaringType;
×
NEW
263
                    continue;
×
264
                }
265
            }
266
            else // Return value
267
            {
NEW
268
                if (instruction.Destination is LocalVariable returnValue)
×
NEW
269
                    returnValue.Type = calledMethod.ReturnType;
×
270
            }
271

272
            // 'this' param
NEW
273
            if (!calledMethod.IsStatic)
×
274
            {
NEW
275
                if (instruction.Operands[instruction.OpCode == OpCode.CallVoid ? 1 : 2] is LocalVariable thisParam)
×
NEW
276
                    thisParam.Type = calledMethod.DeclaringType;
×
277
            }
278

279
            // Set types
NEW
280
            var paramOffset = calledMethod.IsStatic ? 1 : 2;
×
NEW
281
            if (instruction.OpCode == OpCode.Call) // Skip return value
×
NEW
282
                paramOffset += 1;
×
283

NEW
284
            for (var i = paramOffset; i < instruction.Operands.Count; i++)
×
285
            {
NEW
286
                var operand = instruction.Operands[i];
×
287

NEW
288
                if (operand is LocalVariable local)
×
289
                {
NEW
290
                    if ((i - paramOffset) > calledMethod.Parameters.Count - 1) // Probably MethodInfo*
×
291
                        continue;
292

NEW
293
                    local.Type = calledMethod.Parameters[i - paramOffset].ParameterType;
×
294
                }
295
            }
296
        }
NEW
297
    }
×
298

299
    private static void PropagateFromParameters(MethodAnalysisContext method)
300
    {
NEW
301
        if (method.Parameters.Count == 0)
×
NEW
302
            return;
×
303

304
        // 'this'
NEW
305
        if (!method.IsStatic)
×
306
        {
NEW
307
            var thisLocal = method.ParameterLocals.FirstOrDefault(p => p.IsThis);
×
NEW
308
            if (thisLocal != null)
×
NEW
309
                thisLocal.Type = method.DeclaringType;
×
310
        }
311

312
        // Normal params
NEW
313
        var paramIndex = 0;
×
NEW
314
        foreach (var local in method.ParameterLocals)
×
315
        {
NEW
316
            if (local.IsThis || local.IsMethodInfo)
×
317
                continue;
318

NEW
319
            if (paramIndex >= method.Parameters.Count)
×
NEW
320
                break;
×
321

NEW
322
            local.Type = method.Parameters[paramIndex].ParameterType;
×
NEW
323
            paramIndex++;
×
324
        }
NEW
325
    }
×
326

327
    private static void PropagateFromReturn(MethodAnalysisContext method)
328
    {
NEW
329
        var returns = method.ControlFlowGraph!.Instructions.Where(i => i.OpCode == OpCode.Return);
×
330

NEW
331
        foreach (var instruction in returns)
×
332
        {
NEW
333
            if (instruction.Operands.Count == 1 && instruction.Operands[0] is LocalVariable local)
×
NEW
334
                local.Type = method.ReturnType;
×
335
        }
NEW
336
    }
×
337
}
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