From 2c42df260f9e9392c1adbb7f340e9c5384aa3e80 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 12:21:05 +0200 Subject: [PATCH 1/9] TypeMatcher is a FunctionalInterface --- .../src/main/java/datadog/instrument/classmatch/TypeMatcher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/TypeMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/TypeMatcher.java index af1ea37..54eedaa 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/TypeMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/TypeMatcher.java @@ -13,6 +13,7 @@ import java.util.function.Predicate; /** Fluent-API for building type hierarchy predicates. */ +@FunctionalInterface public interface TypeMatcher extends Predicate { /** From 41888b72c4c50c28a9068052296d458d6f47a5d9 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 14:43:34 +0200 Subject: [PATCH 2/9] Provide strong-type form of 'not(...)' --- .../classmatch/StandardMatchers.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java b/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java index 1fefda2..3c87d9a 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java @@ -62,6 +62,46 @@ public static IntPredicate not(IntPredicate predicate) { return predicate.negate(); } + /** + * Negates the given type matcher. + * + * @param matcher the matcher to negate + * @return negation of the matcher + */ + public static TypeMatcher not(TypeMatcher matcher) { + return cs -> !matcher.test(cs); + } + + /** + * Negates the given class matcher. + * + * @param matcher the matcher to negate + * @return negation of the matcher + */ + public static ClassMatcher not(ClassMatcher matcher) { + return c -> !matcher.test(c); + } + + /** + * Negates the given field matcher. + * + * @param matcher the matcher to negate + * @return negation of the matcher + */ + public static FieldMatcher not(FieldMatcher matcher) { + return f -> !matcher.test(f); + } + + /** + * Negates the given method matcher. + * + * @param matcher the matcher to negate + * @return negation of the matcher + */ + public static MethodMatcher not(MethodMatcher matcher) { + return m -> !matcher.test(m); + } + /** * Syntactic sugar around {@link Predicate#negate()}. * From 55c3bba050ad536b1adecb31f7abdd6ef024f074 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 17:19:18 +0200 Subject: [PATCH 3/9] Introduce AccessMatcher functional type --- .../instrument/classmatch/AccessMatcher.java | 73 +++++++++++++++++++ .../instrument/classmatch/ClassMatcher.java | 5 +- .../instrument/classmatch/FieldMatcher.java | 3 +- .../instrument/classmatch/MethodMatcher.java | 3 +- .../classmatch/StandardMatchers.java | 45 ++---------- 5 files changed, 82 insertions(+), 47 deletions(-) create mode 100644 class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java diff --git a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java new file mode 100644 index 0000000..0f21eb3 --- /dev/null +++ b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java @@ -0,0 +1,73 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2025-Present Datadog, Inc. + */ + +package datadog.instrument.classmatch; + +import java.lang.reflect.Modifier; +import java.util.function.IntPredicate; + +/** + * Predicate for matching access modifiers defined in class-files. + */ +@FunctionalInterface +public interface AccessMatcher extends IntPredicate { + + /** Matches public access. */ + AccessMatcher PUBLIC = Modifier::isPublic; + + /** Matches private access. */ + AccessMatcher PRIVATE = Modifier::isPrivate; + + /** Matches protected access. */ + AccessMatcher PROTECTED = Modifier::isProtected; + + /** Matches static access. */ + AccessMatcher STATIC = Modifier::isStatic; + + /** Matches final classes/methods/fields. */ + AccessMatcher FINAL = Modifier::isFinal; + + /** Matches synchronized methods. */ + AccessMatcher SYNCHRONIZED = Modifier::isSynchronized; + + /** Matches volatile fields. */ + AccessMatcher VOLATILE = Modifier::isVolatile; + + /** Matches transient fields. */ + AccessMatcher TRANSIENT = Modifier::isTransient; + + /** Matches native methods. */ + AccessMatcher NATIVE = Modifier::isNative; + + /** Matches interface classes. */ + AccessMatcher INTERFACE = Modifier::isInterface; + + /** Matches abstract classes. */ + AccessMatcher ABSTRACT = Modifier::isAbstract; + + /** + * Conjunction of this matcher AND another. + * + * @param other the other matcher + * @return conjunction of both matchers + */ + default AccessMatcher and(AccessMatcher other) { + // simple approach as we don't expect many access-matcher unions + return acc -> test(acc) && other.test(acc); + } + + /** + * Disjunction of this matcher OR another. + * + * @param other the other matcher + * @return disjunction of both matchers + */ + default AccessMatcher or(AccessMatcher other) { + // simple approach as we don't expect many access-matcher unions + return acc -> test(acc) || other.test(acc); + } +} diff --git a/class-match/src/main/java/datadog/instrument/classmatch/ClassMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/ClassMatcher.java index 837d66a..7aed30e 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/ClassMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/ClassMatcher.java @@ -12,7 +12,6 @@ import static java.util.Arrays.asList; import java.util.Collection; -import java.util.function.IntPredicate; import java.util.function.Predicate; /** Fluent-API for building {@link ClassOutline} predicates. */ @@ -36,7 +35,7 @@ static ClassMatcher declares(FieldMatcher fieldMatcher) { * @param fieldMatcher the field matcher * @return matcher of classes with a matching field */ - static ClassMatcher declares(IntPredicate accessMatcher, FieldMatcher fieldMatcher) { + static ClassMatcher declares(AccessMatcher accessMatcher, FieldMatcher fieldMatcher) { FieldMatcher combinedMatcher = fieldMatcher.access(accessMatcher); return c -> anyMatch(c.fields, combinedMatcher); } @@ -58,7 +57,7 @@ static ClassMatcher declares(MethodMatcher methodMatcher) { * @param methodMatcher the method matcher * @return matcher of classes with a matching method */ - static ClassMatcher declares(IntPredicate accessMatcher, MethodMatcher methodMatcher) { + static ClassMatcher declares(AccessMatcher accessMatcher, MethodMatcher methodMatcher) { MethodMatcher combinedMatcher = methodMatcher.access(accessMatcher); return c -> anyMatch(c.methods, combinedMatcher); } diff --git a/class-match/src/main/java/datadog/instrument/classmatch/FieldMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/FieldMatcher.java index fe4c108..742b375 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/FieldMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/FieldMatcher.java @@ -8,7 +8,6 @@ import static datadog.instrument.classmatch.InternalMatchers.descriptor; -import java.util.function.IntPredicate; import java.util.function.Predicate; /** Fluent-API for building {@link FieldOutline} predicates. */ @@ -41,7 +40,7 @@ static FieldMatcher field(Predicate nameMatcher) { * @param accessMatcher the access matcher * @return matcher of fields with matching access */ - default FieldMatcher access(IntPredicate accessMatcher) { + default FieldMatcher access(AccessMatcher accessMatcher) { return f -> test(f) && accessMatcher.test(f.access); } diff --git a/class-match/src/main/java/datadog/instrument/classmatch/MethodMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/MethodMatcher.java index 7e9a8eb..7235bd0 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/MethodMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/MethodMatcher.java @@ -16,7 +16,6 @@ import static java.util.Arrays.asList; import java.util.Collection; -import java.util.function.IntPredicate; import java.util.function.Predicate; /** Fluent-API for building {@link MethodOutline} predicates. */ @@ -76,7 +75,7 @@ static MethodMatcher staticInitializer() { * @param accessMatcher the access matcher * @return matcher of methods with matching access */ - default MethodMatcher access(IntPredicate accessMatcher) { + default MethodMatcher access(AccessMatcher accessMatcher) { return and(m -> accessMatcher.test(m.access)); } diff --git a/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java b/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java index 3c87d9a..8d7341b 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java @@ -8,58 +8,23 @@ import static java.util.Arrays.asList; -import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashSet; -import java.util.function.IntPredicate; import java.util.function.Predicate; /** Standard matchers, part of the public API. */ public final class StandardMatchers { - /** Matches public access. */ - public static final IntPredicate PUBLIC = Modifier::isPublic; - - /** Matches private access. */ - public static final IntPredicate PRIVATE = Modifier::isPrivate; - - /** Matches protected access. */ - public static final IntPredicate PROTECTED = Modifier::isProtected; - - /** Matches static access. */ - public static final IntPredicate STATIC = Modifier::isStatic; - - /** Matches final classes/methods/fields. */ - public static final IntPredicate FINAL = Modifier::isFinal; - - /** Matches synchronized methods. */ - public static final IntPredicate SYNCHRONIZED = Modifier::isSynchronized; - - /** Matches volatile fields. */ - public static final IntPredicate VOLATILE = Modifier::isVolatile; - - /** Matches transient fields. */ - public static final IntPredicate TRANSIENT = Modifier::isTransient; - - /** Matches native methods. */ - public static final IntPredicate NATIVE = Modifier::isNative; - - /** Matches interface classes. */ - public static final IntPredicate INTERFACE = Modifier::isInterface; - - /** Matches abstract classes. */ - public static final IntPredicate ABSTRACT = Modifier::isAbstract; - private StandardMatchers() {} /** - * Syntactic sugar around {@link IntPredicate#negate()}. + * Negates the given access matcher. * - * @param predicate the predicate to negate - * @return negated predicate + * @param matcher the matcher to negate + * @return negation of the matcher */ - public static IntPredicate not(IntPredicate predicate) { - return predicate.negate(); + public static AccessMatcher not(AccessMatcher matcher) { + return acc -> !matcher.test(acc); } /** From 99cfc6dd90e71dfed0d8bb7d86cd8236513f54a1 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 17:52:37 +0200 Subject: [PATCH 4/9] Consistent ordering --- .../classmatch/StandardMatchers.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java b/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java index 8d7341b..958551e 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/StandardMatchers.java @@ -17,16 +17,6 @@ public final class StandardMatchers { private StandardMatchers() {} - /** - * Negates the given access matcher. - * - * @param matcher the matcher to negate - * @return negation of the matcher - */ - public static AccessMatcher not(AccessMatcher matcher) { - return acc -> !matcher.test(acc); - } - /** * Negates the given type matcher. * @@ -67,6 +57,16 @@ public static MethodMatcher not(MethodMatcher matcher) { return m -> !matcher.test(m); } + /** + * Negates the given access matcher. + * + * @param matcher the matcher to negate + * @return negation of the matcher + */ + public static AccessMatcher not(AccessMatcher matcher) { + return acc -> !matcher.test(acc); + } + /** * Syntactic sugar around {@link Predicate#negate()}. * From bb27bca7506ffee8fb05ca82c085c5c59b4da477 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 18:16:16 +0200 Subject: [PATCH 5/9] Add some common 'negative' access matchers --- .../datadog/instrument/classmatch/AccessMatcher.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java index 0f21eb3..45a76e3 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java @@ -28,9 +28,15 @@ public interface AccessMatcher extends IntPredicate { /** Matches static access. */ AccessMatcher STATIC = Modifier::isStatic; + /** Matches non-static access. */ + AccessMatcher NON_STATIC = acc -> (acc & Modifier.STATIC) == 0; + /** Matches final classes/methods/fields. */ AccessMatcher FINAL = Modifier::isFinal; + /** Matches non-final classes/methods/fields. */ + AccessMatcher NON_FINAL = acc -> (acc & Modifier.FINAL) == 0; + /** Matches synchronized methods. */ AccessMatcher SYNCHRONIZED = Modifier::isSynchronized; @@ -46,9 +52,15 @@ public interface AccessMatcher extends IntPredicate { /** Matches interface classes. */ AccessMatcher INTERFACE = Modifier::isInterface; + /** Matches non-interface classes. */ + AccessMatcher CLASS = acc -> (acc & Modifier.INTERFACE) == 0; + /** Matches abstract classes. */ AccessMatcher ABSTRACT = Modifier::isAbstract; + /** Matches concrete (constructable) classes. */ + AccessMatcher CONCRETE = acc -> (acc & Modifier.ABSTRACT) == 0; + /** * Conjunction of this matcher AND another. * From 7add8cb59b661fcf0989984f4e67d9a753559077 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 18:24:02 +0200 Subject: [PATCH 6/9] Cleanup --- .../java/datadog/instrument/classmatch/AccessMatcher.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java index 45a76e3..698313d 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java @@ -52,15 +52,9 @@ public interface AccessMatcher extends IntPredicate { /** Matches interface classes. */ AccessMatcher INTERFACE = Modifier::isInterface; - /** Matches non-interface classes. */ - AccessMatcher CLASS = acc -> (acc & Modifier.INTERFACE) == 0; - - /** Matches abstract classes. */ + /** Matches abstract classes/methods. */ AccessMatcher ABSTRACT = Modifier::isAbstract; - /** Matches concrete (constructable) classes. */ - AccessMatcher CONCRETE = acc -> (acc & Modifier.ABSTRACT) == 0; - /** * Conjunction of this matcher AND another. * From 13d76fcf11881ca487724da1a26ea482d3a60bd3 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 18:25:58 +0200 Subject: [PATCH 7/9] Add some common 'negative' access matchers --- .../main/java/datadog/instrument/classmatch/AccessMatcher.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java index 698313d..35b43d4 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java @@ -55,6 +55,9 @@ public interface AccessMatcher extends IntPredicate { /** Matches abstract classes/methods. */ AccessMatcher ABSTRACT = Modifier::isAbstract; + /** Matches non-abstract classes/methods. */ + AccessMatcher NON_ABSTRACT = acc -> (acc & Modifier.ABSTRACT) == 0; + /** * Conjunction of this matcher AND another. * From 5e6a4cb01812890aa373c75249cc8b21a217f6c8 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 21:35:14 +0200 Subject: [PATCH 8/9] Add some common method matchers --- .../datadog/instrument/classmatch/AccessMatcher.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java index 35b43d4..1142baf 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java @@ -25,6 +25,10 @@ public interface AccessMatcher extends IntPredicate { /** Matches protected access. */ AccessMatcher PROTECTED = Modifier::isProtected; + /** Matches package-private access. */ + AccessMatcher PACKAGE_PRIVATE = + acc -> (acc & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) == 0; + /** Matches static access. */ AccessMatcher STATIC = Modifier::isStatic; @@ -40,15 +44,15 @@ public interface AccessMatcher extends IntPredicate { /** Matches synchronized methods. */ AccessMatcher SYNCHRONIZED = Modifier::isSynchronized; + /** Matches varargs methods. */ + AccessMatcher VARARGS = acc -> (acc & 0x0080) != 0; + /** Matches volatile fields. */ AccessMatcher VOLATILE = Modifier::isVolatile; /** Matches transient fields. */ AccessMatcher TRANSIENT = Modifier::isTransient; - /** Matches native methods. */ - AccessMatcher NATIVE = Modifier::isNative; - /** Matches interface classes. */ AccessMatcher INTERFACE = Modifier::isInterface; From 1f832f9d7bbadbca1e09a62cb7b9935e20f40187 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 9 Oct 2025 23:46:23 +0200 Subject: [PATCH 9/9] Cleanup access matchers --- .../datadog/instrument/classmatch/AccessMatcher.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java index 1142baf..0015df6 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/AccessMatcher.java @@ -29,11 +29,11 @@ public interface AccessMatcher extends IntPredicate { AccessMatcher PACKAGE_PRIVATE = acc -> (acc & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) == 0; - /** Matches static access. */ + /** Matches static methods/fields. */ AccessMatcher STATIC = Modifier::isStatic; - /** Matches non-static access. */ - AccessMatcher NON_STATIC = acc -> (acc & Modifier.STATIC) == 0; + /** Matches non-static methods/fields. */ + AccessMatcher INSTANCE = acc -> (acc & Modifier.STATIC) == 0; /** Matches final classes/methods/fields. */ AccessMatcher FINAL = Modifier::isFinal; @@ -56,11 +56,14 @@ public interface AccessMatcher extends IntPredicate { /** Matches interface classes. */ AccessMatcher INTERFACE = Modifier::isInterface; + /** Matches non-interface classes. */ + AccessMatcher CLASS = acc -> (acc & Modifier.INTERFACE) == 0; + /** Matches abstract classes/methods. */ AccessMatcher ABSTRACT = Modifier::isAbstract; /** Matches non-abstract classes/methods. */ - AccessMatcher NON_ABSTRACT = acc -> (acc & Modifier.ABSTRACT) == 0; + AccessMatcher CONCRETE = acc -> (acc & Modifier.ABSTRACT) == 0; /** * Conjunction of this matcher AND another.