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

SamboyCoding / Cpp2IL / 30749432829

02 Aug 2026 01:12PM UTC coverage: 36.382% (+0.2%) from 36.169%
30749432829

push

github

SamboyCoding
Decompiler: Stop having operands be raw `object`-typed

2857 of 8963 branches covered (31.88%)

Branch coverage included in aggregate %.

78 of 217 new or added lines in 31 files covered. (35.94%)

7 existing lines in 3 files now uncovered.

5293 of 13438 relevant lines covered (39.39%)

164513.36 hits per line

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

0.0
/Cpp2IL.Core/Analysis/DelegateInvokeRecovery.cs
1
using System.Collections.Generic;
2
using System.Linq;
3
using Cpp2IL.Core.ISIL;
4
using Cpp2IL.Core.Model.Contexts;
5
using Cpp2IL.Core.Utils;
6

7
namespace Cpp2IL.Core.Analysis;
8

9
//Recovers IndirectCall (call reg) for delegate invoke back to actual calls on Invoke
10
public static class DelegateInvokeRecovery
11
{
12
    public static void Run(MethodAnalysisContext method)
13
    {
14
        var instructions = method.ControlFlowGraph!.Blocks.SelectMany(block => block.Instructions).ToList();
×
15

16
        // Il2CppObject is two pointers (klass, monitor), then method_ptr, then invoke_impl.
17
        var invokeImplOffset = (method.AppContext.Binary.is32Bit ? 4 : 8) * 3;
×
18

19
        foreach (var instruction in instructions)
×
20
        {
21
            if (instruction.OpCode != OpCode.IndirectCall)
×
22
                continue;
23

24
            if (GetInvokeImplLoad(instruction, instructions) is not { } memory)
×
25
                continue;
26

27
            if (memory.Addend != invokeImplOffset || memory.Index != null || memory.Scale != 0)
×
28
                continue;
29

30
            if (memory.Base is not LocalVariable delegateLocal || delegateLocal.Type is not { IsDelegate: true } delegateType)
×
31
                continue;
32

33
            if (delegateType.Methods.FirstOrDefault(m => m.Name == "Invoke") is not { } invoke)
×
34
                continue;
35

36
            RewriteAsInvoke(instruction, delegateLocal, invoke);
×
37
        }
38
    }
×
39

40
    // The address being called, whether it is still a separate load or has been inlined
41
    private static MemoryOperand? GetInvokeImplLoad(Instruction call, List<Instruction> instructions)
42
    {
43
        if (call.Operands.Count == 0)
×
44
            return null;
×
45

46
        if (call.Operands[0] is MemoryOperand folded)
×
47
            return folded;
×
48

49
        if (call.Operands[0] is not LocalVariable target)
×
50
            return null;
×
51

52
        var definition = instructions.FirstOrDefault(i => ReferenceEquals(i.Destination, target));
×
53

54
        return definition is { OpCode: OpCode.Move, Operands: [_, MemoryOperand loaded] } ? loaded : null;
×
55
    }
56

57
    private static void RewriteAsInvoke(Instruction call, LocalVariable delegateLocal, MethodAnalysisContext invoke)
58
    {
59
        //TODO This needs to run for the actual instruction set, not hardcoded x64
60
        var abi = X64CallingConventionResolver.ResolveForManaged(invoke);
×
61

62
        // Operands are [target, returnValue, <argument registers>], and the resolved list is in the same
63
        // register order, so they align from index 2 onwards
64
        if (call.Operands.Count - 2 < abi.Length)
×
65
            return;
×
66
        
67
        // TODO fix this (handle float registers not being present here and so possible DCE'd) and make this not x64 hardcoded
68
        if (abi.Any(operand => operand is Register register && register.Name.StartsWith("xmm")))
×
69
            return;
×
70

NEW
71
        var operands = new List<IOperand> { invoke };
×
72

73
        if (!invoke.IsVoid)
×
74
            operands.Add(call.Operands[1]);
×
75

76
        operands.Add(delegateLocal); // Replaces invoke_impl_this, which isn't the delegate
×
77

78
        for (var i = 1; i < abi.Length; i++)
×
79
            operands.Add(call.Operands[2 + i]);
×
80

81
        call.OpCode = invoke.IsVoid ? OpCode.CallVoid : OpCode.Call;
×
NEW
82
        call.SetOperands(operands);
×
83
    }
×
84
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc