WW-5674 perf(ognl): walk package names in place instead of building prefixes

Replaces the split/IntStream/String.join prefix construction with an index walk,
extracted into a pure package-private helper so it can be tested against package
name shapes no real Class can produce. Per call this drops a String[], a list
wrapper, a stream pipeline, N sublist views and N joined strings, leaving one
substring per package level.

Equivalence with the replaced implementation is asserted over a matrix of
package name shapes and candidate sets.
This commit is contained in:
Lukasz Lenart
2026-08-03 13:05:06 +02:00
parent a8f6b4c59d
commit 8237a1fd8b
2 changed files with 47 additions and 6 deletions
@@ -32,11 +32,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
import static java.text.MessageFormat.format;
import static java.util.Collections.emptySet;
@@ -393,10 +391,33 @@ public class SecurityMemberAccess implements MemberAccess {
}
public static boolean isClassBelongsToPackages(Class<?> clazz, Set<String> matchingPackages) {
List<String> packageParts = List.of(toPackageName(clazz).split("\\."));
return IntStream.range(0, packageParts.size())
.mapToObj(i -> String.join(".", packageParts.subList(0, i + 1)))
.anyMatch(matchingPackages::contains);
return isPackageBelongsToPackages(toPackageName(clazz), matchingPackages, emptySet());
}
/**
* Tests whether the given package name, or any of its parent packages, is present in either
* set. Walks the name in place rather than building the full prefix list, since this runs on
* the OGNL member-access path. Shortest prefix first, so broad entries such as {@code java.io}
* short-circuit earliest.
*
* @param packageName the package name to test, empty for the default package
* @param first the first set of package names to match against
* @param second the second set of package names to match against
* @return {@code true} if the package or any parent package is in either set
*/
static boolean isPackageBelongsToPackages(String packageName, Set<String> first, Set<String> second) {
if (first.isEmpty() && second.isEmpty()) {
return false;
}
int idx = packageName.indexOf('.');
while (idx != -1) {
String prefix = packageName.substring(0, idx);
if (first.contains(prefix) || second.contains(prefix)) {
return true;
}
idx = packageName.indexOf('.', idx + 1);
}
return first.contains(packageName) || second.contains(packageName);
}
protected boolean isClassExcluded(Class<?> clazz) {
@@ -175,4 +175,24 @@ public class SecurityMemberAccessPackageMatchingTest {
}
}
}
@Test
public void indexWalkMatchesLegacyAcrossPackageNameShapes() {
for (String packageName : PACKAGE_NAMES) {
for (Set<String> candidates : CANDIDATE_SETS) {
assertThat(SecurityMemberAccess.isPackageBelongsToPackages(packageName, candidates, emptySet()))
.as("packageName=[%s] candidates=%s", packageName, candidates)
.isEqualTo(legacyPrefixMatch(packageName, candidates));
}
}
}
@Test
public void bothSetsEmptyShortCircuitsToFalse() {
for (String packageName : PACKAGE_NAMES) {
assertThat(SecurityMemberAccess.isPackageBelongsToPackages(packageName, emptySet(), emptySet()))
.as("packageName=[%s] with no configured packages", packageName)
.isFalse();
}
}
}