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.transformation;
7   
8   import static mockit.asm.jvmConstants.Opcodes.ASTORE;
9   import static mockit.asm.jvmConstants.Opcodes.CHECKCAST;
10  import static mockit.asm.jvmConstants.Opcodes.ISTORE;
11  
12  import edu.umd.cs.findbugs.annotations.NonNull;
13  import edu.umd.cs.findbugs.annotations.Nullable;
14  
15  import java.util.ArrayList;
16  import java.util.HashMap;
17  import java.util.List;
18  import java.util.Map;
19  
20  import mockit.asm.types.ReferenceType;
21  
22  import org.checkerframework.checker.index.qual.NonNegative;
23  
24  public final class ArgumentCapturing {
25      private static final Map<Integer, String> varIndexToTypeDesc = new HashMap<>();
26  
27      @NonNull
28      private final InvocationBlockModifier modifier;
29      @Nullable
30      private List<Capture> captures;
31      private boolean parameterForCapture;
32      @Nullable
33      private String capturedTypeDesc;
34  
35      ArgumentCapturing(@NonNull InvocationBlockModifier modifier) {
36          this.modifier = modifier;
37      }
38  
39      boolean registerMatcher(boolean withCaptureMethod, @NonNull String methodDesc,
40              @NonNegative int lastLoadedVarIndex) {
41          if (withCaptureMethod && "(Ljava/lang/Object;)Ljava/util/List;".equals(methodDesc)) {
42              return false;
43          }
44  
45          if (withCaptureMethod) {
46              if (methodDesc.contains("List")) {
47                  if (lastLoadedVarIndex > 0) {
48                      int parameterIndex = modifier.argumentMatching.getMatcherCount();
49                      Capture capture = new Capture(modifier, lastLoadedVarIndex, parameterIndex);
50                      addCapture(capture);
51                  }
52  
53                  parameterForCapture = false;
54              } else {
55                  parameterForCapture = true;
56              }
57          } else {
58              parameterForCapture = false;
59          }
60  
61          return true;
62      }
63  
64      void registerTypeToCaptureIfApplicable(@NonNegative int opcode, @NonNull String typeDesc) {
65          if (opcode == CHECKCAST && parameterForCapture) {
66              capturedTypeDesc = typeDesc;
67          }
68      }
69  
70      static void registerTypeToCaptureIntoListIfApplicable(@NonNegative int varIndex, @NonNull String signature) {
71          if (signature.startsWith("Ljava/util/List<")) {
72              String typeDesc = signature.substring(16, signature.length() - 2);
73              int p = typeDesc.indexOf('<');
74  
75              if (p > 0) {
76                  typeDesc = typeDesc.substring(0, p) + ';';
77              }
78  
79              ReferenceType type = ReferenceType.createFromTypeDescriptor(typeDesc);
80              varIndexToTypeDesc.put(varIndex, type.getInternalName());
81          }
82      }
83  
84      void registerAssignmentToCaptureVariableIfApplicable(@NonNegative int opcode, @NonNegative int varIndex) {
85          if (opcode >= ISTORE && opcode <= ASTORE && parameterForCapture) {
86              int parameterIndex = modifier.argumentMatching.getMatcherCount() - 1;
87              Capture capture = new Capture(modifier, opcode, varIndex, capturedTypeDesc, parameterIndex);
88              addCapture(capture);
89              parameterForCapture = false;
90              capturedTypeDesc = null;
91          }
92      }
93  
94      private void addCapture(@NonNull Capture capture) {
95          if (captures == null) {
96              captures = new ArrayList<>();
97          }
98  
99          captures.add(capture);
100     }
101 
102     void updateCaptureIfAny(@NonNegative int originalIndex, @NonNegative int newIndex) {
103         if (captures != null) {
104             for (int i = captures.size() - 1; i >= 0; i--) {
105                 Capture capture = captures.get(i);
106 
107                 if (capture.fixParameterIndex(originalIndex, newIndex)) {
108                     break;
109                 }
110             }
111         }
112     }
113 
114     void generateCallsToSetArgumentTypesToCaptureIfAny() {
115         if (captures != null) {
116             for (Capture capture : captures) {
117                 capture.generateCallToSetArgumentTypeIfNeeded();
118             }
119         }
120     }
121 
122     void generateCallsToCaptureMatchedArgumentsIfPending() {
123         if (captures != null) {
124             for (Capture capture : captures) {
125                 capture.generateCodeToStoreCapturedValue();
126             }
127 
128             captures = null;
129         }
130     }
131 
132     @Nullable
133     public static String extractArgumentType(@NonNegative int varIndex) {
134         return varIndexToTypeDesc.remove(varIndex);
135     }
136 }