• 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

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

8
import static java.lang.reflect.Modifier.isNative;
9
import static java.lang.reflect.Modifier.isStatic;
10

11
import static mockit.asm.jvmConstants.Opcodes.AASTORE;
12
import static mockit.asm.jvmConstants.Opcodes.ACONST_NULL;
13
import static mockit.asm.jvmConstants.Opcodes.ALOAD;
14
import static mockit.asm.jvmConstants.Opcodes.ANEWARRAY;
15
import static mockit.asm.jvmConstants.Opcodes.DUP;
16
import static mockit.asm.jvmConstants.Opcodes.GETSTATIC;
17
import static mockit.asm.jvmConstants.Opcodes.ICONST_0;
18
import static mockit.asm.jvmConstants.Opcodes.ILOAD;
19
import static mockit.asm.jvmConstants.Opcodes.INVOKEINTERFACE;
20
import static mockit.asm.jvmConstants.Opcodes.INVOKESPECIAL;
21
import static mockit.asm.jvmConstants.Opcodes.INVOKESTATIC;
22
import static mockit.asm.jvmConstants.Opcodes.IRETURN;
23
import static mockit.asm.jvmConstants.Opcodes.NEW;
24
import static mockit.asm.jvmConstants.Opcodes.NEWARRAY;
25
import static mockit.asm.jvmConstants.Opcodes.RETURN;
26
import static mockit.asm.jvmConstants.Opcodes.SIPUSH;
27

28
import edu.umd.cs.findbugs.annotations.NonNull;
29
import edu.umd.cs.findbugs.annotations.Nullable;
30

31
import mockit.asm.annotations.AnnotationVisitor;
32
import mockit.asm.classes.ClassInfo;
33
import mockit.asm.classes.ClassReader;
34
import mockit.asm.classes.ClassWriter;
35
import mockit.asm.classes.WrappingClassVisitor;
36
import mockit.asm.controlFlow.Label;
37
import mockit.asm.jvmConstants.Access;
38
import mockit.asm.jvmConstants.ClassVersion;
39
import mockit.asm.methods.MethodVisitor;
40
import mockit.asm.methods.MethodWriter;
41
import mockit.asm.methods.WrappingMethodVisitor;
42
import mockit.asm.types.ArrayType;
43
import mockit.asm.types.JavaType;
44
import mockit.asm.types.ObjectType;
45
import mockit.asm.types.PrimitiveType;
46
import mockit.asm.types.ReferenceType;
47
import mockit.internal.expectations.ExecutionMode;
48
import mockit.internal.state.TestRun;
49
import mockit.internal.util.ClassLoad;
50
import mockit.internal.util.TypeConversionBytecode;
51

52
import org.checkerframework.checker.index.qual.NonNegative;
53

54
public class BaseClassModifier extends WrappingClassVisitor {
55
    private static final int METHOD_ACCESS_MASK = 0xFFFF - Access.ABSTRACT - Access.NATIVE;
56
    protected static final JavaType VOID_TYPE = ObjectType.create("java/lang/Void");
1✔
57

58
    @NonNull
1✔
59
    protected final MethodVisitor methodAnnotationsVisitor = new MethodVisitor() {
1✔
60
        @Override
61
        public AnnotationVisitor visitAnnotation(@NonNull String desc) {
62
            return mw.visitAnnotation(desc);
×
63
        }
64
    };
65

66
    protected MethodWriter mw;
67
    protected boolean useClassLoadingBridge;
68
    protected String superClassName;
69
    protected String classDesc;
70
    protected int methodAccess;
71
    protected String methodName;
72
    protected String methodDesc;
73

74
    protected BaseClassModifier(@NonNull ClassReader classReader) {
75
        super(new ClassWriter(classReader));
1✔
76
    }
1✔
77

78
    protected final void setUseClassLoadingBridge(@Nullable ClassLoader classLoader) {
79
        useClassLoadingBridge = ClassLoad.isClassLoaderWithNoDirectAccess(classLoader);
1✔
80
    }
1✔
81

82
    @Override
83
    public void visit(int version, int access, @NonNull String name, @NonNull ClassInfo additionalInfo) {
84
        int modifiedVersion = version;
1✔
85
        int originalVersion = version & 0xFFFF;
1✔
86

87
        if (originalVersion < ClassVersion.V5) {
1!
88
            // LDC instructions (see MethodVisitor#visitLdcInsn) are more capable in JVMs with support for class files
89
            // of
90
            // version 49 (Java 5) or newer, so we "upgrade" it to avoid a VerifyError:
91
            modifiedVersion = ClassVersion.V5;
×
92
        }
93

94
        cw.visit(modifiedVersion, access, name, additionalInfo);
1✔
95
        superClassName = additionalInfo.superName;
1✔
96
        classDesc = name;
1✔
97
    }
1✔
98

99
    /**
100
     * Just creates a new MethodWriter which will write out the method bytecode when visited.
101
     * <p>
102
     * Removes any "abstract" or "native" modifiers for the modified version.
103
     */
104
    protected final void startModifiedMethodVersion(int access, @NonNull String name, @NonNull String desc,
105
            @Nullable String signature, @Nullable String[] exceptions) {
106
        mw = cw.visitMethod(access & METHOD_ACCESS_MASK, name, desc, signature, exceptions);
1✔
107
        methodAccess = access;
1✔
108
        methodName = name;
1✔
109
        methodDesc = desc;
1✔
110

111
        if (isNative(access)) {
1✔
112
            TestRun.mockFixture().addRedefinedClassWithNativeMethods(classDesc);
1✔
113
        }
114
    }
1✔
115

116
    public final boolean wasModified() {
117
        return methodName != null;
1✔
118
    }
119

120
    protected final void generateDirectCallToHandler(@NonNull String className, int access, @NonNull String name,
121
            @NonNull String desc, @Nullable String genericSignature) {
122
        generateDirectCallToHandler(className, access, name, desc, genericSignature, ExecutionMode.Regular);
1✔
123
    }
1✔
124

125
    protected final void generateDirectCallToHandler(@NonNull String className, int access, @NonNull String name,
126
            @NonNull String desc, @Nullable String genericSignature, @NonNull ExecutionMode executionMode) {
127
        // First argument: the mock instance, if any.
128
        boolean isStatic = generateCodeToPassThisOrNullIfStaticMethod(access);
1✔
129

130
        // Second argument: method access flags.
131
        mw.visitLdcInsn(access);
1✔
132

133
        // Third argument: class name.
134
        mw.visitLdcInsn(className);
1✔
135

136
        // Fourth argument: method signature.
137
        mw.visitLdcInsn(name + desc);
1✔
138

139
        // Fifth argument: generic signature, or null if none.
140
        generateInstructionToLoadNullableString(genericSignature);
1✔
141

142
        // Sixth argument: indicate regular or special modes of execution.
143
        mw.visitLdcInsn(executionMode.ordinal());
1✔
144

145
        // Seventh argument: array with invocation arguments.
146
        JavaType[] argTypes = JavaType.getArgumentTypes(desc);
1✔
147
        int argCount = argTypes.length;
1✔
148

149
        if (argCount == 0) {
1✔
150
            mw.visitInsn(ACONST_NULL);
1✔
151
        } else {
152
            generateCodeToCreateArrayOfObject(argCount);
1✔
153
            generateCodeToFillArrayWithParameterValues(argTypes, 0, isStatic ? 0 : 1);
1✔
154
        }
155

156
        mw.visitMethodInsn(INVOKESTATIC, "mockit/internal/expectations/RecordAndReplayExecution", "recordOrReplay",
1✔
157
                "(Ljava/lang/Object;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/Object;)Ljava/lang/Object;",
158
                false);
159
    }
1✔
160

161
    private void generateInstructionToLoadNullableString(@Nullable String text) {
162
        if (text == null) {
1✔
163
            mw.visitInsn(ACONST_NULL);
1✔
164
        } else {
165
            mw.visitLdcInsn(text);
1✔
166
        }
167
    }
1✔
168

169
    protected final void generateReturnWithObjectAtTopOfTheStack(@NonNull String mockedMethodDesc) {
170
        JavaType returnType = JavaType.getReturnType(mockedMethodDesc);
1✔
171
        TypeConversionBytecode.generateCastFromObject(mw, returnType);
1✔
172
        mw.visitInsn(returnType.getOpcode(IRETURN));
1✔
173
    }
1✔
174

175
    protected final boolean generateCodeToPassThisOrNullIfStaticMethod() {
176
        return generateCodeToPassThisOrNullIfStaticMethod(methodAccess);
1✔
177
    }
178

179
    private boolean generateCodeToPassThisOrNullIfStaticMethod(int access) {
180
        boolean isStatic = isStatic(access);
1✔
181

182
        if (isStatic) {
1✔
183
            mw.visitInsn(ACONST_NULL);
1✔
184
        } else {
185
            mw.visitVarInsn(ALOAD, 0);
1✔
186
        }
187

188
        return isStatic;
1✔
189
    }
190

191
    protected final void generateCodeToCreateArrayOfObject(@NonNegative int arrayLength) {
192
        mw.visitIntInsn(SIPUSH, arrayLength);
1✔
193
        mw.visitTypeInsn(ANEWARRAY, "java/lang/Object");
1✔
194
    }
1✔
195

196
    protected final void generateCodeToFillArrayWithParameterValues(@NonNull JavaType[] parameterTypes,
197
            @NonNegative int initialArrayIndex, @NonNegative int initialParameterIndex) {
198
        int i = initialArrayIndex;
1✔
199
        int j = initialParameterIndex;
1✔
200

201
        for (JavaType parameterType : parameterTypes) {
1✔
202
            mw.visitInsn(DUP);
1✔
203
            mw.visitIntInsn(SIPUSH, i);
1✔
204
            i++;
1✔
205
            mw.visitVarInsn(parameterType.getOpcode(ILOAD), j);
1✔
206
            TypeConversionBytecode.generateCastToObject(mw, parameterType);
1✔
207
            mw.visitInsn(AASTORE);
1✔
208
            j += parameterType.getSize();
1✔
209
        }
210
    }
1✔
211

212
    protected final void generateCodeToObtainInstanceOfClassLoadingBridge(
213
            @NonNull ClassLoadingBridge classLoadingBridge) {
214
        String hostClassName = ClassLoadingBridge.getHostClassName();
1✔
215
        mw.visitFieldInsn(GETSTATIC, hostClassName, classLoadingBridge.id, "Ljava/lang/reflect/InvocationHandler;");
1✔
216
    }
1✔
217

218
    protected final void generateCodeToFillArrayElement(@NonNegative int arrayIndex, @Nullable Object value) {
219
        mw.visitInsn(DUP);
1✔
220
        mw.visitIntInsn(SIPUSH, arrayIndex);
1✔
221

222
        if (value == null) {
1✔
223
            mw.visitInsn(ACONST_NULL);
1✔
224
        } else if (value instanceof Integer) {
1✔
225
            mw.visitIntInsn(SIPUSH, (Integer) value);
1✔
226
            mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
1✔
227
        } else {
228
            mw.visitLdcInsn(value);
1✔
229
        }
230

231
        mw.visitInsn(AASTORE);
1✔
232
    }
1✔
233

234
    private void pushDefaultValueForType(@NonNull JavaType type) {
235
        if (type instanceof ArrayType) {
1✔
236
            generateCreationOfEmptyArray((ArrayType) type);
1✔
237
        } else {
238
            int constOpcode = type.getConstOpcode();
1✔
239

240
            if (constOpcode > 0) {
1✔
241
                mw.visitInsn(constOpcode);
1✔
242
            }
243
        }
244
    }
1✔
245

246
    private void generateCreationOfEmptyArray(@NonNull ArrayType arrayType) {
247
        int dimensions = arrayType.getDimensions();
1✔
248

249
        for (int dimension = 0; dimension < dimensions; dimension++) {
1✔
250
            mw.visitInsn(ICONST_0);
1✔
251
        }
252

253
        if (dimensions > 1) {
1✔
254
            mw.visitMultiANewArrayInsn(arrayType.getDescriptor(), dimensions);
1✔
255
            return;
1✔
256
        }
257

258
        JavaType elementType = arrayType.getElementType();
1✔
259

260
        if (elementType instanceof ReferenceType) {
1✔
261
            mw.visitTypeInsn(ANEWARRAY, ((ReferenceType) elementType).getInternalName());
1✔
262
        } else {
263
            int typeCode = PrimitiveType.getArrayElementType((PrimitiveType) elementType);
1✔
264
            mw.visitIntInsn(NEWARRAY, typeCode);
1✔
265
        }
266
    }
1✔
267

268
    protected final void generateCallToInvocationHandler() {
269
        mw.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke",
1✔
270
                "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
271
    }
1✔
272

273
    protected final void generateEmptyImplementation(@NonNull String desc) {
274
        JavaType returnType = JavaType.getReturnType(desc);
1✔
275
        pushDefaultValueForType(returnType);
1✔
276
        mw.visitInsn(returnType.getOpcode(IRETURN));
1✔
277
        mw.visitMaxStack(1);
1✔
278
    }
1✔
279

280
    protected final void generateEmptyImplementation() {
281
        mw.visitInsn(RETURN);
1✔
282
        mw.visitMaxStack(1);
1✔
283
    }
1✔
284

285
    @NonNull
286
    protected final MethodVisitor copyOriginalImplementationWithInjectedInterceptionCode() {
287
        if ("<init>".equals(methodName)) {
1✔
288
            return new DynamicConstructorModifier();
1✔
289
        }
290

291
        generateInterceptionCode();
1✔
292
        return new DynamicModifier();
1✔
293
    }
294

295
    protected void generateInterceptionCode() {
296
    }
×
297

298
    private class DynamicModifier extends WrappingMethodVisitor {
299
        DynamicModifier() {
1✔
300
            super(BaseClassModifier.this.mw);
1✔
301
        }
1✔
302

303
        @Override
304
        public final void visitLocalVariable(@NonNull String name, @NonNull String desc, @Nullable String signature,
305
                @NonNull Label start, @NonNull Label end, @NonNegative int index) {
306
            // For some reason, the start position for "this" gets displaced by bytecode inserted at the beginning,
307
            // in a method modified by the EMMA tool. If not treated, this causes a ClassFormatError.
308
            if (end.position > 0 && start.position > end.position) {
1!
309
                start.position = end.position;
×
310
            }
311

312
            // Ignores any local variable with required information missing, to avoid a VerifyError/ClassFormatError.
313
            if (start.position > 0 && end.position > 0) {
1!
314
                mw.visitLocalVariable(name, desc, signature, start, end, index);
1✔
315
            }
316
        }
1✔
317
    }
318

319
    private final class DynamicConstructorModifier extends DynamicModifier {
1✔
320
        private boolean pendingCallToConstructorOfSameClass;
321
        private boolean callToAnotherConstructorAlreadyCopied;
322

323
        @Override
324
        public void visitTypeInsn(int opcode, @NonNull String typeDesc) {
325
            mw.visitTypeInsn(opcode, typeDesc);
1✔
326

327
            if (opcode == NEW && !callToAnotherConstructorAlreadyCopied && typeDesc.equals(classDesc)) {
1✔
328
                pendingCallToConstructorOfSameClass = true;
1✔
329
            }
330
        }
1✔
331

332
        @Override
333
        public void visitMethodInsn(int opcode, @NonNull String owner, @NonNull String name, @NonNull String desc,
334
                boolean itf) {
335
            mw.visitMethodInsn(opcode, owner, name, desc, itf);
1✔
336

337
            if (pendingCallToConstructorOfSameClass) {
1✔
338
                if (opcode == INVOKESPECIAL && "<init>".equals(name) && owner.equals(classDesc)) {
1!
339
                    pendingCallToConstructorOfSameClass = false;
1✔
340
                }
341
            } else if (opcode == INVOKESPECIAL && !callToAnotherConstructorAlreadyCopied && "<init>".equals(name)
1!
342
                    && (owner.equals(superClassName) || owner.equals(classDesc))) {
1✔
343
                generateInterceptionCode();
1✔
344
                callToAnotherConstructorAlreadyCopied = true;
1✔
345
            }
346
        }
1✔
347
    }
348
}
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