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

SamboyCoding / Cpp2IL / 15051179934

15 May 2025 05:16PM UTC coverage: 34.039% (-0.4%) from 34.453%
15051179934

Pull #451

github

web-flow
Merge 59eb1b3f5 into d3bbbb38e
Pull Request #451: Support injecting anything

1774 of 6622 branches covered (26.79%)

Branch coverage included in aggregate %.

147 of 195 new or added lines in 27 files covered. (75.38%)

53 existing lines in 2 files now uncovered.

4155 of 10796 relevant lines covered (38.49%)

188285.05 hits per line

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

0.0
/Cpp2IL.Core/Utils/CsFileUtils.cs
1
using System;
2
using System.Reflection;
3
using System.Text;
4
using Cpp2IL.Core.Logging;
5
using Cpp2IL.Core.Model.Contexts;
6
using LibCpp2IL;
7

8
namespace Cpp2IL.Core.Utils;
9

10
public static class CsFileUtils
11
{
12
    /// <summary>
13
    /// Returns the parameters of the given method as they would likely appear in a C# method signature.
14
    /// That is to say, joined with a comma and a space, and with each parameter expressed as its type, a space, then its name, and optionally a default value if one is set.
15
    /// Note this does not include the method name, the return type, or the parentheses around the parameters.
16
    /// </summary>
17
    /// <param name="method">The method to generate the parameter string for</param>
18
    /// <returns>A properly-formatted parameter string as described above.</returns>
19
    public static string GetMethodParameterString(MethodAnalysisContext method)
20
    {
21
        var sb = new StringBuilder();
×
22
        var first = true;
×
23
        foreach (var paramData in method!.Parameters!)
×
24
        {
25
            if (!first)
×
26
                sb.Append(", ");
×
27

28
            first = false;
×
29

30
            sb.Append(paramData); //ToString on the ParameterData will do the right thing.
×
31
        }
32

33
        return sb.ToString();
×
34
    }
35

36
    /// <summary>
37
    /// Returns all the keywords that would be present in the c# source file to generate this type, i.e. access modifiers, static/sealed/etc, and the type of type (class, enum, interface).
38
    /// Does not include the name of the type.
39
    /// </summary>
40
    /// <param name="type">The type to generate the keywords for</param>
41
    public static string GetKeyWordsForType(TypeAnalysisContext type)
42
    {
43
        var sb = new StringBuilder();
×
44
        var attributes = type.Definition!.Attributes;
×
45

46
        if (attributes.HasFlag(TypeAttributes.NestedPrivate))
×
47
            sb.Append("private ");
×
48
        else if (attributes.HasFlag(TypeAttributes.Public))
×
49
            sb.Append("public ");
×
50
        else
51
            sb.Append("internal "); //private top-level classes don't exist, for obvious reasons
×
52

53
        if (type.IsEnumType)
×
54
            sb.Append("enum ");
×
55
        else if (type.IsValueType)
×
56
            sb.Append("struct ");
×
57
        else if (attributes.HasFlag(TypeAttributes.Interface))
×
58
            sb.Append("interface ");
×
59
        else
60
        {
61
            if (attributes.HasFlag(TypeAttributes.Abstract) && attributes.HasFlag(TypeAttributes.Sealed))
×
62
                //Abstract Sealed => Static
63
                sb.Append("static ");
×
64
            else if (attributes.HasFlag(TypeAttributes.Abstract))
×
65
                sb.Append("abstract ");
×
66
            else if (attributes.HasFlag(TypeAttributes.Sealed))
×
67
                sb.Append("sealed ");
×
68

69
            sb.Append("class ");
×
70
        }
71

72
        return sb.ToString().Trim();
×
73
    }
74

75
    /// <summary>
76
    /// Returns all the keywords that would be present in the c# source file to generate this method, i.e. access modifiers, static/const/etc.
77
    /// Does not include the type of the field or its name.
78
    /// </summary>
79
    /// <param name="field">The field to generate keywords for</param>
80
    public static string GetKeyWordsForField(FieldAnalysisContext field)
81
    {
82
        var sb = new StringBuilder();
×
83
        var attributes = field.BackingData!.Attributes;
×
84

85
        if (attributes.HasFlag(FieldAttributes.Public))
×
86
            sb.Append("public ");
×
87
        else if (attributes.HasFlag(FieldAttributes.Family))
×
88
            sb.Append("protected ");
×
89
        if (attributes.HasFlag(FieldAttributes.Assembly))
×
90
            sb.Append("internal ");
×
91
        else if (attributes.HasFlag(FieldAttributes.Private))
×
92
            sb.Append("private ");
×
93

94
        if (attributes.HasFlag(FieldAttributes.Literal))
×
95
            sb.Append("const ");
×
96
        else
97
        {
98
            if (attributes.HasFlag(FieldAttributes.Static))
×
99
                sb.Append("static ");
×
100

101
            if (attributes.HasFlag(FieldAttributes.InitOnly))
×
102
                sb.Append("readonly ");
×
103
        }
104

105
        return sb.ToString().Trim();
×
106
    }
107

108
    /// <summary>
109
    /// Returns all the keywords that would be present in the c# source file to generate this method, i.e. access modifiers, static/abstract/etc.
110
    /// Does not include the return type, name, or parameters.
111
    /// </summary>
112
    /// <param name="method">The method to generate keywords for</param>
113
    /// <param name="skipSlotRelated">Skip slot-related modifiers like abstract, virtual, override</param>
114
    /// <param name="skipKeywordsInvalidForAccessors">Skip the public and static keywords, as those aren't valid for property accessors</param>
115
    public static string GetKeyWordsForMethod(MethodAnalysisContext method, bool skipSlotRelated = false, bool skipKeywordsInvalidForAccessors = false)
116
    {
117
        var sb = new StringBuilder();
×
118
        var attributes = method.Definition!.Attributes;
×
119

120
        if (!skipKeywordsInvalidForAccessors)
×
121
        {
122
            if (attributes.HasFlag(MethodAttributes.Public))
×
123
                sb.Append("public ");
×
124
            else if (attributes.HasFlag(MethodAttributes.Family))
×
125
                sb.Append("protected ");
×
126
        }
127

128
        if (attributes.HasFlag(MethodAttributes.Assembly))
×
129
            sb.Append("internal ");
×
130
        else if (attributes.HasFlag(MethodAttributes.Private))
×
131
            sb.Append("private ");
×
132

133
        if (!skipKeywordsInvalidForAccessors && attributes.HasFlag(MethodAttributes.Static))
×
134
            sb.Append("static ");
×
135

136
        if (method.DeclaringType!.Definition!.Attributes.HasFlag(TypeAttributes.Interface) || skipSlotRelated)
×
137
        {
138
            //Deliberate no-op to avoid unnecessarily marking interface methods as abstract
139
        }
140
        else if (attributes.HasFlag(MethodAttributes.Abstract))
×
141
            sb.Append("abstract ");
×
142
        else if (attributes.HasFlag(MethodAttributes.NewSlot))
×
143
            sb.Append("override ");
×
144
        else if (attributes.HasFlag(MethodAttributes.Virtual))
×
145
            sb.Append("virtual ");
×
146

147

148
        return sb.ToString().Trim();
×
149
    }
150

151
    /// <summary>
152
    /// Returns all the keywords that would be present in the c# source file to generate this event, i.e. access modifiers, static/abstract/etc.
153
    /// Does not include the event type or name
154
    /// </summary>
155
    /// <param name="evt">The event to generate keywords for</param>
156
    public static string GetKeyWordsForEvent(EventAnalysisContext evt)
157
    {
158
        var sb = new StringBuilder();
×
159

160
        var addAttrs = evt.Adder?.Attributes ?? 0;
×
161
        var removeAttrs = evt.Remover?.Attributes ?? 0;
×
162
        var raiseAttrs = evt.Invoker?.Attributes ?? 0;
×
163

164
        var all = addAttrs | removeAttrs | raiseAttrs;
×
165

166
        //Accessibility must be that of the most accessible method
167
        if (addAttrs.HasFlag(MethodAttributes.Public) || removeAttrs.HasFlag(MethodAttributes.Public) || raiseAttrs.HasFlag(MethodAttributes.Public))
×
168
            sb.Append("public ");
×
169
        else if (all.HasFlag(MethodAttributes.Family)) //Family is only one bit so we can use the OR'd attributes
×
170
            sb.Append("protected ");
×
171
        if (addAttrs.HasFlag(MethodAttributes.Assembly) || removeAttrs.HasFlag(MethodAttributes.Assembly) || raiseAttrs.HasFlag(MethodAttributes.Assembly))
×
172
            sb.Append("internal ");
×
173
        else if (all.HasFlag(MethodAttributes.Private))
×
174
            sb.Append("private ");
×
175

176
        if (all.HasFlag(MethodAttributes.Static))
×
177
            sb.Append("static ");
×
178

179
        if (evt.DeclaringType!.Definition!.Attributes.HasFlag(TypeAttributes.Interface))
×
180
        {
181
            //Deliberate no-op to avoid unnecessarily marking interface methods as abstract
182
        }
183
        else if (all.HasFlag(MethodAttributes.Abstract))
×
184
            sb.Append("abstract ");
×
185
        else if (all.HasFlag(MethodAttributes.NewSlot))
×
186
            sb.Append("override ");
×
187
        else if (all.HasFlag(MethodAttributes.Virtual))
×
188
            sb.Append("virtual ");
×
189

190
        sb.Append("event ");
×
191

192
        return sb.ToString().Trim();
×
193
    }
194

195
    /// <summary>
196
    /// Returns all the keywords that would be present in the c# source file to generate this event, i.e. access modifiers, static/abstract/etc.
197
    /// Does not include the event type or name
198
    /// </summary>
199
    /// <param name="prop">The event to generate keywords for</param>
200
    public static string GetKeyWordsForProperty(PropertyAnalysisContext prop)
201
    {
202
        var sb = new StringBuilder();
×
203

204
        var getterAttributes = prop.Getter?.Attributes ?? 0;
×
205
        var setterAttributes = prop.Setter?.Attributes ?? 0;
×
206

207
        var all = getterAttributes | setterAttributes;
×
208

209
        //Accessibility must be that of the most accessible method
210
        if (getterAttributes.HasFlag(MethodAttributes.Public) || setterAttributes.HasFlag(MethodAttributes.Public))
×
211
            sb.Append("public ");
×
212
        else if (all.HasFlag(MethodAttributes.Family)) //Family is only one bit so we can use the OR'd attributes
×
213
            sb.Append("protected ");
×
214
        if (getterAttributes.HasFlag(MethodAttributes.Assembly) || setterAttributes.HasFlag(MethodAttributes.Assembly))
×
215
            sb.Append("internal ");
×
216
        else if (all.HasFlag(MethodAttributes.Private))
×
217
            sb.Append("private ");
×
218

219
        if (all.HasFlag(MethodAttributes.Static))
×
220
            sb.Append("static ");
×
221

222
        if (prop.DeclaringType!.Definition!.Attributes.HasFlag(TypeAttributes.Interface))
×
223
        {
224
            //Deliberate no-op to avoid unnecessarily marking interface methods as abstract
225
        }
226
        else if (all.HasFlag(MethodAttributes.Abstract))
×
227
            sb.Append("abstract ");
×
228
        else if (all.HasFlag(MethodAttributes.NewSlot))
×
229
            sb.Append("override ");
×
230
        else if (all.HasFlag(MethodAttributes.Virtual))
×
231
            sb.Append("virtual ");
×
232

233
        return sb.ToString().Trim();
×
234
    }
235

236
    /// <summary>
237
    /// Returns all the custom attributes for the given entity, as they would appear in a C# source file (i.e. properly wrapped in square brackets, with params if known)
238
    /// </summary>
239
    /// <param name="context">The entity to generate custom attribute strings for</param>
240
    /// <param name="indentCount">The number of tab characters to emit at the start of each line</param>
241
    /// <param name="analyze">True to call <see cref="HasCustomAttributes.AnalyzeCustomAttributeData"/> before generating.</param>
242
    /// <param name="includeIncomplete">True to emit custom attributes even if they have required parameters that aren't known</param>
243
    public static string GetCustomAttributeStrings(HasCustomAttributes context, int indentCount, bool analyze = true, bool includeIncomplete = true)
244
    {
245
        var sb = new StringBuilder();
×
246

247
        if (analyze)
×
248
            context.AnalyzeCustomAttributeData();
×
249

250
        //Sort alphabetically by type name
251
        context.CustomAttributes!.SortByExtractedKey(a => a.Constructor.DeclaringType!.Name);
×
252

253
        foreach (var analyzedCustomAttribute in context.CustomAttributes!)
×
254
        {
255
            if (!includeIncomplete && !analyzedCustomAttribute.IsSuitableForEmission)
×
256
                continue;
257

258
            if (indentCount > 0)
×
259
                sb.Append('\t', indentCount);
×
260

261
            try
262
            {
263
                sb.AppendLine(analyzedCustomAttribute.ToString());
×
264
            }
×
265
            catch (Exception e)
×
266
            {
267
                Logger.WarnNewline("Exception printing/formatting custom attribute: " + e, "C# Generator");
×
268
                sb.Append("/*Cpp2IL: Exception outputting custom attribute of type ").Append(analyzedCustomAttribute.Constructor.DeclaringType?.Name ?? "<unknown type?>").AppendLine("*/");
×
269
            }
×
270
        }
271

272
        return sb.ToString();
×
273
    }
274

275
    /// <summary>
276
    /// Returns the name of the given type, as it would appear in a C# source file.
277
    /// This mainly involves stripping the backtick section from generic type names, and replacing certain system types with their primitive name.
278
    /// </summary>
279
    /// <param name="originalName">The original name of the type</param>
280
    public static string GetTypeName(string originalName)
281
    {
282
        if (originalName.Contains("`"))
×
283
            //Generics - remove `1 etc
284
            return originalName.Remove(originalName.IndexOf('`'), 2);
×
285

286
        if (originalName[^1] == '&')
×
287
            originalName = originalName[..^1]; //Remove trailing & for ref params
×
288

289
        return originalName switch
×
290
        {
×
291
            "Void" => "void",
×
292
            "Boolean" => "bool",
×
293
            "Byte" => "byte",
×
294
            "SByte" => "sbyte",
×
295
            "Char" => "char",
×
296
            "Decimal" => "decimal",
×
297
            "Single" => "float",
×
298
            "Double" => "double",
×
299
            "Int32" => "int",
×
300
            "UInt32" => "uint",
×
301
            "Int64" => "long",
×
302
            "UInt64" => "ulong",
×
303
            "Int16" => "short",
×
304
            "UInt16" => "ushort",
×
305
            "String" => "string",
×
306
            "Object" => "object",
×
307
            _ => originalName
×
308
        };
×
309
    }
310

311
    /// <summary>
312
    /// Appends inheritance data (base class and interfaces) for the given type to the given string builder.
313
    /// If the base class is System.Object, System.ValueType, or System.Enum, it will be ignored
314
    /// </summary>
315
    /// <param name="type"></param>
316
    /// <param name="sb"></param>
317
    public static void AppendInheritanceInfo(TypeAnalysisContext type, StringBuilder sb)
318
    {
319
        var baseType = type.BaseType;
×
320
        var needsBaseClass = baseType is { FullName: not "System.Object" and not "System.ValueType" and not "System.Enum" };
×
321
        if (needsBaseClass)
×
322
            sb.Append(" : ").Append(GetTypeName(baseType!.Name));
×
323

324
        //Interfaces
NEW
325
        if (type.InterfaceContexts.Count <= 0)
×
326
            return;
×
327

328
        if (!needsBaseClass)
×
329
            sb.Append(" : ");
×
330

331
        var addComma = needsBaseClass;
×
332
        foreach (var iface in type.InterfaceContexts)
×
333
        {
334
            if (addComma)
×
335
                sb.Append(", ");
×
336

337
            addComma = true;
×
338

339
            sb.Append(GetTypeName(iface.Name));
×
340
        }
341
    }
×
342
}
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