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