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