• 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

93.89
/main/src/main/java/mockit/internal/state/MockFixture.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.state;
7

8
import static java.lang.reflect.Modifier.isAbstract;
9

10
import static mockit.internal.util.GeneratedClasses.getMockedClass;
11
import static mockit.internal.util.GeneratedClasses.getMockedClassOrInterfaceType;
12
import static mockit.internal.util.GeneratedClasses.isGeneratedImplementationClass;
13
import static mockit.internal.util.Utilities.getClassType;
14

15
import edu.umd.cs.findbugs.annotations.NonNull;
16
import edu.umd.cs.findbugs.annotations.Nullable;
17

18
import java.lang.instrument.ClassDefinition;
19
import java.lang.reflect.InvocationTargetException;
20
import java.lang.reflect.Method;
21
import java.lang.reflect.Type;
22
import java.util.ArrayList;
23
import java.util.Collections;
24
import java.util.HashMap;
25
import java.util.HashSet;
26
import java.util.IdentityHashMap;
27
import java.util.Iterator;
28
import java.util.List;
29
import java.util.Map;
30
import java.util.Map.Entry;
31
import java.util.Set;
32
import java.util.concurrent.ConcurrentHashMap;
33

34
import mockit.internal.ClassFile;
35
import mockit.internal.ClassIdentification;
36
import mockit.internal.capturing.CaptureTransformer;
37
import mockit.internal.expectations.mocking.CaptureOfNewInstances;
38
import mockit.internal.expectations.mocking.InstanceFactory;
39
import mockit.internal.startup.Startup;
40
import mockit.internal.util.ClassLoad;
41

42
/**
43
 * Holds data about redefined/transformed classes, with methods to add/remove and query such data.
44
 */
45
public final class MockFixture {
46
    /**
47
     * Similar to {@link #redefinedClasses}, but for classes modified by a <code>ClassFileTransformer</code> such as the
48
     * <code>CaptureTransformer</code>, and containing the pre-transform bytecode instead of the modified one.
49
     *
50
     * @see #addTransformedClass(ClassIdentification, byte[])
51
     * @see #getTransformedClasses()
52
     * @see #restoreTransformedClasses(Set)
53
     */
54
    @NonNull
55
    private final Map<ClassIdentification, byte[]> transformedClasses;
56

57
    /**
58
     * Real classes currently redefined in the running JVM and their current (modified) bytecodes.
59
     * <p>
60
     * The keys in the map allow each redefined real class to be later restored to a previous definition.
61
     * <p>
62
     * The modified bytecode arrays in the map allow a new redefinition to be made on top of the current redefinition
63
     * (in the case of the Faking API), or to restore the class to a previous definition (provided the map is copied
64
     * between redefinitions of the same class).
65
     *
66
     * @see #addRedefinedClass(ClassDefinition)
67
     * @see #getRedefinedClasses()
68
     * @see #getRedefinedClassfile(Class)
69
     * @see #containsRedefinedClass(Class)
70
     * @see #restoreRedefinedClasses(Map)
71
     */
72
    @NonNull
73
    private final Map<Class<?>, byte[]> redefinedClasses;
74

75
    /**
76
     * Subset of all currently redefined classes which contain one or more native methods.
77
     * <p/>
78
     * This is needed because in order to restore such methods it is necessary (for some classes) to re-register them
79
     * with the JVM.
80
     *
81
     * @see #reregisterNativeMethodsForRestoredClass(Class)
82
     */
83
    @NonNull
84
    private final Set<String> redefinedClassesWithNativeMethods;
85

86
    /**
87
     * Maps redefined real classes to the internal name of the corresponding fake classes, when it's the case.
88
     * <p>
89
     * This allows any global state associated to a fake class to be discarded when the corresponding real class is
90
     * later restored to its original definition.
91
     *
92
     * @see #addRedefinedClass(String, ClassDefinition)
93
     */
94
    @NonNull
95
    private final Map<Class<?>, String> realClassesToFakeClasses;
96

97
    /**
98
     * A list of classes that are currently mocked. Said classes are also added to {@link #mockedTypesAndInstances}.
99
     *
100
     * @see #registerMockedClass(Class)
101
     * @see #getMockedClasses()
102
     * @see #isStillMocked(Object, String)
103
     * @see #isInstanceOfMockedClass(Object)
104
     * @see #removeMockedClasses(List)
105
     */
106
    @NonNull
107
    private final List<Class<?>> mockedClasses;
108

109
    /**
110
     * A map of mocked types to their corresponding {@linkplain InstanceFactory mocked instance factories}.
111
     *
112
     * @see #registerInstanceFactoryForMockedType(Class, InstanceFactory)
113
     * @see #findInstanceFactory(Type)
114
     * @see #isStillMocked(Object, String)
115
     * @see #removeMockedClasses(List)
116
     */
117
    @NonNull
118
    private final Map<Type, InstanceFactory> mockedTypesAndInstances;
119

120
    /**
121
     * A list of "capturing" class file transformers, used by both the mocking and faking APIs.
122
     *
123
     * @see #addCaptureTransformer(CaptureTransformer)
124
     * @see #areCapturedClasses(Class, Class)
125
     * @see #isCaptured(Object)
126
     * @see #getCaptureTransformerCount()
127
     * @see #removeCaptureTransformers(int)
128
     */
129
    @NonNull
130
    private final List<CaptureTransformer<?>> captureTransformers;
131

132
    MockFixture() {
1✔
133
        transformedClasses = new HashMap<>(2);
1✔
134
        redefinedClasses = new ConcurrentHashMap<>(8);
1✔
135
        redefinedClassesWithNativeMethods = new HashSet<>();
1✔
136
        realClassesToFakeClasses = new IdentityHashMap<>(8);
1✔
137
        mockedClasses = new ArrayList<>();
1✔
138
        mockedTypesAndInstances = new IdentityHashMap<>();
1✔
139
        captureTransformers = new ArrayList<>();
1✔
140
    }
1✔
141

142
    // Methods to add/remove transformed/redefined classes /////////////////////////////////////////////////////////////
143

144
    public void addTransformedClass(@NonNull ClassIdentification classId, @NonNull byte[] pretransformClassfile) {
145
        transformedClasses.put(classId, pretransformClassfile);
1✔
146
    }
1✔
147

148
    // Methods used by both the Mocking and Faking APIs.
149

150
    public void addRedefinedClass(@NonNull ClassDefinition newClassDefinition) {
151
        redefinedClasses.put(newClassDefinition.getDefinitionClass(), newClassDefinition.getDefinitionClassFile());
1✔
152
    }
1✔
153

154
    public void registerMockedClass(@NonNull Class<?> mockedType) {
155
        if (!mockedClasses.contains(mockedType)) {
1✔
156
            mockedType = getMockedClassOrInterfaceType(mockedType);
1✔
157
            mockedClasses.add(mockedType);
1✔
158
        }
159
    }
1✔
160

161
    // Methods used by the Mocking API.
162

163
    public void redefineClasses(@NonNull ClassDefinition... definitions) {
164
        Startup.redefineMethods(definitions);
1✔
165

166
        for (ClassDefinition def : definitions) {
1✔
167
            addRedefinedClass(def);
1✔
168
        }
169
    }
1✔
170

171
    public void redefineMethods(@NonNull Map<Class<?>, byte[]> modifiedClassfiles) {
172
        ClassDefinition[] classDefs = new ClassDefinition[modifiedClassfiles.size()];
1✔
173
        int i = 0;
1✔
174

175
        for (Entry<Class<?>, byte[]> classAndBytecode : modifiedClassfiles.entrySet()) {
1✔
176
            Class<?> modifiedClass = classAndBytecode.getKey();
1✔
177
            byte[] modifiedClassfile = classAndBytecode.getValue();
1✔
178

179
            ClassDefinition classDef = new ClassDefinition(modifiedClass, modifiedClassfile);
1✔
180
            classDefs[i] = classDef;
1✔
181
            i++;
1✔
182

183
            addRedefinedClass(classDef);
1✔
184
        }
1✔
185

186
        Startup.redefineMethods(classDefs);
1✔
187
    }
1✔
188

189
    public boolean isStillMocked(@Nullable Object instance, @NonNull String classDesc) {
190
        Class<?> targetClass;
191

192
        if (instance == null) {
1✔
193
            targetClass = ClassLoad.loadByInternalName(classDesc);
1✔
194
            return isClassAssignableTo(targetClass);
1✔
195
        }
196

197
        targetClass = instance.getClass();
1✔
198
        return mockedTypesAndInstances.containsKey(targetClass) || isInstanceOfMockedClass(instance);
1✔
199
    }
200

201
    private boolean isClassAssignableTo(@NonNull Class<?> toClass) {
202
        for (Class<?> mockedClass : mockedClasses) {
1!
203
            if (toClass == mockedClass || toClass.isAssignableFrom(mockedClass)) {
1✔
204
                return true;
1✔
205
            }
206
        }
1✔
207

208
        return false;
×
209
    }
210

211
    public boolean isInstanceOfMockedClass(@NonNull Object mockedInstance) {
212
        Class<?> mockedClass = getMockedClassOrInterfaceType(mockedInstance.getClass());
1✔
213
        Class<?> mockedSuperclass = mockedClass.getSuperclass();
1✔
214

215
        if (mockedSuperclass != null && mockedSuperclass.isEnum()) {
1✔
216
            return mockedClasses.contains(mockedSuperclass);
1✔
217
        }
218

219
        return mockedClasses.contains(mockedClass) || isCaptured(mockedClass);
1✔
220
    }
221

222
    public void registerInstanceFactoryForMockedType(@NonNull Class<?> mockedType,
223
            @NonNull InstanceFactory mockedInstanceFactory) {
224
        registerMockedClass(mockedType);
1✔
225
        mockedTypesAndInstances.put(mockedType, mockedInstanceFactory);
1✔
226
    }
1✔
227

228
    @Nullable
229
    public InstanceFactory findInstanceFactory(@NonNull Type mockedType) {
230
        InstanceFactory instanceFactory = mockedTypesAndInstances.get(mockedType);
1✔
231

232
        if (instanceFactory != null) {
1✔
233
            return instanceFactory;
1✔
234
        }
235

236
        Class<?> mockedClass = getClassType(mockedType);
1✔
237
        // noinspection ReuseOfLocalVariable
238
        instanceFactory = mockedTypesAndInstances.get(mockedClass);
1✔
239

240
        if (instanceFactory != null) {
1✔
241
            return instanceFactory;
1✔
242
        }
243

244
        boolean abstractType = mockedClass.isInterface() || isAbstract(mockedClass.getModifiers());
1✔
245

246
        for (Entry<Type, InstanceFactory> entry : mockedTypesAndInstances.entrySet()) {
1✔
247
            Type registeredMockedType = entry.getKey();
1✔
248
            Class<?> registeredMockedClass = getClassType(registeredMockedType);
1✔
249

250
            if (abstractType) {
1✔
251
                registeredMockedClass = getMockedClassOrInterfaceType(registeredMockedClass);
1✔
252
            }
253

254
            if (mockedClass.isAssignableFrom(registeredMockedClass)) {
1✔
255
                instanceFactory = entry.getValue();
1✔
256
                break;
1✔
257
            }
258
        }
1✔
259

260
        return instanceFactory;
1✔
261
    }
262

263
    // Methods used by the Faking API.
264

265
    public void addRedefinedClass(@NonNull String fakeClassInternalName, @NonNull ClassDefinition classDef) {
266
        @NonNull
267
        Class<?> redefinedClass = classDef.getDefinitionClass();
1✔
268
        String previousNames = realClassesToFakeClasses.put(redefinedClass, fakeClassInternalName);
1✔
269

270
        if (previousNames != null) {
1✔
271
            realClassesToFakeClasses.put(redefinedClass, previousNames + ' ' + fakeClassInternalName);
1✔
272
        }
273

274
        addRedefinedClass(classDef);
1✔
275
    }
1✔
276

277
    // Methods used by test save-points ////////////////////////////////////////////////////////////////////////////////
278

279
    void restoreTransformedClasses(@NonNull Set<ClassIdentification> previousTransformedClasses) {
280
        if (!transformedClasses.isEmpty()) {
1✔
281
            Set<ClassIdentification> classesToRestore;
282

283
            if (previousTransformedClasses.isEmpty()) {
1!
284
                classesToRestore = transformedClasses.keySet();
1✔
285
            } else {
286
                classesToRestore = getTransformedClasses();
×
287
                classesToRestore.removeAll(previousTransformedClasses);
×
288
            }
289

290
            if (!classesToRestore.isEmpty()) {
1!
291
                restoreAndRemoveTransformedClasses(classesToRestore);
1✔
292
            }
293
        }
294
    }
1✔
295

296
    @NonNull
297
    Set<ClassIdentification> getTransformedClasses() {
298
        return transformedClasses.isEmpty() ? Collections.emptySet() : new HashSet<>(transformedClasses.keySet());
1!
299
    }
300

301
    @NonNull
302
    Map<Class<?>, byte[]> getRedefinedClasses() {
303
        return redefinedClasses.isEmpty() ? Collections.emptyMap() : new HashMap<>(redefinedClasses);
1✔
304
    }
305

306
    private void restoreAndRemoveTransformedClasses(@NonNull Set<ClassIdentification> classesToRestore) {
307
        for (ClassIdentification transformedClassId : classesToRestore) {
1✔
308
            byte[] definitionToRestore = transformedClasses.get(transformedClassId);
1✔
309
            Startup.redefineMethods(transformedClassId, definitionToRestore);
1✔
310
        }
1✔
311

312
        transformedClasses.keySet().removeAll(classesToRestore);
1✔
313
    }
1✔
314

315
    void restoreRedefinedClasses(@NonNull Map<?, byte[]> previousDefinitions) {
316
        if (redefinedClasses.isEmpty()) {
1✔
317
            return;
1✔
318
        }
319

320
        Iterator<Entry<Class<?>, byte[]>> itr = redefinedClasses.entrySet().iterator();
1✔
321

322
        while (itr.hasNext()) {
1✔
323
            Entry<Class<?>, byte[]> entry = itr.next();
1✔
324
            Class<?> redefinedClass = entry.getKey();
1✔
325
            byte[] currentDefinition = entry.getValue();
1✔
326
            byte[] previousDefinition = previousDefinitions.get(redefinedClass);
1✔
327

328
            if (previousDefinition == null) {
1✔
329
                restoreDefinition(redefinedClass);
1✔
330
                itr.remove();
1✔
331
            } else if (currentDefinition != previousDefinition) {
1✔
332
                Startup.redefineMethods(redefinedClass, previousDefinition);
1✔
333
                entry.setValue(previousDefinition);
1✔
334
            }
335
        }
1✔
336
    }
1✔
337

338
    private void restoreDefinition(@NonNull Class<?> redefinedClass) {
339
        if (!isGeneratedImplementationClass(redefinedClass)) {
1✔
340
            byte[] previousDefinition = ClassFile.getClassFile(redefinedClass);
1✔
341
            Startup.redefineMethods(redefinedClass, previousDefinition);
1✔
342
        }
343
        if (redefinedClassesWithNativeMethods.contains(redefinedClass.getName())) {
1✔
344
            reregisterNativeMethodsForRestoredClass(redefinedClass);
1✔
345
        }
346

347
        removeMockedClass(redefinedClass);
1✔
348
        discardStateForCorrespondingFakeClassIfAny(redefinedClass);
1✔
349
    }
1✔
350

351
    private void removeMockedClass(@NonNull Class<?> mockedClass) {
352
        mockedTypesAndInstances.remove(mockedClass);
1✔
353
        mockedClasses.remove(mockedClass);
1✔
354
    }
1✔
355

356
    private void discardStateForCorrespondingFakeClassIfAny(@NonNull Class<?> redefinedClass) {
357
        String mockClassesInternalNames = realClassesToFakeClasses.remove(redefinedClass);
1✔
358
        TestRun.getFakeStates().removeClassState(redefinedClass, mockClassesInternalNames);
1✔
359
    }
1✔
360

361
    void removeMockedClasses(@NonNull List<Class<?>> previousMockedClasses) {
362
        int currentMockedClassCount = mockedClasses.size();
1✔
363

364
        if (currentMockedClassCount > 0) {
1✔
365
            int previousMockedClassCount = previousMockedClasses.size();
1✔
366

367
            if (previousMockedClassCount == 0) {
1✔
368
                mockedClasses.clear();
1✔
369
                mockedTypesAndInstances.clear();
1✔
370
            } else if (previousMockedClassCount < currentMockedClassCount) {
1✔
371
                mockedClasses.retainAll(previousMockedClasses);
1✔
372
                mockedTypesAndInstances.keySet().retainAll(previousMockedClasses);
1✔
373
            }
374
        }
375
    }
1✔
376

377
    // Getter methods for the maps and collections of transformed/redefined/mocked classes /////////////////////////////
378

379
    @Nullable
380
    public byte[] getRedefinedClassfile(@NonNull Class<?> redefinedClass) {
381
        return redefinedClasses.get(redefinedClass);
1✔
382
    }
383

384
    public boolean containsRedefinedClass(@NonNull Class<?> redefinedClass) {
385
        return redefinedClasses.containsKey(redefinedClass);
1✔
386
    }
387

388
    @NonNull
389
    public List<Class<?>> getMockedClasses() {
390
        return mockedClasses.isEmpty() ? Collections.emptyList() : new ArrayList<>(mockedClasses);
1✔
391
    }
392

393
    // Methods dealing with capture transformers ///////////////////////////////////////////////////////////////////////
394

395
    public void addCaptureTransformer(@NonNull CaptureTransformer<?> transformer) {
396
        captureTransformers.add(transformer);
1✔
397
    }
1✔
398

399
    // The following methods are used by test save-points to discard currently active capture transformers.
400

401
    int getCaptureTransformerCount() {
402
        return captureTransformers.size();
1✔
403
    }
404

405
    void removeCaptureTransformers(int previousTransformerCount) {
406
        int currentTransformerCount = captureTransformers.size();
1✔
407

408
        for (int i = currentTransformerCount - 1; i >= previousTransformerCount; i--) {
1✔
409
            CaptureTransformer<?> transformer = captureTransformers.get(i);
1✔
410
            transformer.deactivate();
1✔
411
            Startup.instrumentation().removeTransformer(transformer);
1✔
412
            captureTransformers.remove(i);
1✔
413
        }
414
    }
1✔
415

416
    // The following methods are only used by the Mocking API.
417

418
    public boolean isCaptured(@NonNull Object mockedInstance) {
419
        if (!captureTransformers.isEmpty()) {
1✔
420
            Class<?> mockedClass = getMockedClass(mockedInstance);
1✔
421
            return isCaptured(mockedClass);
1✔
422
        }
423

424
        return false;
1✔
425
    }
426

427
    private boolean isCaptured(@NonNull Class<?> mockedClass) {
428
        for (CaptureTransformer<?> captureTransformer : captureTransformers) {
1✔
429
            CaptureOfNewInstances capture = captureTransformer.getCaptureOfImplementationsIfApplicable(mockedClass);
1✔
430

431
            if (capture != null) {
1✔
432
                return true;
1✔
433
            }
434
        }
1✔
435

436
        return false;
1✔
437
    }
438

439
    public boolean areCapturedClasses(@NonNull Class<?> mockedClass1, @NonNull Class<?> mockedClass2) {
440
        for (CaptureTransformer<?> captureTransformer : captureTransformers) {
1✔
441
            if (captureTransformer.areCapturedClasses(mockedClass1, mockedClass2)) {
1✔
442
                return true;
1✔
443
            }
444
        }
1✔
445

446
        return false;
1✔
447
    }
448

449
    private static void reregisterNativeMethodsForRestoredClass(@NonNull Class<?> realClass) {
450
        Method registerNatives = null;
1✔
451

452
        try {
453
            registerNatives = realClass.getDeclaredMethod("registerNatives");
×
454
        } catch (NoSuchMethodException ignore) {
1✔
455
            try {
456
                registerNatives = realClass.getDeclaredMethod("initIDs");
×
457
            } catch (NoSuchMethodException ignored) {
1✔
458
            } // OK
×
459
        }
×
460

461
        if (registerNatives != null) {
1!
462
            try {
463
                registerNatives.setAccessible(true);
×
464
                registerNatives.invoke(null);
×
465
            } catch (IllegalAccessException | InvocationTargetException ignore) {
×
466
            } // won't happen
×
467
        }
468

469
        // OK, although another solution will be required for this particular class if it requires
470
        // natives to be explicitly registered again (not all do, such as java.lang.Float).
471
    }
1✔
472

473
    public void addRedefinedClassWithNativeMethods(@NonNull String redefinedClassInternalName) {
474
        redefinedClassesWithNativeMethods.add(redefinedClassInternalName.replace('/', '.'));
1✔
475
    }
1✔
476
}
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