Merge pull request #29130 from sreenath-tm

* pr/29130:
  Polish "Adapt FieldHint to recent GraalVM versions"
  Adapt FieldHint to recent GraalVM versions

Closes gh-29130
This commit is contained in:
Stephane Nicoll 2022-09-12 14:50:43 +02:00
commit c8f7a76659
17 changed files with 40 additions and 545 deletions

View File

@ -73,7 +73,7 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
RegisteredBean registeredBean = getAndApplyContribution( RegisteredBean registeredBean = getAndApplyContribution(
PrivateFieldInjectionSample.class); PrivateFieldInjectionSample.class);
assertThat(RuntimeHintsPredicates.reflection() assertThat(RuntimeHintsPredicates.reflection()
.onField(PrivateFieldInjectionSample.class, "environment").withWriteMode()) .onField(PrivateFieldInjectionSample.class, "environment"))
.accepts(this.generationContext.getRuntimeHints()); .accepts(this.generationContext.getRuntimeHints());
compile(registeredBean, (postProcessor, compiled) -> { compile(registeredBean, (postProcessor, compiled) -> {
PrivateFieldInjectionSample instance = new PrivateFieldInjectionSample(); PrivateFieldInjectionSample instance = new PrivateFieldInjectionSample();
@ -92,7 +92,7 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
RegisteredBean registeredBean = getAndApplyContribution( RegisteredBean registeredBean = getAndApplyContribution(
PackagePrivateFieldInjectionSample.class); PackagePrivateFieldInjectionSample.class);
assertThat(RuntimeHintsPredicates.reflection() assertThat(RuntimeHintsPredicates.reflection()
.onField(PackagePrivateFieldInjectionSample.class, "environment").withWriteMode()) .onField(PackagePrivateFieldInjectionSample.class, "environment"))
.accepts(this.generationContext.getRuntimeHints()); .accepts(this.generationContext.getRuntimeHints());
compile(registeredBean, (postProcessor, compiled) -> { compile(registeredBean, (postProcessor, compiled) -> {
PackagePrivateFieldInjectionSample instance = new PackagePrivateFieldInjectionSample(); PackagePrivateFieldInjectionSample instance = new PackagePrivateFieldInjectionSample();

View File

@ -290,7 +290,7 @@ enum InstrumentedMethod {
* {@link Field#set(Object, Object)}. * {@link Field#set(Object, Object)}.
*/ */
FIELD_SET(Field.class, "set", HintType.REFLECTION, FIELD_SET(Field.class, "set", HintType.REFLECTION,
invocation -> reflection().onField(invocation.getInstance()).withWriteMode()), invocation -> reflection().onField(invocation.getInstance())),
/* /*

View File

@ -17,10 +17,6 @@
package org.springframework.aot.hint; package org.springframework.aot.hint;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/** /**
* A hint that describes the need for reflection on a {@link Field}. * A hint that describes the need for reflection on a {@link Field}.
@ -30,115 +26,8 @@ import org.springframework.util.Assert;
*/ */
public final class FieldHint extends MemberHint { public final class FieldHint extends MemberHint {
private final FieldMode mode; FieldHint(String name) {
super(name);
private final boolean allowUnsafeAccess;
private FieldHint(Builder builder) {
super(builder.name);
this.mode = (builder.mode != null ? builder.mode : FieldMode.WRITE);
this.allowUnsafeAccess = builder.allowUnsafeAccess;
} }
/**
* Return whether setting the value of the field should be allowed.
* @return {@code true} to allow {@link Field#set(Object, Object)}.
* @deprecated in favor of {@link #getMode()}
*/
@Deprecated
public boolean isAllowWrite() {
return this.mode == FieldMode.WRITE;
}
/**
* Return the {@linkplain FieldMode mode} that applies to this hint.
* @return the mode
*/
public FieldMode getMode() {
return this.mode;
}
/**
* Return whether using {@code Unsafe} on the field should be allowed.
* @return {@code true} to allow unsafe access
*/
public boolean isAllowUnsafeAccess() {
return this.allowUnsafeAccess;
}
/**
* Return a {@link Consumer} that applies the given {@link FieldMode}
* to the accepted {@link Builder}.
* @param mode the mode to apply
* @return a consumer to apply the mode
*/
public static Consumer<Builder> builtWith(FieldMode mode) {
return builder -> builder.withMode(mode);
}
/**
* Builder for {@link FieldHint}.
*/
public static class Builder {
private final String name;
@Nullable
private FieldMode mode;
private boolean allowUnsafeAccess;
Builder(String name) {
this.name = name;
}
/**
* Specify if setting the value of the field should be allowed.
* @param allowWrite {@code true} to allow {@link Field#set(Object, Object)}
* @return {@code this}, to facilitate method chaining
* @deprecated in favor of {@link #withMode(FieldMode)}
*/
@Deprecated
public Builder allowWrite(boolean allowWrite) {
if (allowWrite) {
return withMode(FieldMode.WRITE);
}
return this;
}
/**
* Specify that the {@linkplain FieldMode mode} is required.
* @param mode the required mode
* @return {@code this}, to facilitate method chaining
*/
public Builder withMode(FieldMode mode) {
Assert.notNull(mode, "'mode' must not be null");
if ((this.mode == null || !this.mode.includes(mode))) {
this.mode = mode;
}
return this;
}
/**
* Specify whether using {@code Unsafe} on the field should be allowed.
* @param allowUnsafeAccess {@code true} to allow unsafe access
* @return {@code this}, to facilitate method chaining
*/
public Builder allowUnsafeAccess(boolean allowUnsafeAccess) {
this.allowUnsafeAccess = allowUnsafeAccess;
return this;
}
/**
* Create a {@link FieldHint} based on the state of this builder.
* @return a field hint
*/
FieldHint build() {
return new FieldHint(this);
}
}
} }

View File

@ -1,51 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aot.hint;
import java.lang.reflect.Field;
import org.springframework.lang.Nullable;
/**
* Represents the need of reflection for a given {@link Field}.
*
* @author Phillip Webb
* @since 6.0
* @see ReflectionHints
*/
public enum FieldMode {
/**
* Only field read is required.
*/
READ,
/**
* Full field read and write is required.
*/
WRITE;
/**
* Specify if this mode already includes the specified {@code other} mode.
* @param other the other mode to check
* @return {@code true} if this mode includes the other mode
*/
public boolean includes(@Nullable FieldMode other) {
return (other == null || this.ordinal() >= other.ordinal());
}
}

View File

@ -148,34 +148,13 @@ public class ReflectionHints {
} }
/** /**
* Register the need for reflection on the specified {@link Field}, * Register the need for reflection on the specified {@link Field}.
* enabling {@link FieldMode#WRITE}.
* @param field the field that requires reflection * @param field the field that requires reflection
* @return {@code this}, to facilitate method chaining * @return {@code this}, to facilitate method chaining
*/ */
public ReflectionHints registerField(Field field) { public ReflectionHints registerField(Field field) {
return registerField(field, FieldMode.WRITE);
}
/**
* Register the need for reflection on the specified {@link Field}
* using the specified {@link FieldMode}.
* @param field the field that requires reflection
* @return {@code this}, to facilitate method chaining
*/
public ReflectionHints registerField(Field field, FieldMode mode) {
return registerField(field, FieldHint.builtWith(mode));
}
/**
* Register the need for reflection on the specified {@link Field}.
* @param field the field that requires reflection
* @param fieldHint a builder to further customize the hints of this field
* @return {@code this}, to facilitate method chaining
*/
public ReflectionHints registerField(Field field, Consumer<FieldHint.Builder> fieldHint) {
return registerType(TypeReference.of(field.getDeclaringClass()), return registerType(TypeReference.of(field.getDeclaringClass()),
typeHint -> typeHint.withField(field.getName(), fieldHint)); typeHint -> typeHint.withField(field.getName()));
} }
/** /**

View File

@ -57,7 +57,7 @@ public final class TypeHint implements ConditionalHint {
this.type = builder.type; this.type = builder.type;
this.reachableType = builder.reachableType; this.reachableType = builder.reachableType;
this.memberCategories = Set.copyOf(builder.memberCategories); this.memberCategories = Set.copyOf(builder.memberCategories);
this.fields = builder.fields.values().stream().map(FieldHint.Builder::build).collect(Collectors.toSet()); this.fields = builder.fields.stream().map(FieldHint::new).collect(Collectors.toSet());
this.constructors = builder.constructors.values().stream().map(ExecutableHint.Builder::build).collect(Collectors.toSet()); this.constructors = builder.constructors.values().stream().map(ExecutableHint.Builder::build).collect(Collectors.toSet());
this.methods = builder.methods.values().stream().map(ExecutableHint.Builder::build).collect(Collectors.toSet()); this.methods = builder.methods.values().stream().map(ExecutableHint.Builder::build).collect(Collectors.toSet());
} }
@ -147,7 +147,7 @@ public final class TypeHint implements ConditionalHint {
@Nullable @Nullable
private TypeReference reachableType; private TypeReference reachableType;
private final Map<String, FieldHint.Builder> fields = new HashMap<>(); private final Set<String> fields = new HashSet<>();
private final Map<ExecutableKey, ExecutableHint.Builder> constructors = new HashMap<>(); private final Map<ExecutableKey, ExecutableHint.Builder> constructors = new HashMap<>();
@ -185,35 +185,12 @@ public final class TypeHint implements ConditionalHint {
} }
/** /**
* Register the need for reflection on the field with the specified name, * Register the need for reflection on the field with the specified name.
* enabling write access.
* @param name the name of the field * @param name the name of the field
* @return {@code this}, to facilitate method chaining * @return {@code this}, to facilitate method chaining
*/ */
public Builder withField(String name) { public Builder withField(String name) {
return withField(name, FieldMode.WRITE); this.fields.add(name);
}
/**
* Register the need for reflection on the field with the specified name
* using the specified {@link FieldMode}.
* @param name the name of the field
* @param mode the requested mode
* @return {@code this}, to facilitate method chaining
*/
public Builder withField(String name, FieldMode mode) {
return withField(name, FieldHint.builtWith(mode));
}
/**
* Register the need for reflection on the field with the specified name.
* @param name the name of the field
* @param fieldHint a builder to further customize the hints of this field
* @return {@code this}, to facilitate method chaining
*/
public Builder withField(String name, Consumer<FieldHint.Builder> fieldHint) {
FieldHint.Builder builder = this.fields.computeIfAbsent(name, FieldHint.Builder::new);
fieldHint.accept(builder);
return this; return this;
} }

View File

@ -28,8 +28,6 @@ import java.util.function.Predicate;
import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableHint;
import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.FieldHint;
import org.springframework.aot.hint.FieldMode;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
@ -375,57 +373,10 @@ public class ReflectionHintsPredicates {
private final Field field; private final Field field;
private FieldMode mode = FieldMode.READ;
private boolean allowUnsafeAccess;
FieldHintPredicate(Field field) { FieldHintPredicate(Field field) {
this.field = field; this.field = field;
} }
/**
* Refine the current predicate to match if write access is allowed on the field.
* @return the refined {@link RuntimeHints} predicate
* @see FieldHint#isAllowWrite()
* @deprecated in favor of {@link #withReadMode()} or {@link #withWriteMode()}
*/
@Deprecated
public FieldHintPredicate allowWrite() {
this.mode = FieldMode.WRITE;
return this;
}
/**
* Refine the current predicate to match if read access is allowed on the field.
* @return the refined {@link RuntimeHints} predicate
* @see FieldHint#getMode()
*/
public FieldHintPredicate withReadMode() {
// FieldMode.READ is already the default and should not override a writeMode() call.
return this;
}
/**
* Refine the current predicate to match if write access is allowed on the field.
* @return the refined {@link RuntimeHints} predicate
* @see FieldHint#getMode()
*/
public FieldHintPredicate withWriteMode() {
this.mode = FieldMode.WRITE;
return this;
}
/**
* Refine the current predicate to match if unsafe access is allowed on the field.
* @return the refined {@link RuntimeHints} predicate
* @see FieldHint#isAllowUnsafeAccess() ()
*/
public FieldHintPredicate allowUnsafeAccess() {
this.allowUnsafeAccess = true;
return this;
}
@Override @Override
public boolean test(RuntimeHints runtimeHints) { public boolean test(RuntimeHints runtimeHints) {
TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass()); TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass());
@ -447,9 +398,7 @@ public class ReflectionHintsPredicates {
private boolean exactMatch(TypeHint typeHint) { private boolean exactMatch(TypeHint typeHint) {
return typeHint.fields().anyMatch(fieldHint -> return typeHint.fields().anyMatch(fieldHint ->
this.field.getName().equals(fieldHint.getName()) this.field.getName().equals(fieldHint.getName()));
&& (fieldHint.getMode().includes(this.mode))
&& (!this.allowUnsafeAccess || this.allowUnsafeAccess == fieldHint.isAllowUnsafeAccess()));
} }
} }

View File

@ -26,7 +26,6 @@ import java.util.stream.Stream;
import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableHint;
import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.FieldHint; import org.springframework.aot.hint.FieldHint;
import org.springframework.aot.hint.FieldMode;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeHint;
@ -78,12 +77,6 @@ class ReflectionHintsWriter {
private Map<String, Object> toAttributes(FieldHint hint) { private Map<String, Object> toAttributes(FieldHint hint) {
Map<String, Object> attributes = new LinkedHashMap<>(); Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", hint.getName()); attributes.put("name", hint.getName());
if (hint.getMode() == FieldMode.WRITE) {
attributes.put("allowWrite", true);
}
if (hint.isAllowUnsafeAccess()) {
attributes.put("allowUnsafeAccess", hint.isAllowUnsafeAccess());
}
return attributes; return attributes;
} }

View File

@ -1,38 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aot.hint;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FieldHint}.
*
* @author Phillip Webb
*/
class FieldHintTests {
@Test
void builtWithAppliesMode() {
FieldHint.Builder builder = new FieldHint.Builder("test");
assertThat(builder.build().getMode()).isEqualTo(FieldMode.WRITE);
FieldHint.builtWith(FieldMode.READ).accept(builder);
assertThat(builder.build().getMode()).isEqualTo(FieldMode.READ);
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aot.hint;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FieldMode}.
*
* @author Phillip Webb
* @since 6.0
*/
class FieldModeTests {
@Test
void writeIncludesNullMode() {
assertThat(FieldMode.WRITE.includes(null)).isTrue();
}
@Test
void writeIncludesRead() {
assertThat(FieldMode.WRITE.includes(FieldMode.READ)).isTrue();
}
@Test
void writeIncludesWrite() {
assertThat(FieldMode.WRITE.includes(FieldMode.WRITE)).isTrue();
}
@Test
void readIncludesNullMode() {
assertThat(FieldMode.READ.includes(null)).isTrue();
}
@Test
void readIncludesRead() {
assertThat(FieldMode.READ.includes(FieldMode.READ)).isTrue();
}
@Test
void readDoesNotIncludeWrite() {
assertThat(FieldMode.READ.includes(FieldMode.WRITE)).isFalse();
}
}

View File

@ -124,63 +124,12 @@ class ReflectionHintsTests {
} }
@Test @Test
void registerFieldAllowsWriteByDefault() { void registerField() {
Field field = ReflectionUtils.findField(TestType.class, "field"); Field field = ReflectionUtils.findField(TestType.class, "field");
assertThat(field).isNotNull(); assertThat(field).isNotNull();
this.reflectionHints.registerField(field); this.reflectionHints.registerField(field);
assertTestTypeFieldHint(fieldHint -> { assertTestTypeFieldHint(fieldHint ->
assertThat(fieldHint.getName()).isEqualTo("field"); assertThat(fieldHint.getName()).isEqualTo("field"));
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}
@Test
void registerFieldWithEmptyCustomizerAppliesConsistentDefault() {
Field field = ReflectionUtils.findField(TestType.class, "field");
assertThat(field).isNotNull();
this.reflectionHints.registerField(field, fieldHint -> {});
assertTestTypeFieldHint(fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("field");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}
@Test
void registerFieldWithCustomizerAppliesCustomization() {
Field field = ReflectionUtils.findField(TestType.class, "field");
assertThat(field).isNotNull();
this.reflectionHints.registerField(field, fieldHint ->
fieldHint.withMode(FieldMode.READ).allowUnsafeAccess(true));
assertTestTypeFieldHint(fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("field");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.READ);
assertThat(fieldHint.isAllowUnsafeAccess()).isTrue();
});
}
@Test
void registerFieldWithMode() {
Field field = ReflectionUtils.findField(TestType.class, "field");
assertThat(field).isNotNull();
this.reflectionHints.registerField(field, FieldMode.READ);
assertTestTypeFieldHint(fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("field");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.READ);
});
}
@Test // gh-29055
void registerFieldWithCustomizersCannotDowngradeWrite() {
Field field = ReflectionUtils.findField(TestType.class, "field");
assertThat(field).isNotNull();
this.reflectionHints.registerField(field, FieldMode.WRITE);
this.reflectionHints.registerField(field, FieldMode.READ);
assertTestTypeFieldHint(fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("field");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
});
} }
private void assertTestTypeFieldHint(Consumer<FieldHint> fieldHint) { private void assertTestTypeFieldHint(Consumer<FieldHint> fieldHint) {

View File

@ -55,62 +55,9 @@ class TypeHintTests {
} }
@Test @Test
void createWithFieldAllowsWriteByDefault() { void createWithField() {
assertFieldHint(TypeHint.of(TypeReference.of(String.class)) assertFieldHint(TypeHint.of(TypeReference.of(String.class))
.withField("value"), fieldHint -> { .withField("value"), fieldHint -> assertThat(fieldHint.getName()).isEqualTo("value"));
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}
@Test
void createWithFieldAndEmptyCustomizerAppliesConsistentDefault() {
assertFieldHint(TypeHint.of(TypeReference.of(String.class))
.withField("value", fieldHint -> {}), fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}
@Test
void createWithFieldAndCustomizerAppliesCustomization() {
assertFieldHint(TypeHint.of(TypeReference.of(String.class))
.withField("value", fieldHint -> {
fieldHint.withMode(FieldMode.READ);
fieldHint.allowUnsafeAccess(true);
}), fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.READ);
assertThat(fieldHint.isAllowUnsafeAccess()).isTrue();
});
}
@Test
void createWithFieldReuseBuilder() {
Builder builder = TypeHint.of(TypeReference.of(String.class));
builder.withField("value", fieldHint -> fieldHint.allowUnsafeAccess(true));
builder.withField("value", fieldHint -> {
fieldHint.withMode(FieldMode.WRITE);
fieldHint.allowUnsafeAccess(false);
});
assertFieldHint(builder, fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}
@Test
void createFieldWithFieldMode() {
Builder builder = TypeHint.of(TypeReference.of(String.class));
builder.withField("value", FieldMode.READ);
assertFieldHint(builder, fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.READ);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
} }
void assertFieldHint(Builder builder, Consumer<FieldHint> fieldHint) { void assertFieldHint(Builder builder, Consumer<FieldHint> fieldHint) {

View File

@ -27,7 +27,6 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.FieldMode;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
@ -480,30 +479,9 @@ class ReflectionHintsPredicatesTests {
} }
@Test @Test
void fieldWriteReflectionDoesNotMatchFieldHint() { void fieldReflectionDoesNotMatchNonRegisteredFielddHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField",
FieldMode.READ));
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").withWriteMode());
}
@Test
void fieldUnsafeReflectionDoesNotMatchFieldHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField")); runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField"));
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").allowUnsafeAccess()); assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField"));
}
@Test
void fieldWriteReflectionMatchesFieldHintWithWrite() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withField("publicField", FieldMode.WRITE));
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").withWriteMode());
}
@Test
void fieldUnsafeReflectionMatchesFieldHintWithUnsafe() {
runtimeHints.reflection().registerType(SampleClass.class,
typeHint -> typeHint.withField("publicField", fieldHint -> fieldHint.allowUnsafeAccess(true)));
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").allowUnsafeAccess());
} }
@Test @Test

View File

@ -32,7 +32,6 @@ import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.FieldMode;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ProxyHints; import org.springframework.aot.hint.ProxyHints;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
@ -99,8 +98,7 @@ public class FileNativeConfigurationWriterTests {
FileNativeConfigurationWriter generator = new FileNativeConfigurationWriter(tempDir); FileNativeConfigurationWriter generator = new FileNativeConfigurationWriter(tempDir);
RuntimeHints hints = new RuntimeHints(); RuntimeHints hints = new RuntimeHints();
ReflectionHints reflectionHints = hints.reflection(); ReflectionHints reflectionHints = hints.reflection();
reflectionHints.registerType(StringDecoder.class, builder -> { reflectionHints.registerType(StringDecoder.class, builder -> builder
builder
.onReachableType(String.class) .onReachableType(String.class)
.withMembers(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS, .withMembers(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS,
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
@ -108,15 +106,11 @@ public class FileNativeConfigurationWriterTests {
MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES) MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES)
.withField("DEFAULT_CHARSET", fieldBuilder -> fieldBuilder.withMode(FieldMode.READ)) .withField("DEFAULT_CHARSET")
.withField("defaultCharset", fieldBuilder -> { .withField("defaultCharset")
fieldBuilder.withMode(FieldMode.WRITE);
fieldBuilder.allowUnsafeAccess(true);
})
.withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT) .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT)
.withMethod("setDefaultCharset", TypeReference.listOf(Charset.class)) .withMethod("setDefaultCharset", TypeReference.listOf(Charset.class))
.withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT); .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT));
});
generator.write(hints); generator.write(hints);
assertEquals(""" assertEquals("""
[ [
@ -137,7 +131,7 @@ public class FileNativeConfigurationWriterTests {
"allDeclaredClasses": true, "allDeclaredClasses": true,
"fields": [ "fields": [
{ "name": "DEFAULT_CHARSET" }, { "name": "DEFAULT_CHARSET" },
{ "name": "defaultCharset", "allowWrite": true, "allowUnsafeAccess": true } { "name": "defaultCharset" }
], ],
"methods": [ "methods": [
{ "name": "setDefaultCharset", "parameterTypes": [ "java.nio.charset.Charset" ] } { "name": "setDefaultCharset", "parameterTypes": [ "java.nio.charset.Charset" ] }

View File

@ -27,7 +27,6 @@ import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.FieldMode;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
@ -58,11 +57,8 @@ public class ReflectionHintsWriterTests {
MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES) MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES)
.withField("DEFAULT_CHARSET", fieldBuilder -> fieldBuilder.withMode(FieldMode.READ)) .withField("DEFAULT_CHARSET")
.withField("defaultCharset", fieldBuilder -> { .withField("defaultCharset")
fieldBuilder.withMode(FieldMode.WRITE);
fieldBuilder.allowUnsafeAccess(true);
})
.withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT) .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT)
.withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class))) .withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class)))
.withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT)); .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT));
@ -85,7 +81,7 @@ public class ReflectionHintsWriterTests {
"allDeclaredClasses": true, "allDeclaredClasses": true,
"fields": [ "fields": [
{ "name": "DEFAULT_CHARSET" }, { "name": "DEFAULT_CHARSET" },
{ "name": "defaultCharset", "allowWrite": true, "allowUnsafeAccess": true } { "name": "defaultCharset" }
], ],
"methods": [ "methods": [
{ "name": "setDefaultCharset", "parameterTypes": [ "java.nio.charset.Charset" ] } { "name": "setDefaultCharset", "parameterTypes": [ "java.nio.charset.Charset" ] }

View File

@ -87,7 +87,7 @@ class InjectionCodeGeneratorTests {
TestBean bean = new TestBean(); TestBean bean = new TestBean();
Field field = ReflectionUtils.findField(bean.getClass(), "age"); Field field = ReflectionUtils.findField(bean.getClass(), "age");
this.generator.generateInjectionCode(field, INSTANCE_VARIABLE, CodeBlock.of("$L", 123)); this.generator.generateInjectionCode(field, INSTANCE_VARIABLE, CodeBlock.of("$L", 123));
assertThat(RuntimeHintsPredicates.reflection().onField(TestBean.class, "age").withWriteMode()) assertThat(RuntimeHintsPredicates.reflection().onField(TestBean.class, "age"))
.accepts(this.hints); .accepts(this.hints);
} }

View File

@ -32,7 +32,6 @@ import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.FieldMode;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.aot.test.generate.compile.CompileWithTargetClassAccess; import org.springframework.aot.test.generate.compile.CompileWithTargetClassAccess;
@ -129,13 +128,8 @@ class PersistenceAnnotationBeanPostProcessorAotContributionTests {
.singleElement().satisfies(typeHint -> { .singleElement().satisfies(typeHint -> {
assertThat(typeHint.getType()).isEqualTo( assertThat(typeHint.getType()).isEqualTo(
TypeReference.of(DefaultPersistenceContextField.class)); TypeReference.of(DefaultPersistenceContextField.class));
assertThat(typeHint.fields()).singleElement() assertThat(typeHint.fields()).singleElement().satisfies(fieldHint ->
.satisfies(fieldHint -> { assertThat(fieldHint.getName()).isEqualTo("entityManager"));
assertThat(fieldHint.getName())
.isEqualTo("entityManager");
assertThat(fieldHint.getMode()).isEqualTo(FieldMode.WRITE);
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}); });
}); });
} }