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

hazendaz / jmockit1 / 120

26 Apr 2025 09:14PM UTC coverage: 73.275% (-0.02%) from 73.295%
120

push

github

web-flow
Potential fix for code scanning alert no. 655: Random used only once

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

5623 of 8178 branches covered (68.76%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

11886 of 15717 relevant lines covered (75.63%)

0.76 hits per line

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

93.54
/main/src/main/java/mockit/internal/state/MockFixture.java
1
/*
2
 * Copyright (c) 2006 JMockit developers
3
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
4
 */
5
package mockit.internal.state;
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

160
    // Methods used by the Mocking API.
161

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

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

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

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

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

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

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

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

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

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

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

207
        return false;
×
208
    }
209

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

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

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

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

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

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

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

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

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

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

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

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

259
        return instanceFactory;
1✔
260
    }
261

262
    // Methods used by the Faking API.
263

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

392
    // Methods dealing with capture transformers ///////////////////////////////////////////////////////////////////////
393

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

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

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

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

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

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

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

423
        return false;
1✔
424
    }
425

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

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

435
        return false;
1✔
436
    }
437

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

445
        return false;
1✔
446
    }
447

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

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

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

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

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