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.faking;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.lang.reflect.Method;
12  
13  import mockit.Invocation;
14  import mockit.internal.ClassLoadingBridge;
15  import mockit.internal.reflection.MethodReflection;
16  import mockit.internal.reflection.ParameterReflection;
17  import mockit.internal.state.TestRun;
18  import mockit.internal.util.DefaultValues;
19  import mockit.internal.util.TypeDescriptor;
20  
21  public final class FakeMethodBridge extends ClassLoadingBridge {
22      @NonNull
23      public static final ClassLoadingBridge MB = new FakeMethodBridge();
24  
25      private FakeMethodBridge() {
26          super("$FMB");
27      }
28  
29      @Nullable
30      @Override
31      public Object invoke(@Nullable Object fakedInstance, Method method, @NonNull Object[] args) throws Throwable {
32          String fakeClassDesc = (String) args[0];
33          String fakedClassDesc = (String) args[1];
34          String fakeDesc = (String) args[4];
35  
36          Object fake = TestRun.getFake(fakeClassDesc, fakedInstance);
37  
38          if (fake == null || notToBeMocked(fakedInstance, fakedClassDesc)) {
39              return DefaultValues.computeForReturnType(fakedClassDesc);
40          }
41  
42          String fakeName = (String) args[3];
43          int fakeStateIndex = (Integer) args[5];
44          Object[] fakeArgs = extractArguments(6, args);
45  
46          return callFake(fakedInstance, fake, fakedClassDesc, fakeName, fakeDesc, fakeStateIndex, fakeArgs);
47      }
48  
49      @Nullable
50      private static Object callFake(@Nullable Object fakedInstance, @NonNull Object fake, @NonNull String fakedClassDesc,
51              @NonNull String fakeOrFakedName, @NonNull String fakeOrFakedDesc, int fakeStateIndex,
52              @NonNull Object[] fakeArgs) throws Throwable {
53          Class<?> fakeClass = fake.getClass();
54  
55          if (fakeStateIndex < 0) {
56              return executeSimpleFakeMethod(fakeClass, fake, fakeOrFakedName, fakeOrFakedDesc, fakeArgs);
57          }
58  
59          FakeState fakeState = TestRun.getFakeStates().getFakeState(fake, fakeStateIndex);
60  
61          if (!fakeState.fakeMethod.hasInvocationParameter()) {
62              return executeFakeMethodWithoutInvocationArgument(fakeState, fakeClass, fake, fakeOrFakedDesc, fakeArgs);
63          }
64  
65          if (fakeState.shouldProceedIntoRealImplementation(fakedInstance, fakedClassDesc)) {
66              return Void.class;
67          }
68  
69          return executeFakeMethodWithInvocationArgument(fakeState, fakeClass, fake, fakedInstance, fakedClassDesc,
70                  fakeOrFakedName, fakeOrFakedDesc, fakeArgs);
71      }
72  
73      @Nullable
74      private static Object executeSimpleFakeMethod(@NonNull Class<?> fakeClass, @Nullable Object fake,
75              @NonNull String fakeOrFakedName, @NonNull String fakeOrFakedDesc, @NonNull Object[] fakeArgs)
76              throws Throwable {
77          Class<?>[] paramClasses = TypeDescriptor.getParameterTypes(fakeOrFakedDesc);
78          return MethodReflection.invokeWithCheckedThrows(fakeClass, fake, fakeOrFakedName, paramClasses, fakeArgs);
79      }
80  
81      @Nullable
82      private static Object executeFakeMethodWithoutInvocationArgument(@NonNull FakeState fakeState,
83              @NonNull Class<?> fakeClass, @Nullable Object fake, @NonNull String fakeOrFakedDesc,
84              @NonNull Object[] fakeArgs) throws Throwable {
85          Class<?>[] paramClasses = TypeDescriptor.getParameterTypes(fakeOrFakedDesc);
86          Method fakeMethod = fakeState.getFakeMethod(fakeClass, paramClasses);
87          return MethodReflection.invokeWithCheckedThrows(fake, fakeMethod, fakeArgs);
88      }
89  
90      @Nullable
91      private static Object executeFakeMethodWithInvocationArgument(@NonNull FakeState fakeState,
92              @NonNull Class<?> fakeClass, @Nullable Object fake, @Nullable Object fakedInstance,
93              @NonNull String fakedClassDesc, @NonNull String fakedName, @NonNull String fakedDesc,
94              @NonNull Object[] fakeArgs) throws Throwable {
95          Class<?>[] paramClasses;
96          Method fakeMethod;
97          FakeInvocation invocation;
98          Object[] executionArgs;
99  
100         if (fakeState.fakeMethod.hasInvocationParameterOnly()) {
101             paramClasses = new Class[] { Invocation.class };
102             fakeMethod = fakeState.getFakeMethod(fakeClass, paramClasses);
103             invocation = new FakeInvocation(fakedInstance, fakeArgs, fakeState, fakedClassDesc, fakedName, fakedDesc);
104             executionArgs = new Object[] { invocation };
105         } else {
106             paramClasses = TypeDescriptor.getParameterTypes(fakedDesc);
107             fakeMethod = fakeState.getFakeMethod(fakeClass, paramClasses);
108 
109             // noinspection AssignmentToMethodParameter
110             fakedDesc = fakeState.fakeMethod.fakeDescWithoutInvocationParameter;
111             invocation = new FakeInvocation(fakedInstance, fakeArgs, fakeState, fakedClassDesc, fakedName, fakedDesc);
112             executionArgs = ParameterReflection.argumentsWithExtraFirstValue(fakeArgs, invocation);
113         }
114 
115         Object result = MethodReflection.invokeWithCheckedThrows(fake, fakeMethod, executionArgs);
116         return invocation.shouldProceedIntoConstructor() ? Void.class : result;
117     }
118 }