File size: 5,682 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Mono.Reflection;

namespace UnityEditor.TestTools.CodeCoverage.OpenCover
{
    internal static class CyclomaticComplexity
    {
        private static List<Instruction> targets = new List<Instruction>();

        public static int CalculateCyclomaticComplexity(this MethodBase method)
        {
            if (method == null || method.GetMethodBody() == null)
            {
                return 1;
            }

            bool hasSwitch = false;
            foreach (Instruction ins in method.GetInstructions())
            {
                if (ins.OpCode.OperandType == OperandType.InlineSwitch)
                {
                    hasSwitch = true;
                    break;
                }
            }

            if (hasSwitch)
            {
                return GetSwitchCyclomaticComplexity(method);
            }
            return GetFastCyclomaticComplexity(method);
        }

        private static int GetFastCyclomaticComplexity(MethodBase method)
        {
            int cc = 1;
            foreach (Instruction ins in method.GetInstructions())
            {
                switch (ins.OpCode.FlowControl)
                {
                    case FlowControl.Branch:
                        // detect ternary pattern
                        Instruction previous = ins.Previous;
                        if (previous != null && previous.OpCode.Name.StartsWith("ld"))
                        {
                            ++cc;
                        }
                        break;

                    case FlowControl.Cond_Branch:
                        ++cc;
                        break;
                }
            }

            return cc;
        }

        private static int GetSwitchCyclomaticComplexity(MethodBase method)
        {
            Instruction previous = null;
            Instruction branch = null;
            int cc = 1;

            foreach (Instruction ins in method.GetInstructions())
            {
                switch (ins.OpCode.FlowControl)
                {
                    case FlowControl.Branch:
                        if (previous == null)
                        {
                            continue;
                        }

                        // detect ternary pattern
                        previous = ins.Previous;
                        if (previous.OpCode.Name.StartsWith("ld"))
                        {
                            cc++;
                        }

                        // or 'default' (xmcs)
                        if (previous.OpCode.FlowControl == FlowControl.Cond_Branch)
                        {
                            branch = (previous.Operand as Instruction);
                            // branch can be null (e.g. switch -> Instruction[])
                            if ((branch != null) && targets.Contains(branch) && !targets.Contains(ins))
                            {
                                targets.Add(ins);
                            }
                        }
                        break;

                    case FlowControl.Cond_Branch:
                        // note: a single switch (C#) with sparse values can be broken into several swicth (IL)
                        // that will use the same 'targets' and must be counted only once
                        if (ins.OpCode.OperandType == OperandType.InlineSwitch)
                        {
                            AccumulateSwitchTargets(ins);
                        }
                        else
                        {
                            // some conditional branch can be related to the sparse switch
                            branch = (ins.Operand as Instruction);
                            previous = branch.Previous;
                            if ((previous != null) && previous.Previous.OpCode.OperandType != OperandType.InlineSwitch)
                            {
                                if (!targets.Contains(branch))
                                {
                                    cc++;
                                }
                            }
                        }
                        break;
                }
            }
            // count all unique targets (and default if more than one C# switch is used)
            cc += targets.Count;
            targets.Clear();

            return cc;
        }

        private static void AccumulateSwitchTargets(Instruction ins)
        {
            Instruction[] cases = (Instruction[])ins.Operand;
            foreach (Instruction target in cases)
            {
                // ignore targets that are the next instructions (xmcs)
                if (target != ins.Next && !targets.Contains(target))
                    targets.Add(target);
            }
            // add 'default' branch (if one exists)
            Instruction next = ins.Next;
            if (next.OpCode.FlowControl == FlowControl.Branch)
            {
                Instruction unc = FindFirstUnconditionalBranchTarget(cases[0]);
                if (unc != next.Operand && !targets.Contains(next.Operand as Instruction))
                    targets.Add(next.Operand as Instruction);
            }
        }

        private static Instruction FindFirstUnconditionalBranchTarget(Instruction ins)
        {
            while (ins != null)
            {
                if (FlowControl.Branch == ins.OpCode.FlowControl)
                    return ((Instruction)ins.Operand);

                ins = ins.Next;
            }
            return null;
        }
    }
}