View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.expectations.mocking;
7   
8   import static java.lang.reflect.Modifier.isFinal;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  
12  import java.lang.reflect.Field;
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Map.Entry;
18  
19  import mockit.asm.jvmConstants.Access;
20  import mockit.internal.reflection.FieldReflection;
21  import mockit.internal.state.TestRun;
22  import mockit.internal.util.StackTrace;
23  
24  @SuppressWarnings("UnnecessaryFullyQualifiedName")
25  public final class FieldTypeRedefinitions extends TypeRedefinitions {
26      private static final int FIELD_ACCESS_MASK = Access.SYNTHETIC + Access.STATIC;
27  
28      @NonNull
29      private final Map<MockedType, InstanceFactory> mockInstanceFactories;
30      @NonNull
31      private final List<MockedType> mockFieldsNotSet;
32  
33      public FieldTypeRedefinitions(@NonNull Class<?> testClass) {
34          mockInstanceFactories = new HashMap<>();
35          mockFieldsNotSet = new ArrayList<>();
36  
37          TestRun.enterNoMockingZone();
38  
39          try {
40              redefineFieldTypes(testClass);
41          } finally {
42              TestRun.exitNoMockingZone();
43          }
44      }
45  
46      private void redefineFieldTypes(@NonNull Class<?> classWithMockFields) {
47          Class<?> superClass = classWithMockFields.getSuperclass();
48  
49          if (superClass != null && superClass != Object.class && superClass != mockit.Expectations.class) {
50              redefineFieldTypes(superClass);
51          }
52  
53          Field[] fields = classWithMockFields.getDeclaredFields();
54  
55          for (Field candidateField : fields) {
56              int fieldModifiers = candidateField.getModifiers();
57  
58              if ((fieldModifiers & FIELD_ACCESS_MASK) == 0) {
59                  redefineFieldType(candidateField, fieldModifiers);
60              }
61          }
62      }
63  
64      private void redefineFieldType(@NonNull Field field, int modifiers) {
65          MockedType mockedType = new MockedType(field);
66  
67          if (mockedType.isMockableType()) {
68              boolean partialMocking = field.isAnnotationPresent(mockit.Tested.class);
69              boolean needsValueToSet = !isFinal(modifiers) && !partialMocking;
70  
71              redefineFieldType(mockedType, partialMocking, needsValueToSet);
72  
73              if (!partialMocking) {
74                  registerCaptureOfNewInstances(mockedType);
75              }
76          }
77      }
78  
79      private void redefineFieldType(@NonNull MockedType mockedType, boolean partialMocking, boolean needsValueToSet) {
80          FieldTypeRedefinition typeRedefinition = new FieldTypeRedefinition(mockedType);
81          boolean redefined;
82  
83          if (needsValueToSet) {
84              InstanceFactory factory = typeRedefinition.redefineType();
85              redefined = factory != null;
86  
87              if (redefined) {
88                  mockInstanceFactories.put(mockedType, factory);
89              }
90          } else {
91              if (partialMocking) {
92                  redefined = typeRedefinition.redefineTypeForTestedField();
93              } else {
94                  redefined = typeRedefinition.redefineTypeForFinalField();
95              }
96  
97              if (redefined) {
98                  mockFieldsNotSet.add(mockedType);
99              }
100         }
101 
102         if (redefined) {
103             addTargetClass(mockedType);
104         }
105     }
106 
107     private void registerCaptureOfNewInstances(@NonNull MockedType mockedType) {
108         if (mockedType.getMaxInstancesToCapture() > 0) {
109             if (captureOfNewInstances == null) {
110                 captureOfNewInstances = new CaptureOfNewInstancesForFields();
111             }
112 
113             captureOfNewInstances.registerCaptureOfNewInstances(mockedType, null);
114         }
115     }
116 
117     public void assignNewInstancesToMockFields(@NonNull Object target) {
118         TestRun.getExecutingTest().clearRegularAndInjectableMocks();
119         createAndAssignNewInstances(target);
120         obtainAndRegisterInstancesOfFieldsNotSet(target);
121     }
122 
123     private void createAndAssignNewInstances(@NonNull Object target) {
124         for (Entry<MockedType, InstanceFactory> metadataAndFactory : mockInstanceFactories.entrySet()) {
125             MockedType mockedType = metadataAndFactory.getKey();
126             InstanceFactory instanceFactory = metadataAndFactory.getValue();
127 
128             Object mock = assignNewInstanceToMockField(target, mockedType, instanceFactory);
129             registerMock(mockedType, mock);
130         }
131     }
132 
133     @NonNull
134     private Object assignNewInstanceToMockField(@NonNull Object target, @NonNull MockedType mockedType,
135             @NonNull InstanceFactory instanceFactory) {
136         Field mockField = mockedType.field;
137         assert mockField != null;
138         Object mock = FieldReflection.getFieldValue(mockField, target);
139 
140         if (mock == null) {
141             try {
142                 mock = instanceFactory.create();
143             } catch (NoClassDefFoundError | ExceptionInInitializerError e) {
144                 StackTrace.filterStackTrace(e);
145                 e.printStackTrace();
146                 throw e;
147             }
148 
149             FieldReflection.setFieldValue(mockField, target, mock);
150 
151             if (mockedType.getMaxInstancesToCapture() > 0) {
152                 assert captureOfNewInstances != null;
153                 CaptureOfNewInstancesForFields capture = (CaptureOfNewInstancesForFields) captureOfNewInstances;
154                 capture.resetCaptureCount(mockField);
155             }
156         }
157 
158         return mock;
159     }
160 
161     private void obtainAndRegisterInstancesOfFieldsNotSet(@NonNull Object target) {
162         for (MockedType metadata : mockFieldsNotSet) {
163             assert metadata.field != null;
164             Object mock = FieldReflection.getFieldValue(metadata.field, target);
165 
166             if (mock != null) {
167                 registerMock(metadata, mock);
168             }
169         }
170     }
171 
172     /**
173      * Returns true iff the mock instance concrete class is not mocked in some test, i.e. it's a class which only
174      * appears in the code under test.
175      */
176     public boolean captureNewInstanceForApplicableMockField(@NonNull Object mock) {
177         if (captureOfNewInstances == null) {
178             return false;
179         }
180 
181         Object fieldOwner = TestRun.getCurrentTestInstance();
182         return captureOfNewInstances.captureNewInstance(fieldOwner, mock);
183     }
184 
185     @Override
186     public void cleanUp() {
187         TestRun.getExecutingTest().getCascadingTypes().clear();
188         super.cleanUp();
189     }
190 }