1
2
3
4
5 package mockit.internal.expectations.transformation;
6
7 import static mockit.asm.jvmConstants.Opcodes.ACONST_NULL;
8 import static mockit.asm.jvmConstants.Opcodes.ALOAD;
9 import static mockit.asm.jvmConstants.Opcodes.SIPUSH;
10 import static mockit.internal.util.TypeConversionBytecode.generateCastOrUnboxing;
11 import static mockit.internal.util.TypeConversionBytecode.isPrimitiveWrapper;
12
13 import edu.umd.cs.findbugs.annotations.NonNull;
14 import edu.umd.cs.findbugs.annotations.Nullable;
15
16 import mockit.asm.methods.MethodWriter;
17 import mockit.asm.types.ArrayType;
18 import mockit.asm.types.JavaType;
19 import mockit.asm.types.ObjectType;
20 import mockit.asm.types.ReferenceType;
21
22 import org.checkerframework.checker.index.qual.NonNegative;
23
24 final class Capture {
25 @NonNull
26 private final InvocationBlockModifier invocationBlockModifier;
27 @NonNull
28 private final MethodWriter mw;
29 @NonNegative
30 private final int opcode;
31 @NonNegative
32 private final int varIndex;
33 @Nullable
34 private String typeToCapture;
35 @NonNegative
36 private int parameterIndex;
37 @NonNegative
38 private boolean parameterIndexFixed;
39
40 Capture(@NonNull InvocationBlockModifier invocationBlockModifier, @NonNegative int opcode,
41 @NonNegative int varIndex, @Nullable String typeToCapture, @NonNegative int parameterIndex) {
42 this.invocationBlockModifier = invocationBlockModifier;
43 mw = invocationBlockModifier.getMethodWriter();
44 this.opcode = opcode;
45 this.varIndex = varIndex;
46 this.typeToCapture = typeToCapture;
47 this.parameterIndex = parameterIndex;
48 }
49
50 Capture(@NonNull InvocationBlockModifier invocationBlockModifier, @NonNegative int varIndex,
51 @NonNegative int parameterIndex) {
52 this.invocationBlockModifier = invocationBlockModifier;
53 mw = invocationBlockModifier.getMethodWriter();
54 opcode = ALOAD;
55 this.varIndex = varIndex;
56 this.parameterIndex = parameterIndex;
57 }
58
59
60
61
62
63
64 void generateCodeToStoreCapturedValue() {
65 if (opcode != ALOAD) {
66 mw.visitIntInsn(SIPUSH, parameterIndex);
67
68 if (typeToCapture == null) {
69 mw.visitInsn(ACONST_NULL);
70 } else {
71 mw.visitLdcInsn(typeToCapture);
72 }
73
74 invocationBlockModifier.generateCallToActiveInvocationsMethod("matchedArgument",
75 "(ILjava/lang/String;)Ljava/lang/Object;");
76
77 JavaType argType = getArgumentType();
78 generateCastOrUnboxing(mw, argType, opcode);
79
80 mw.visitVarInsn(opcode, varIndex);
81 }
82 }
83
84 @NonNull
85 private JavaType getArgumentType() {
86 if (typeToCapture == null) {
87 return invocationBlockModifier.argumentMatching.getParameterType(parameterIndex);
88 }
89
90 if (typeToCapture.charAt(0) == '[') {
91 return ArrayType.create(typeToCapture);
92 }
93
94 return ObjectType.create(typeToCapture);
95 }
96
97 boolean fixParameterIndex(@NonNegative int originalIndex, @NonNegative int newIndex) {
98 if (!parameterIndexFixed && parameterIndex == originalIndex) {
99 parameterIndex = newIndex;
100 parameterIndexFixed = true;
101 return true;
102 }
103
104 return false;
105 }
106
107 void generateCallToSetArgumentTypeIfNeeded() {
108 if (opcode == ALOAD) {
109 mw.visitIntInsn(SIPUSH, parameterIndex);
110 mw.visitLdcInsn(varIndex);
111 invocationBlockModifier.generateCallToActiveInvocationsMethod("setExpectedArgumentType", "(II)V");
112 } else if (typeToCapture != null && !isTypeToCaptureSameAsParameterType(typeToCapture)) {
113 mw.visitIntInsn(SIPUSH, parameterIndex);
114 mw.visitLdcInsn(typeToCapture);
115 invocationBlockModifier.generateCallToActiveInvocationsMethod("setExpectedArgumentType",
116 "(ILjava/lang/String;)V");
117 }
118 }
119
120 private boolean isTypeToCaptureSameAsParameterType(@NonNull String typeDesc) {
121 JavaType parameterType = invocationBlockModifier.argumentMatching.getParameterType(parameterIndex);
122
123 if (parameterType instanceof ReferenceType) {
124 String parameterTypeDesc = ((ReferenceType) parameterType).getInternalName();
125 return typeDesc.equals(parameterTypeDesc);
126 }
127
128 return isPrimitiveWrapper(typeDesc);
129 }
130 }