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

SamboyCoding / Cpp2IL / 30857607998

03 Aug 2026 10:09PM UTC coverage: 32.946% (-1.4%) from 34.341%
30857607998

push

github

SamboyCoding
Decompiler: Handle arrays of structs, struct return buffers, interface method calls

2940 of 10661 branches covered (27.58%)

Branch coverage included in aggregate %.

20 of 467 new or added lines in 12 files covered. (4.28%)

8 existing lines in 6 files now uncovered.

5375 of 14577 relevant lines covered (36.87%)

151659.48 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 Still x64 specific, other instruction sets won't match the layout and get left alone
NEW
60
        if (!X64CallingConventionResolver.HasRawArgumentLayout(call, invoke.AppContext))
×
UNCOV
61
            return;
×
62

NEW
63
        if (invoke.IsVoid)
×
NEW
64
            call.RemoveOperandAt(1);
×
65

NEW
66
        call.OpCode = invoke.IsVoid ? OpCode.CallVoid : OpCode.Call;
×
NEW
67
        call.SetOperand(0, invoke);
×
68

69
        // the receiver register holds invoke_impl_this rather than the delegate itself
NEW
70
        call.SetOperand(invoke.IsVoid ? 1 : 2, delegateLocal);
×
71

NEW
72
        X64CallingConventionResolver.RemapRawArguments(call, invoke);
×
73
    }
×
74
}
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