1
2
3
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 }