1
1
using System ;
2
+ using System . Diagnostics ;
2
3
using System . Diagnostics . CodeAnalysis ;
3
4
using System . IO ;
4
5
using System . Linq ;
7
8
using Coverlet . Core . Attributes ;
8
9
using Coverlet . Core . Helpers ;
9
10
using Coverlet . Core . Symbols ;
10
- using Coverlet . Tracker ;
11
11
12
12
using Mono . Cecil ;
13
13
using Mono . Cecil . Cil ;
@@ -22,8 +22,12 @@ internal class Instrumenter
22
22
private readonly string [ ] _excludeFilters ;
23
23
private readonly string [ ] _includeFilters ;
24
24
private readonly string [ ] _excludedFiles ;
25
- private readonly static Lazy < MethodInfo > _markExecutedMethodLoader = new Lazy < MethodInfo > ( GetMarkExecutedMethod ) ;
26
25
private InstrumenterResult _result ;
26
+ private FieldDefinition _customTrackerHitsArray ;
27
+ private FieldDefinition _customTrackerHitsFilePath ;
28
+ private ILProcessor _customTrackerClassConstructorIl ;
29
+ private TypeDefinition _customTrackerTypeDef ;
30
+ private MethodReference _customTrackerRecordHitMethod ;
27
31
28
32
public Instrumenter ( string module , string identifier , string [ ] excludeFilters , string [ ] includeFilters , string [ ] excludedFiles )
29
33
{
@@ -51,7 +55,6 @@ public InstrumenterResult Instrument()
51
55
} ;
52
56
53
57
InstrumentModule ( ) ;
54
- InstrumentationHelper . CopyCoverletDependency ( _module ) ;
55
58
56
59
return _result ;
57
60
}
@@ -67,20 +70,119 @@ private void InstrumentModule()
67
70
using ( var module = ModuleDefinition . ReadModule ( stream , parameters ) )
68
71
{
69
72
var types = module . GetTypes ( ) ;
73
+ AddCustomModuleTrackerToModule ( module ) ;
74
+
70
75
foreach ( TypeDefinition type in types )
71
76
{
72
77
var actualType = type . DeclaringType ?? type ;
73
78
if ( ! actualType . CustomAttributes . Any ( IsExcludeAttribute )
79
+ && actualType . Namespace != "Coverlet.Core.Instrumentation.Tracker"
74
80
&& ! InstrumentationHelper . IsTypeExcluded ( _module , actualType . FullName , _excludeFilters )
75
81
&& InstrumentationHelper . IsTypeIncluded ( _module , actualType . FullName , _includeFilters ) )
76
82
InstrumentType ( type ) ;
77
83
}
78
84
85
+ // Fixup the custom tracker class constructor, according to all instrumented types
86
+ Instruction lastInstr = _customTrackerClassConstructorIl . Body . Instructions . Last ( ) ;
87
+ _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldc_I4 , _result . HitCandidates . Count ) ) ;
88
+ _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Newarr , module . TypeSystem . Int32 ) ) ;
89
+ _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Stsfld , _customTrackerHitsArray ) ) ;
90
+ _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Ldstr , _result . HitsFilePath ) ) ;
91
+ _customTrackerClassConstructorIl . InsertBefore ( lastInstr , Instruction . Create ( OpCodes . Stsfld , _customTrackerHitsFilePath ) ) ;
92
+
79
93
module . Write ( stream ) ;
80
94
}
81
95
}
82
96
}
83
97
98
+ private void AddCustomModuleTrackerToModule ( ModuleDefinition module )
99
+ {
100
+ using ( AssemblyDefinition coverletInstrumentationAssembly = AssemblyDefinition . ReadAssembly ( typeof ( Instrumenter ) . Assembly . Location ) )
101
+ {
102
+ TypeDefinition moduleTrackerTemplate = coverletInstrumentationAssembly . MainModule . GetType (
103
+ "Coverlet.Core.Instrumentation" , nameof ( ModuleTrackerTemplate ) ) ;
104
+
105
+ _customTrackerTypeDef = new TypeDefinition (
106
+ "Coverlet.Core.Instrumentation.Tracker" , Path . GetFileNameWithoutExtension ( module . Name ) + "_" + _identifier , moduleTrackerTemplate . Attributes ) ;
107
+
108
+ _customTrackerTypeDef . BaseType = module . TypeSystem . Object ;
109
+ foreach ( FieldDefinition fieldDef in moduleTrackerTemplate . Fields )
110
+ {
111
+ var fieldClone = new FieldDefinition ( fieldDef . Name , fieldDef . Attributes , fieldDef . FieldType ) ;
112
+ fieldClone . FieldType = module . ImportReference ( fieldClone . FieldType ) ;
113
+
114
+ _customTrackerTypeDef . Fields . Add ( fieldClone ) ;
115
+
116
+ if ( fieldClone . Name == "HitsArray" )
117
+ _customTrackerHitsArray = fieldClone ;
118
+ else if ( fieldClone . Name == "HitsFilePath" )
119
+ _customTrackerHitsFilePath = fieldClone ;
120
+ }
121
+
122
+ foreach ( MethodDefinition methodDef in moduleTrackerTemplate . Methods )
123
+ {
124
+ MethodDefinition methodOnCustomType = new MethodDefinition ( methodDef . Name , methodDef . Attributes , methodDef . ReturnType ) ;
125
+
126
+ if ( methodDef . Name == "RecordHit" )
127
+ {
128
+ foreach ( var parameter in methodDef . Parameters )
129
+ {
130
+ methodOnCustomType . Parameters . Add ( new ParameterDefinition ( module . ImportReference ( parameter . ParameterType ) ) ) ;
131
+ }
132
+ }
133
+
134
+ foreach ( var variable in methodDef . Body . Variables )
135
+ {
136
+ methodOnCustomType . Body . Variables . Add ( new VariableDefinition ( module . ImportReference ( variable . VariableType ) ) ) ;
137
+ }
138
+
139
+ methodOnCustomType . Body . InitLocals = methodDef . Body . InitLocals ;
140
+
141
+ ILProcessor ilProcessor = methodOnCustomType . Body . GetILProcessor ( ) ;
142
+ if ( methodDef . Name == ".cctor" )
143
+ _customTrackerClassConstructorIl = ilProcessor ;
144
+
145
+ foreach ( Instruction instr in methodDef . Body . Instructions )
146
+ {
147
+ if ( instr . Operand is MethodReference methodReference )
148
+ {
149
+ if ( ! methodReference . FullName . Contains ( moduleTrackerTemplate . Namespace ) )
150
+ {
151
+ // External method references, just import then
152
+ instr . Operand = module . ImportReference ( methodReference ) ;
153
+ }
154
+ else
155
+ {
156
+ // Move to the custom type
157
+ instr . Operand = new MethodReference (
158
+ methodReference . Name , methodReference . ReturnType , _customTrackerTypeDef ) ;
159
+ }
160
+ }
161
+ else if ( instr . Operand is FieldReference fieldReference )
162
+ {
163
+ instr . Operand = _customTrackerTypeDef . Fields . Single ( fd => fd . Name == fieldReference . Name ) ;
164
+ }
165
+ else if ( instr . Operand is TypeReference typeReference )
166
+ {
167
+ instr . Operand = module . ImportReference ( typeReference ) ;
168
+ }
169
+
170
+ ilProcessor . Append ( instr ) ;
171
+ }
172
+
173
+ foreach ( var handler in methodDef . Body . ExceptionHandlers )
174
+ methodOnCustomType . Body . ExceptionHandlers . Add ( handler ) ;
175
+
176
+ _customTrackerTypeDef . Methods . Add ( methodOnCustomType ) ;
177
+ }
178
+
179
+ module . Types . Add ( _customTrackerTypeDef ) ;
180
+ }
181
+
182
+ Debug . Assert ( _customTrackerHitsArray != null ) ;
183
+ Debug . Assert ( _customTrackerClassConstructorIl != null ) ;
184
+ }
185
+
84
186
private void InstrumentType ( TypeDefinition type )
85
187
{
86
188
var methods = type . GetMethods ( ) ;
@@ -143,7 +245,7 @@ private void InstrumentIL(MethodDefinition method)
143
245
foreach ( ExceptionHandler handler in processor . Body . ExceptionHandlers )
144
246
ReplaceExceptionHandlerBoundary ( handler , instruction , target ) ;
145
247
146
- index += 3 ;
248
+ index += 2 ;
147
249
}
148
250
149
251
foreach ( var _branchTarget in targetedBranchPoints )
@@ -164,7 +266,7 @@ private void InstrumentIL(MethodDefinition method)
164
266
foreach ( ExceptionHandler handler in processor . Body . ExceptionHandlers )
165
267
ReplaceExceptionHandlerBoundary ( handler , instruction , target ) ;
166
268
167
- index += 3 ;
269
+ index += 2 ;
168
270
}
169
271
170
272
index ++ ;
@@ -188,17 +290,10 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
188
290
document . Lines . Add ( i , new Line { Number = i , Class = method . DeclaringType . FullName , Method = method . FullName } ) ;
189
291
}
190
292
191
- string marker = $ "L,{ document . Index } ,{ sequencePoint . StartLine } ,{ sequencePoint . EndLine } ";
293
+ var entry = ( false , document . Index , sequencePoint . StartLine , sequencePoint . EndLine ) ;
294
+ _result . HitCandidates . Add ( entry ) ;
192
295
193
- var pathInstr = Instruction . Create ( OpCodes . Ldstr , _result . HitsFilePath ) ;
194
- var markInstr = Instruction . Create ( OpCodes . Ldstr , marker ) ;
195
- var callInstr = Instruction . Create ( OpCodes . Call , processor . Body . Method . Module . ImportReference ( _markExecutedMethodLoader . Value ) ) ;
196
-
197
- processor . InsertBefore ( instruction , callInstr ) ;
198
- processor . InsertBefore ( callInstr , markInstr ) ;
199
- processor . InsertBefore ( markInstr , pathInstr ) ;
200
-
201
- return pathInstr ;
296
+ return AddInstrumentationInstructions ( method , processor , instruction , _result . HitCandidates . Count - 1 ) ;
202
297
}
203
298
204
299
private Instruction AddInstrumentationCode ( MethodDefinition method , ILProcessor processor , Instruction instruction , BranchPoint branchPoint )
@@ -225,17 +320,28 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
225
320
}
226
321
) ;
227
322
228
- string marker = $ "B,{ document . Index } ,{ branchPoint . StartLine } ,{ branchPoint . Ordinal } ";
323
+ var entry = ( true , document . Index , branchPoint . StartLine , ( int ) branchPoint . Ordinal ) ;
324
+ _result . HitCandidates . Add ( entry ) ;
325
+
326
+ return AddInstrumentationInstructions ( method , processor , instruction , _result . HitCandidates . Count - 1 ) ;
327
+ }
328
+
329
+ private Instruction AddInstrumentationInstructions ( MethodDefinition method , ILProcessor processor , Instruction instruction , int hitEntryIndex )
330
+ {
331
+ if ( _customTrackerRecordHitMethod == null )
332
+ {
333
+ _customTrackerRecordHitMethod = new MethodReference (
334
+ "RecordHit" , method . Module . TypeSystem . Void , _customTrackerTypeDef ) ;
335
+ _customTrackerRecordHitMethod . Parameters . Add ( new ParameterDefinition ( method . Module . TypeSystem . Int32 ) ) ;
336
+ }
229
337
230
- var pathInstr = Instruction . Create ( OpCodes . Ldstr , _result . HitsFilePath ) ;
231
- var markInstr = Instruction . Create ( OpCodes . Ldstr , marker ) ;
232
- var callInstr = Instruction . Create ( OpCodes . Call , processor . Body . Method . Module . ImportReference ( _markExecutedMethodLoader . Value ) ) ;
338
+ var indxInstr = Instruction . Create ( OpCodes . Ldc_I4 , hitEntryIndex ) ;
339
+ var callInstr = Instruction . Create ( OpCodes . Call , _customTrackerRecordHitMethod ) ;
233
340
234
341
processor . InsertBefore ( instruction , callInstr ) ;
235
- processor . InsertBefore ( callInstr , markInstr ) ;
236
- processor . InsertBefore ( markInstr , pathInstr ) ;
342
+ processor . InsertBefore ( callInstr , indxInstr ) ;
237
343
238
- return pathInstr ;
344
+ return indxInstr ;
239
345
}
240
346
241
347
private static void ReplaceInstructionTarget ( Instruction instruction , Instruction oldTarget , Instruction newTarget )
@@ -301,10 +407,5 @@ private static Mono.Cecil.Cil.MethodBody GetMethodBody(MethodDefinition method)
301
407
return null ;
302
408
}
303
409
}
304
-
305
- private static MethodInfo GetMarkExecutedMethod ( )
306
- {
307
- return typeof ( CoverageTracker ) . GetMethod ( nameof ( CoverageTracker . MarkExecuted ) ) ;
308
- }
309
410
}
310
411
}
0 commit comments