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

hazendaz / jmockit1 / 496

15 Nov 2025 05:33PM UTC coverage: 72.192% (-0.008%) from 72.2%
496

push

github

web-flow
Merge pull request #412 from hazendaz/renovate/major-spring-core

Update spring core to v7 (major)

5677 of 8360 branches covered (67.91%)

Branch coverage included in aggregate %.

11922 of 16018 relevant lines covered (74.43%)

0.74 hits per line

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

0.0
/main/src/main/java/mockit/coverage/modification/MethodModifier.java
1
/*
2
 * MIT License
3
 * Copyright (c) 2006-2025 JMockit developers
4
 * See LICENSE file for full license text.
5
 */
6
package mockit.coverage.modification;
7

8
import static mockit.asm.jvmConstants.Opcodes.ACONST_NULL;
9
import static mockit.asm.jvmConstants.Opcodes.DCONST_0;
10
import static mockit.asm.jvmConstants.Opcodes.DUP;
11
import static mockit.asm.jvmConstants.Opcodes.DUP2_X1;
12
import static mockit.asm.jvmConstants.Opcodes.DUP_X1;
13
import static mockit.asm.jvmConstants.Opcodes.DUP_X2;
14
import static mockit.asm.jvmConstants.Opcodes.FCONST_0;
15
import static mockit.asm.jvmConstants.Opcodes.GETFIELD;
16
import static mockit.asm.jvmConstants.Opcodes.GETSTATIC;
17
import static mockit.asm.jvmConstants.Opcodes.GOTO;
18
import static mockit.asm.jvmConstants.Opcodes.ICONST_0;
19
import static mockit.asm.jvmConstants.Opcodes.INVOKESPECIAL;
20
import static mockit.asm.jvmConstants.Opcodes.INVOKESTATIC;
21
import static mockit.asm.jvmConstants.Opcodes.INVOKEVIRTUAL;
22
import static mockit.asm.jvmConstants.Opcodes.IRETURN;
23
import static mockit.asm.jvmConstants.Opcodes.LCONST_0;
24
import static mockit.asm.jvmConstants.Opcodes.POP;
25
import static mockit.asm.jvmConstants.Opcodes.POP2;
26
import static mockit.asm.jvmConstants.Opcodes.PUTSTATIC;
27
import static mockit.asm.jvmConstants.Opcodes.RETURN;
28
import static mockit.asm.jvmConstants.Opcodes.SIPUSH;
29

30
import edu.umd.cs.findbugs.annotations.NonNull;
31
import edu.umd.cs.findbugs.annotations.Nullable;
32

33
import mockit.asm.annotations.AnnotationVisitor;
34
import mockit.asm.controlFlow.Label;
35
import mockit.asm.methods.MethodWriter;
36
import mockit.asm.methods.WrappingMethodVisitor;
37
import mockit.coverage.data.FileCoverageData;
38
import mockit.coverage.lines.PerFileLineCoverage;
39

40
import org.checkerframework.checker.index.qual.NonNegative;
41

42
final class MethodModifier extends WrappingMethodVisitor {
×
43
    private static final String DATA_RECORDING_CLASS = "mockit/coverage/TestRun";
44

45
    @NonNull
46
    private final String sourceFileName;
47
    @NonNull
48
    private final FileCoverageData fileData;
49
    @NonNull
50
    private final PerFileLineCoverage lineCoverageInfo;
51
    @NonNull
52
    private final CFGTracking cfgTracking;
53
    private boolean foundInterestingInstruction;
54
    @NonNegative
55
    int currentLine;
56

57
    MethodModifier(@NonNull MethodWriter mw, @NonNull String sourceFileName, @NonNull FileCoverageData fileData) {
58
        super(mw);
×
59
        this.sourceFileName = sourceFileName;
×
60
        this.fileData = fileData;
×
61
        lineCoverageInfo = fileData.getLineCoverageData();
×
62
        cfgTracking = new CFGTracking(lineCoverageInfo);
×
63
    }
×
64

65
    @Override
66
    public AnnotationVisitor visitAnnotation(@NonNull String desc) {
67
        boolean isTestMethod = desc.startsWith("Lorg/junit/") || desc.startsWith("Lorg/testng/");
×
68

69
        if (isTestMethod) {
×
70
            throw VisitInterruptedException.INSTANCE;
×
71
        }
72

73
        return mw.visitAnnotation(desc);
×
74
    }
75

76
    @Override
77
    public void visitLineNumber(@NonNegative int line, @NonNull Label start) {
78
        lineCoverageInfo.addLine(line);
×
79
        currentLine = line;
×
80
        cfgTracking.startNewLine();
×
81
        generateCallToRegisterLineExecution();
×
82
        mw.visitLineNumber(line, start);
×
83
    }
×
84

85
    private void generateCallToRegisterLineExecution() {
86
        mw.visitIntInsn(SIPUSH, fileData.index);
×
87
        pushCurrentLineOnTheStack();
×
88
        mw.visitMethodInsn(INVOKESTATIC, DATA_RECORDING_CLASS, "lineExecuted", "(II)V", false);
×
89
    }
×
90

91
    private void pushCurrentLineOnTheStack() {
92
        if (currentLine <= Short.MAX_VALUE) {
×
93
            mw.visitIntInsn(SIPUSH, currentLine);
×
94
        } else {
95
            mw.visitLdcInsn(currentLine);
×
96
        }
97
    }
×
98

99
    @Override
100
    public void visitLabel(@NonNull Label label) {
101
        mw.visitLabel(label);
×
102
        cfgTracking.afterNewLabel(currentLine, label);
×
103
    }
×
104

105
    @Override
106
    public void visitJumpInsn(@NonNegative int opcode, @NonNull Label label) {
107
        Label jumpSource = mw.getCurrentBlock();
×
108
        assert jumpSource != null;
×
109

110
        mw.visitJumpInsn(opcode, label);
×
111

112
        if (opcode == GOTO) {
×
113
            cfgTracking.afterGoto();
×
114
        } else {
115
            cfgTracking.afterConditionalJump(this, jumpSource, label);
×
116
        }
117
    }
×
118

119
    private void generateCallToRegisterBranchTargetExecutionIfPending() {
120
        cfgTracking.generateCallToRegisterBranchTargetExecutionIfPending(this);
×
121
    }
×
122

123
    void generateCallToRegisterBranchTargetExecution(@NonNegative int branchIndex) {
124
        mw.visitIntInsn(SIPUSH, fileData.index);
×
125
        pushCurrentLineOnTheStack();
×
126
        mw.visitIntInsn(SIPUSH, branchIndex);
×
127
        mw.visitMethodInsn(INVOKESTATIC, DATA_RECORDING_CLASS, "branchExecuted", "(III)V", false);
×
128
    }
×
129

130
    @Override
131
    public void visitInsn(@NonNegative int opcode) {
132
        boolean isReturn = opcode >= IRETURN && opcode <= RETURN;
×
133

134
        if (!isReturn && !isDefaultReturnValue(opcode)) {
×
135
            foundInterestingInstruction = true;
×
136
        }
137

138
        if (isReturn && !foundInterestingInstruction && cfgTracking.hasOnlyOneLabelBeingVisited()) {
×
139
            lineCoverageInfo.getOrCreateLineData(currentLine).markAsUnreachable();
×
140
        } else {
141
            cfgTracking.beforeNoOperandInstruction(this, opcode);
×
142
        }
143

144
        mw.visitInsn(opcode);
×
145
    }
×
146

147
    private static boolean isDefaultReturnValue(@NonNegative int opcode) {
148
        return opcode == ACONST_NULL || opcode == ICONST_0 || opcode == LCONST_0 || opcode == FCONST_0
×
149
                || opcode == DCONST_0;
150
    }
151

152
    @Override
153
    public void visitIntInsn(@NonNegative int opcode, int operand) {
154
        foundInterestingInstruction = true;
×
155
        generateCallToRegisterBranchTargetExecutionIfPending();
×
156
        mw.visitIntInsn(opcode, operand);
×
157
    }
×
158

159
    @Override
160
    public void visitVarInsn(@NonNegative int opcode, @NonNegative int varIndex) {
161
        generateCallToRegisterBranchTargetExecutionIfPending();
×
162
        mw.visitVarInsn(opcode, varIndex);
×
163
    }
×
164

165
    @Override
166
    public void visitTypeInsn(@NonNegative int opcode, @NonNull String typeDesc) {
167
        generateCallToRegisterBranchTargetExecutionIfPending();
×
168
        mw.visitTypeInsn(opcode, typeDesc);
×
169
    }
×
170

171
    @Override
172
    public void visitFieldInsn(@NonNegative int opcode, @NonNull String owner, @NonNull String name,
173
            @NonNull String desc) {
174
        // TODO: need to also process field instructions inside accessor methods (STATIC + SYNTHETIC, "access$nnn")
175
        boolean getField = opcode == GETSTATIC || opcode == GETFIELD;
×
176
        boolean isStatic = opcode == PUTSTATIC || opcode == GETSTATIC;
×
177
        char fieldType = desc.charAt(0);
×
178
        boolean size2 = fieldType == 'J' || fieldType == 'D';
×
179
        String classAndFieldNames = null;
×
180
        boolean fieldHasData = false;
×
181

182
        if (!owner.startsWith("java/")) {
×
183
            classAndFieldNames = owner.substring(owner.lastIndexOf('/') + 1) + '.' + name;
×
184
            fieldHasData = fileData.dataCoverageInfo.isFieldWithCoverageData(classAndFieldNames);
×
185

186
            if (fieldHasData && !isStatic) {
×
187
                generateCodeToSaveInstanceReferenceOnTheStack(getField, size2);
×
188
            }
189
        }
190

191
        generateCallToRegisterBranchTargetExecutionIfPending();
×
192
        mw.visitFieldInsn(opcode, owner, name, desc);
×
193

194
        if (opcode == GETSTATIC && "$assertionsDisabled".equals(name)) {
×
195
            cfgTracking.registerAssertFoundInCurrentLine();
×
196
        }
197

198
        if (fieldHasData) {
×
199
            generateCallToRegisterFieldCoverage(getField, isStatic, size2, classAndFieldNames);
×
200
        }
201
    }
×
202

203
    private void generateCodeToSaveInstanceReferenceOnTheStack(boolean getField, boolean size2) {
204
        if (getField) {
×
205
            mw.visitInsn(DUP);
×
206
        } else {
207
            if (size2) {
×
208
                mw.visitInsn(DUP2_X1);
×
209
                mw.visitInsn(POP2);
×
210
                mw.visitInsn(DUP_X2);
×
211
                mw.visitInsn(DUP_X2);
×
212
            } else {
213
                mw.visitInsn(DUP_X1);
×
214
                mw.visitInsn(POP);
×
215
                mw.visitInsn(DUP_X1);
×
216
                mw.visitInsn(DUP_X1);
×
217
            }
218
            mw.visitInsn(POP);
×
219
        }
220
    }
×
221

222
    private void generateCallToRegisterFieldCoverage(boolean getField, boolean isStatic, boolean size2,
223
            @NonNull String classAndFieldNames) {
224
        if (!isStatic && getField) {
×
225
            if (size2) {
×
226
                mw.visitInsn(DUP2_X1);
×
227
                mw.visitInsn(POP2);
×
228
            } else {
229
                mw.visitInsn(DUP_X1);
×
230
                mw.visitInsn(POP);
×
231
            }
232
        }
233

234
        mw.visitLdcInsn(sourceFileName);
×
235
        mw.visitLdcInsn(classAndFieldNames);
×
236

237
        String methodToCall = getField ? "fieldRead" : "fieldAssigned";
×
238
        String methodDesc = isStatic ? "(Ljava/lang/String;Ljava/lang/String;)V"
×
239
                : "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V";
×
240

241
        mw.visitMethodInsn(INVOKESTATIC, DATA_RECORDING_CLASS, methodToCall, methodDesc, false);
×
242
    }
×
243

244
    @Override
245
    public void visitMethodInsn(@NonNegative int opcode, @NonNull String owner, @NonNull String name,
246
            @NonNull String desc, boolean itf) {
247
        // This is to ignore bytecode belonging to a static initialization block inserted in a regular line of code by
248
        // the Java
249
        // compiler when the class contains at least one "assert" statement.
250
        // Otherwise, that line of code would always appear as partially covered when running with assertions enabled.
251
        if (opcode == INVOKEVIRTUAL && "java/lang/Class".equals(owner) && "desiredAssertionStatus".equals(name)) {
×
252
            cfgTracking.registerAssertFoundInCurrentLine();
×
253
        }
254

255
        if (opcode != INVOKESPECIAL || !"()V".equals(desc)) {
×
256
            foundInterestingInstruction = true;
×
257
        }
258

259
        generateCallToRegisterBranchTargetExecutionIfPending();
×
260
        mw.visitMethodInsn(opcode, owner, name, desc, itf);
×
261
        cfgTracking.afterMethodInstruction(opcode, owner, name);
×
262
    }
×
263

264
    @Override
265
    public void visitLdcInsn(@NonNull Object cst) {
266
        foundInterestingInstruction = true;
×
267
        generateCallToRegisterBranchTargetExecutionIfPending();
×
268
        mw.visitLdcInsn(cst);
×
269
    }
×
270

271
    @Override
272
    public void visitIincInsn(@NonNegative int varIndex, int increment) {
273
        generateCallToRegisterBranchTargetExecutionIfPending();
×
274
        mw.visitIincInsn(varIndex, increment);
×
275
    }
×
276

277
    @Override
278
    public void visitTryCatchBlock(@NonNull Label start, @NonNull Label end, @NonNull Label handler,
279
            @Nullable String type) {
280
        generateCallToRegisterBranchTargetExecutionIfPending();
×
281
        mw.visitTryCatchBlock(start, end, handler, type);
×
282
    }
×
283

284
    @Override
285
    public void visitLookupSwitchInsn(@NonNull Label dflt, @NonNull int[] keys, @NonNull Label[] labels) {
286
        cfgTracking.beforeLookupSwitchInstruction();
×
287
        generateCallToRegisterBranchTargetExecutionIfPending();
×
288
        mw.visitLookupSwitchInsn(dflt, keys, labels);
×
289
    }
×
290

291
    @Override
292
    public void visitTableSwitchInsn(@NonNegative int min, @NonNegative int max, @NonNull Label dflt,
293
            @NonNull Label... labels) {
294
        generateCallToRegisterBranchTargetExecutionIfPending();
×
295
        mw.visitTableSwitchInsn(min, max, dflt, labels);
×
296
    }
×
297

298
    @Override
299
    public void visitMultiANewArrayInsn(@NonNull String desc, @NonNegative int dims) {
300
        generateCallToRegisterBranchTargetExecutionIfPending();
×
301
        mw.visitMultiANewArrayInsn(desc, dims);
×
302
    }
×
303

304
    @Override
305
    public void visitMaxStack(@NonNegative int maxStack) {
306
        if (maxStack > 1) {
×
307
            lineCoverageInfo.markLineAsReachable(currentLine);
×
308
        }
309

310
        mw.visitMaxStack(maxStack);
×
311
    }
×
312
}
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

© 2026 Coveralls, Inc