Skip to content

Commit e873715

Browse files
committed
Fix detection of protected generic parameter
This commit fixes the algorithm used to analyze a generic parameter. If a type in the generic signature is protected, the type is return rather than the full signature. This makes sure that the appropriate package is used. Previously, it would have incorrectly used the type of the raw class. Using a generic type for such a use case is wrong, and ProtectedElement has been updated to expose a `Class` rather than a `ResolvableType`. See gh-28030
1 parent 9b07457 commit e873715

File tree

4 files changed

+68
-31
lines changed

4 files changed

+68
-31
lines changed

spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccess.java

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public String getPrivilegedPackageName(String packageName) throws ProtectedAcces
7474
return null;
7575
}
7676
List<String> packageNames = protectedElements.stream()
77-
.map(element -> element.getType().toClass().getPackageName())
77+
.map(element -> element.getType().getPackageName())
7878
.distinct().toList();
7979
if (packageNames.size() == 1) {
8080
return packageNames.get(0);
@@ -86,7 +86,7 @@ public String getPrivilegedPackageName(String packageName) throws ProtectedAcces
8686
private List<ProtectedElement> getProtectedElements(String packageName) {
8787
List<ProtectedElement> matches = new ArrayList<>();
8888
for (ProtectedElement element : this.elements) {
89-
if (!element.getType().toClass().getPackage().getName().equals(packageName)) {
89+
if (!element.getType().getPackage().getName().equals(packageName)) {
9090
matches.add(element);
9191
}
9292
}
@@ -99,8 +99,9 @@ private List<ProtectedElement> getProtectedElements(String packageName) {
9999
* @param type the type to analyze
100100
*/
101101
public void analyze(ResolvableType type) {
102-
if (isProtected(type)) {
103-
registerProtectedType(type, null);
102+
Class<?> protectedType = isProtected(type);
103+
if (protectedType != null) {
104+
registerProtectedType(protectedType, null);
104105
}
105106
}
106107

@@ -119,8 +120,9 @@ public void analyze(Member member, Options options) {
119120
}
120121
if (member instanceof Field field) {
121122
ResolvableType fieldType = ResolvableType.forField(field);
122-
if (isProtected(fieldType) && options.assignReturnType.apply(field)) {
123-
registerProtectedType(fieldType, field);
123+
Class<?> protectedType = isProtected(fieldType);
124+
if (protectedType != null && options.assignReturnType.apply(field)) {
125+
registerProtectedType(protectedType, field);
124126
}
125127
}
126128
else if (member instanceof Constructor<?> constructor) {
@@ -129,8 +131,9 @@ else if (member instanceof Constructor<?> constructor) {
129131
}
130132
else if (member instanceof Method method) {
131133
ResolvableType returnType = ResolvableType.forMethodReturnType(method);
132-
if (isProtected(returnType) && options.assignReturnType.apply(method)) {
133-
registerProtectedType(returnType, method);
134+
Class<?> protectedType = isProtected(returnType);
135+
if (protectedType != null && options.assignReturnType.apply(method)) {
136+
registerProtectedType(protectedType, method);
134137
}
135138
analyzeParameterTypes(method, i -> ResolvableType.forMethodParameter(method, i));
136139
}
@@ -141,37 +144,41 @@ private void analyzeParameterTypes(Executable executable, Function<Integer,
141144

142145
for (int i = 0; i < executable.getParameters().length; i++) {
143146
ResolvableType parameterType = parameterTypeFactory.apply(i);
144-
if (isProtected(parameterType)) {
145-
registerProtectedType(parameterType, executable);
147+
Class<?> protectedType = isProtected(parameterType);
148+
if (protectedType != null) {
149+
registerProtectedType(protectedType, executable);
146150
}
147151
}
148152
}
149153

150-
boolean isProtected(ResolvableType resolvableType) {
154+
@Nullable
155+
Class<?> isProtected(ResolvableType resolvableType) {
151156
return isProtected(new HashSet<>(), resolvableType);
152157
}
153158

154-
private boolean isProtected(Set<ResolvableType> seen, ResolvableType target) {
159+
@Nullable
160+
private Class<?> isProtected(Set<ResolvableType> seen, ResolvableType target) {
155161
if (seen.contains(target)) {
156-
return false;
162+
return null;
157163
}
158164
seen.add(target);
159165
ResolvableType nonProxyTarget = target.as(ClassUtils.getUserClass(target.toClass()));
160-
if (isProtected(nonProxyTarget.toClass())) {
161-
return true;
166+
Class<?> rawClass = nonProxyTarget.toClass();
167+
if (isProtected(rawClass)) {
168+
return rawClass;
162169
}
163-
Class<?> declaringClass = nonProxyTarget.toClass().getDeclaringClass();
170+
Class<?> declaringClass = rawClass.getDeclaringClass();
164171
if (declaringClass != null) {
165172
if (isProtected(declaringClass)) {
166-
return true;
173+
return declaringClass;
167174
}
168175
}
169176
if (nonProxyTarget.hasGenerics()) {
170177
for (ResolvableType generic : nonProxyTarget.getGenerics()) {
171178
return isProtected(seen, generic);
172179
}
173180
}
174-
return false;
181+
return null;
175182
}
176183

177184
private boolean isProtected(Class<?> type) {
@@ -183,14 +190,10 @@ private boolean isProtected(int modifiers) {
183190
return !Modifier.isPublic(modifiers);
184191
}
185192

186-
private void registerProtectedType(ResolvableType type, @Nullable Member member) {
193+
private void registerProtectedType(Class<?> type, @Nullable Member member) {
187194
this.elements.add(ProtectedElement.of(type, member));
188195
}
189196

190-
private void registerProtectedType(Class<?> type, Member member) {
191-
registerProtectedType(ResolvableType.forClass(type), member);
192-
}
193-
194197
/**
195198
* Options to use to analyze if invoking a {@link Member} requires
196199
* privileged access.

spring-core/src/main/java/org/springframework/aot/generator/ProtectedElement.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.lang.reflect.Member;
2020

21-
import org.springframework.core.ResolvableType;
2221
import org.springframework.lang.Nullable;
2322

2423
/**
@@ -29,25 +28,27 @@
2928
*/
3029
public final class ProtectedElement {
3130

32-
private final ResolvableType type;
31+
private final Class<?> type;
3332

3433
@Nullable
3534
private final Member target;
3635

3736

38-
private ProtectedElement(ResolvableType type, @Nullable Member member) {
37+
private ProtectedElement(Class<?> type, @Nullable Member member) {
3938
this.type = type;
4039
this.target = member;
4140
}
4241

4342
/**
44-
* Return the {@link ResolvableType type} that is non-public. For a plain
43+
* Return the {@link Class type} that is non-public. For a plain
4544
* protected {@link Member member} access, the type of the declaring class
4645
* is used. Otherwise, the type in the member signature, such as a parameter
47-
* type for an executable, or the return type of a field is used.
46+
* type for an executable, or the return type of a field is used. If the
47+
* type is generic, the type that is protected in the generic signature is
48+
* used.
4849
* @return the type that is not public
4950
*/
50-
public ResolvableType getType() {
51+
public Class<?> getType() {
5152
return this.type;
5253
}
5354

@@ -60,7 +61,7 @@ public Member getMember() {
6061
return this.target;
6162
}
6263

63-
static ProtectedElement of(ResolvableType type, @Nullable Member member) {
64+
static ProtectedElement of(Class<?> type, @Nullable Member member) {
6465
return new ProtectedElement(type, member);
6566
}
6667

spring-core/src/test/java/org/springframework/aot/generator/ProtectedAccessTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.springframework.aot.generator.ProtectedAccess.Options;
2727
import org.springframework.core.ResolvableType;
28+
import org.springframework.core.testfixture.aot.generator.visibility.ProtectedGenericParameter;
2829
import org.springframework.core.testfixture.aot.generator.visibility.ProtectedParameter;
2930
import org.springframework.core.testfixture.aot.generator.visibility.PublicFactoryBean;
3031
import org.springframework.util.ReflectionUtils;
@@ -82,6 +83,12 @@ void analyzeWithPackagePrivateConstructorParameter() {
8283
assertPrivilegedAccess(ProtectedParameter.class);
8384
}
8485

86+
@Test
87+
void analyzeWithPackagePrivateConstructorGenericParameter() {
88+
this.protectedAccess.analyze(ProtectedGenericParameter.class.getConstructors()[0], DEFAULT_OPTIONS);
89+
assertPrivilegedAccess(ProtectedParameter.class);
90+
}
91+
8592
@Test
8693
void analyzeWithPackagePrivateMethod() {
8794
this.protectedAccess.analyze(method(PublicClass.class, "getProtectedMethod"), DEFAULT_OPTIONS);
@@ -164,7 +171,7 @@ void analyzeTypeWithProtectedGenericArgument() {
164171
@Test
165172
void analyzeWithRecursiveType() {
166173
assertThat(this.protectedAccess.isProtected(ResolvableType.forClassWithGenerics(
167-
SelfReference.class, SelfReference.class))).isTrue();
174+
SelfReference.class, SelfReference.class))).isEqualTo(SelfReference.class);
168175
}
169176

170177
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.testfixture.aot.generator.visibility;
18+
19+
import java.util.List;
20+
21+
public class ProtectedGenericParameter {
22+
23+
public ProtectedGenericParameter(List<ProtectedType> types) {
24+
}
25+
26+
}

0 commit comments

Comments
 (0)