Skip to content

Commit d0a8320

Browse files
committed
Support custom matching of field/parameter/return types
1 parent eff8174 commit d0a8320

File tree

3 files changed

+121
-36
lines changed

3 files changed

+121
-36
lines changed

class-match/src/main/java/datadog/instrument/classmatch/FieldMatcher.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package datadog.instrument.classmatch;
88

99
import static datadog.instrument.classmatch.InternalMatchers.descriptor;
10+
import static datadog.instrument.classmatch.InternalMatchers.hasFieldType;
1011

1112
import java.util.function.IntPredicate;
1213
import java.util.function.Predicate;
@@ -41,32 +42,42 @@ static FieldMatcher field(Predicate<String> nameMatcher) {
4142
* @param accessMatcher the access matcher
4243
* @return matcher of fields with matching access
4344
*/
44-
default FieldMatcher withAccess(IntPredicate accessMatcher) {
45+
default FieldMatcher access(IntPredicate accessMatcher) {
4546
return and(f -> accessMatcher.test(f.access));
4647
}
4748

4849
/**
49-
* Matches fields of the given type.
50+
* Matches fields with the given type.
5051
*
5152
* @param type the field type
5253
* @return matcher of fields with the same type
5354
*/
54-
default FieldMatcher ofType(String type) {
55+
default FieldMatcher type(String type) {
5556
String descriptor = descriptor(type);
5657
return and(f -> descriptor.equals(f.descriptor));
5758
}
5859

5960
/**
60-
* Matches fields of the given type.
61+
* Matches fields with the given type.
6162
*
6263
* @param type the field type
6364
* @return matcher of fields with the same type
6465
*/
65-
default FieldMatcher ofType(Class<?> type) {
66+
default FieldMatcher type(Class<?> type) {
6667
String descriptor = descriptor(type);
6768
return and(f -> descriptor.equals(f.descriptor));
6869
}
6970

71+
/**
72+
* Matches fields with a type matching the given criteria.
73+
*
74+
* @param typeMatcher the field type matcher
75+
* @return matcher of fields with a matching type
76+
*/
77+
default FieldMatcher type(Predicate<String> typeMatcher) {
78+
return and(f -> hasFieldType(f, typeMatcher));
79+
}
80+
7081
/**
7182
* Conjunction of this matcher AND another.
7283
*

class-match/src/main/java/datadog/instrument/classmatch/InternalMatchers.java

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,51 @@ static Predicate<String[]> declaresAnnotationOneOf(Collection<String> names) {
3939
return annotations -> anyMatch(annotations, annotationNamedOneOf);
4040
}
4141

42-
/** Returns {@code true} if the parameter declared at the given index has the given descriptor. */
43-
static boolean declaresParameter(MethodOutline method, int paramIndex, String paramDescriptor) {
44-
int offset;
45-
if (paramIndex > 0) {
46-
// boundaries covers start of second parameter, to start of return descriptor
47-
int[] boundaries = method.descriptorBoundaries();
48-
if (paramIndex >= boundaries.length) {
49-
return false; // don't match against the return descriptor boundary
50-
}
51-
offset = boundaries[paramIndex - 1];
52-
} else {
53-
// no need to parse method descriptor, just check string at position 1 matches
54-
offset = 1;
42+
/**
43+
* Does the method's descriptor contain the parameter descriptor at the given index?
44+
*
45+
* <p>Only for indices of 1 or above; index 0 uses a separate (faster) approach.
46+
*/
47+
static boolean hasParamDescriptor(MethodOutline method, int paramIndex, String paramDescriptor) {
48+
// boundaries covers start of second parameter, to start of return descriptor
49+
int[] boundaries = method.descriptorBoundaries();
50+
return paramIndex < boundaries.length // ignore return descriptor boundary
51+
&& method.descriptor.startsWith(paramDescriptor, boundaries[paramIndex - 1]);
52+
}
53+
54+
/** Does the method's descriptor contain a matching parameter type at the given index? */
55+
static boolean hasParamType(MethodOutline method, int paramIndex, Predicate<String> typeMatcher) {
56+
// boundaries covers start of second parameter, to start of return descriptor
57+
int[] boundaries = method.descriptorBoundaries();
58+
if (paramIndex >= boundaries.length) { // ignore return descriptor boundary
59+
return false;
5560
}
56-
return method.descriptor.regionMatches(offset, paramDescriptor, 0, paramDescriptor.length());
61+
// first parameter always starts at 1, for the rest check the boundary list
62+
int from = paramIndex == 0 ? 1 : boundaries[paramIndex - 1];
63+
String descriptor = method.descriptor;
64+
// extract type from "L...;" descriptor string, ignore primitive/array types
65+
return descriptor.charAt(from) == 'L'
66+
&& typeMatcher.test(descriptor.substring(from + 1, boundaries[paramIndex] - 1));
67+
}
68+
69+
/** Does the method's descriptor contain a matching return type? */
70+
static boolean hasReturnType(MethodOutline method, Predicate<String> typeMatcher) {
71+
// boundaries covers start of second parameter, to start of return descriptor
72+
int[] boundaries = method.descriptorBoundaries();
73+
// return type starts at 2 if no parameters, otherwise check last boundary
74+
int from = boundaries.length == 0 ? 2 : boundaries[boundaries.length - 1];
75+
String descriptor = method.descriptor;
76+
// extract type from "L...;" descriptor string, ignore primitive/array types
77+
return descriptor.charAt(from) == 'L'
78+
&& typeMatcher.test(descriptor.substring(from + 1, descriptor.length() - 1));
79+
}
80+
81+
/** Does the field's descriptor contain a matching type? */
82+
static boolean hasFieldType(FieldOutline field, Predicate<String> typeMatcher) {
83+
String descriptor = field.descriptor;
84+
// extract type from "L...;" descriptor string, ignore primitive/array types
85+
return descriptor.charAt(0) == 'L'
86+
&& typeMatcher.test(descriptor.substring(1, descriptor.length() - 1));
5787
}
5888

5989
/** Returns the descriptor for the given type. */
@@ -245,7 +275,7 @@ abstract static class MatcherUnion<M> {
245275
int rhsLength =
246276
unionType.isInstance(rhs) ? (rhsArray = ((MatcherUnion<M>) rhs).matchers).length : 1;
247277

248-
// only expand array when either side is a nested union of the same type
278+
// expand array if either side is a nested union of the same type
249279
int expandedLength = lhsLength + rhsLength;
250280
if (expandedLength > 2) {
251281
// original array should only have 2 elements, tolerate if there's extra

class-match/src/main/java/datadog/instrument/classmatch/MethodMatcher.java

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import static datadog.instrument.classmatch.InternalMatchers.ALL_METHODS;
1010
import static datadog.instrument.classmatch.InternalMatchers.declaresAnnotation;
1111
import static datadog.instrument.classmatch.InternalMatchers.declaresAnnotationOneOf;
12-
import static datadog.instrument.classmatch.InternalMatchers.declaresParameter;
1312
import static datadog.instrument.classmatch.InternalMatchers.descriptor;
13+
import static datadog.instrument.classmatch.InternalMatchers.hasParamDescriptor;
14+
import static datadog.instrument.classmatch.InternalMatchers.hasParamType;
15+
import static datadog.instrument.classmatch.InternalMatchers.hasReturnType;
1416
import static java.util.Arrays.asList;
1517

1618
import java.util.Collection;
@@ -74,18 +76,31 @@ static MethodMatcher staticInitializer() {
7476
* @param accessMatcher the access matcher
7577
* @return matcher of methods with matching access
7678
*/
77-
default MethodMatcher withAccess(IntPredicate accessMatcher) {
79+
default MethodMatcher access(IntPredicate accessMatcher) {
7880
return and(m -> accessMatcher.test(m.access));
7981
}
8082

83+
/**
84+
* Matches methods with no parameters.
85+
*
86+
* @return matcher of methods with no parameters
87+
*/
88+
default MethodMatcher noParameters() {
89+
return and(m -> m.descriptor.charAt(1) == ')');
90+
}
91+
8192
/**
8293
* Matches methods with the given parameter count.
8394
*
8495
* @param paramCount the parameter count
8596
* @return matcher of methods with the same parameter count
8697
*/
87-
default MethodMatcher withParameters(int paramCount) {
88-
return and(m -> m.descriptorBoundaries().length == paramCount);
98+
default MethodMatcher parameters(int paramCount) {
99+
if (paramCount == 0) {
100+
return noParameters();
101+
} else {
102+
return and(m -> m.descriptorBoundaries().length == paramCount);
103+
}
89104
}
90105

91106
/**
@@ -94,13 +109,13 @@ default MethodMatcher withParameters(int paramCount) {
94109
* @param paramTypes the parameter types
95110
* @return matcher of methods with the same parameter types
96111
*/
97-
default MethodMatcher withParameters(String... paramTypes) {
112+
default MethodMatcher parameters(String... paramTypes) {
98113
StringBuilder buf = new StringBuilder().append('(');
99114
for (String paramType : paramTypes) {
100115
buf.append(descriptor(paramType));
101116
}
102-
String descriptorPrefix = buf.append(')').toString();
103-
return and(m -> m.descriptor.startsWith(descriptorPrefix));
117+
String prefix = buf.append(')').toString();
118+
return and(m -> m.descriptor.startsWith(prefix));
104119
}
105120

106121
/**
@@ -109,13 +124,13 @@ default MethodMatcher withParameters(String... paramTypes) {
109124
* @param paramTypes the parameter types
110125
* @return matcher of methods with the same parameter types
111126
*/
112-
default MethodMatcher withParameters(Class<?>... paramTypes) {
127+
default MethodMatcher parameters(Class<?>... paramTypes) {
113128
StringBuilder buf = new StringBuilder().append('(');
114129
for (Class<?> paramType : paramTypes) {
115130
buf.append(descriptor(paramType));
116131
}
117-
String descriptorPrefix = buf.append(')').toString();
118-
return and(m -> m.descriptor.startsWith(descriptorPrefix));
132+
String prefix = buf.append(')').toString();
133+
return and(m -> m.descriptor.startsWith(prefix));
119134
}
120135

121136
/**
@@ -125,9 +140,13 @@ default MethodMatcher withParameters(Class<?>... paramTypes) {
125140
* @param paramType the parameter type
126141
* @return matcher of methods with the same parameter type at the same position
127142
*/
128-
default MethodMatcher withParameter(int paramIndex, String paramType) {
143+
default MethodMatcher parameter(int paramIndex, String paramType) {
129144
String paramDescriptor = descriptor(paramType);
130-
return and(m -> declaresParameter(m, paramIndex, paramDescriptor));
145+
if (paramIndex == 0) {
146+
return and(m -> m.descriptor.startsWith(paramDescriptor, 1));
147+
} else {
148+
return and(m -> hasParamDescriptor(m, paramIndex, paramDescriptor));
149+
}
131150
}
132151

133152
/**
@@ -137,9 +156,24 @@ default MethodMatcher withParameter(int paramIndex, String paramType) {
137156
* @param paramType the parameter type
138157
* @return matcher of methods with the same parameter type at the same position
139158
*/
140-
default MethodMatcher withParameter(int paramIndex, Class<?> paramType) {
159+
default MethodMatcher parameter(int paramIndex, Class<?> paramType) {
141160
String paramDescriptor = descriptor(paramType);
142-
return and(m -> declaresParameter(m, paramIndex, paramDescriptor));
161+
if (paramIndex == 0) {
162+
return and(m -> m.descriptor.startsWith(paramDescriptor, 1));
163+
} else {
164+
return and(m -> hasParamDescriptor(m, paramIndex, paramDescriptor));
165+
}
166+
}
167+
168+
/**
169+
* Matches methods with a parameter type matching the given criteria.
170+
*
171+
* @param paramIndex the parameter index
172+
* @param typeMatcher the parameter type matcher
173+
* @return matcher of methods with a matching parameter type
174+
*/
175+
default MethodMatcher parameter(int paramIndex, Predicate<String> typeMatcher) {
176+
return and(m -> hasParamType(m, paramIndex, typeMatcher));
143177
}
144178

145179
/**
@@ -149,7 +183,7 @@ default MethodMatcher withParameter(int paramIndex, Class<?> paramType) {
149183
* @return matcher of methods returning the same type
150184
*/
151185
default MethodMatcher returning(String returnType) {
152-
String returnDescriptor = ')' + descriptor(returnType);
186+
String returnDescriptor = descriptor(returnType);
153187
return and(m -> m.descriptor.endsWith(returnDescriptor));
154188
}
155189

@@ -160,10 +194,20 @@ default MethodMatcher returning(String returnType) {
160194
* @return matcher of methods returning the same type
161195
*/
162196
default MethodMatcher returning(Class<?> returnType) {
163-
String returnDescriptor = ')' + descriptor(returnType);
197+
String returnDescriptor = descriptor(returnType);
164198
return and(m -> m.descriptor.endsWith(returnDescriptor));
165199
}
166200

201+
/**
202+
* Matches methods returning a type matching the given criteria.
203+
*
204+
* @param typeMatcher the return type matcher
205+
* @return matcher of methods with a matching return type
206+
*/
207+
default MethodMatcher returning(Predicate<String> typeMatcher) {
208+
return and(m -> hasReturnType(m, typeMatcher));
209+
}
210+
167211
/**
168212
* Matches methods annotated with the given type.
169213
*

0 commit comments

Comments
 (0)