1
2
3
4
5
6 package mockit.internal.expectations.transformation;
7
8 import static mockit.asm.jvmConstants.Opcodes.GETFIELD;
9 import static mockit.asm.jvmConstants.Opcodes.SIPUSH;
10
11 import edu.umd.cs.findbugs.annotations.NonNull;
12
13 import mockit.asm.methods.MethodWriter;
14 import mockit.asm.types.JavaType;
15
16 import org.checkerframework.checker.index.qual.NonNegative;
17
18 final class ArgumentMatching {
19 private static final JavaType[] NO_PARAMETERS = {};
20 private static final String ANY_FIELDS = "any anyString anyInt anyBoolean anyLong anyDouble anyFloat anyChar anyShort anyByte";
21 private static final String WITH_METHODS = "with(Lmockit/Delegate;)Ljava/lang/Object; "
22 + "withAny(Ljava/lang/Object;)Ljava/lang/Object; "
23 + "withArgThat(Lorg/hamcrest/Matcher;)Ljava/lang/Object; "
24 + "withCapture()Ljava/lang/Object; withCapture(Ljava/util/List;)Ljava/lang/Object; "
25 + "withCapture(Ljava/lang/Object;)Ljava/util/List; "
26 + "withEqual(Ljava/lang/Object;)Ljava/lang/Object; withEqual(DD)D withEqual(FD)F "
27 + "withInstanceLike(Ljava/lang/Object;)Ljava/lang/Object; "
28 + "withInstanceOf(Ljava/lang/Class;)Ljava/lang/Object; "
29 + "withNotEqual(Ljava/lang/Object;)Ljava/lang/Object; "
30 + "withNull()Ljava/lang/Object; withNotNull()Ljava/lang/Object; "
31 + "withSameInstance(Ljava/lang/Object;)Ljava/lang/Object; "
32 + "withSubstring(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; "
33 + "withPrefix(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; "
34 + "withSuffix(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; "
35 + "withMatch(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;";
36
37 @NonNull
38 private final InvocationBlockModifier modifier;
39
40
41
42 @NonNull
43 private final int[] matcherStacks;
44 @NonNegative
45 private int matcherCount;
46 @NonNull
47 private JavaType[] parameterTypes;
48
49 static boolean isAnyField(@NonNull String name) {
50 return name.startsWith("any") && ANY_FIELDS.contains(name);
51 }
52
53 static boolean isCallToArgumentMatcher(@NonNull String name, @NonNull String desc) {
54 return name.startsWith("with") && WITH_METHODS.contains(name + desc);
55 }
56
57 ArgumentMatching(@NonNull InvocationBlockModifier modifier) {
58 this.modifier = modifier;
59 matcherStacks = new int[40];
60 parameterTypes = NO_PARAMETERS;
61 }
62
63 void addMatcher(@NonNegative int stackSize) {
64 matcherStacks[matcherCount++] = stackSize;
65 }
66
67 @NonNegative
68 int getMatcherCount() {
69 return matcherCount;
70 }
71
72 @NonNull
73 JavaType getParameterType(@NonNegative int parameterIndex) {
74 return parameterTypes[parameterIndex];
75 }
76
77 void generateCodeToAddArgumentMatcherForAnyField(@NonNull String fieldOwner, @NonNull String name,
78 @NonNull String desc) {
79 MethodWriter mw = modifier.getMethodWriter();
80 mw.visitFieldInsn(GETFIELD, fieldOwner, name, desc);
81 modifier.generateCallToActiveInvocationsMethod(name);
82 }
83
84 boolean handleInvocationParameters(@NonNegative int stackSize, @NonNull String desc) {
85 parameterTypes = JavaType.getArgumentTypes(desc);
86 int stackAfter = stackSize - getSumOfParameterSizes();
87 boolean mockedInvocationUsingTheMatchers = stackAfter < matcherStacks[0];
88
89 if (mockedInvocationUsingTheMatchers) {
90 generateCallsToMoveArgMatchers(stackAfter);
91 modifier.argumentCapturing.generateCallsToSetArgumentTypesToCaptureIfAny();
92 matcherCount = 0;
93 }
94
95 return mockedInvocationUsingTheMatchers;
96 }
97
98 @NonNegative
99 private int getSumOfParameterSizes() {
100 @NonNegative
101 int sum = 0;
102
103 for (JavaType argType : parameterTypes) {
104 sum += argType.getSize();
105 }
106
107 return sum;
108 }
109
110 private void generateCallsToMoveArgMatchers(@NonNegative int initialStack) {
111 @NonNegative
112 int stack = initialStack;
113 @NonNegative
114 int nextMatcher = 0;
115 @NonNegative
116 int matcherStack = matcherStacks[0];
117
118 for (int i = 0; i < parameterTypes.length && nextMatcher < matcherCount; i++) {
119 stack += parameterTypes[i].getSize();
120
121 if (stack == matcherStack || stack == matcherStack + 1) {
122 if (nextMatcher < i) {
123 generateCallToMoveArgMatcher(nextMatcher, i);
124 modifier.argumentCapturing.updateCaptureIfAny(nextMatcher, i);
125 }
126
127 nextMatcher++;
128 matcherStack = matcherStacks[nextMatcher];
129 }
130 }
131 }
132
133 private void generateCallToMoveArgMatcher(@NonNegative int originalMatcherIndex, @NonNegative int toIndex) {
134 MethodWriter mw = modifier.getMethodWriter();
135 mw.visitIntInsn(SIPUSH, originalMatcherIndex);
136 mw.visitIntInsn(SIPUSH, toIndex);
137 modifier.generateCallToActiveInvocationsMethod("moveArgMatcher", "(II)V");
138 }
139 }