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