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