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