Add FieldMode for field hints and ensure that it cannot be downgraded

Add a `FieldMode` enum analogous to `ExecutableHint` and update
`FieldHint` to ensure that registration cannot downgrade `WRITE`
to `READ`.

Fixes gh-29055
This commit is contained in:
Phillip Webb
2022-09-01 14:37:23 -07:00
parent 0bd923b0a7
commit da1005cd66
13 changed files with 188 additions and 30 deletions

View File

@@ -19,6 +19,7 @@ package org.springframework.aot.hint;
import java.lang.reflect.Field;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A hint that describes the need of reflection on a {@link Field}.
@@ -28,23 +29,33 @@ import org.springframework.lang.Nullable;
*/
public final class FieldHint extends MemberHint {
private final boolean allowWrite;
private final FieldMode mode;
private final boolean allowUnsafeAccess;
private FieldHint(Builder builder) {
super(builder.name);
this.allowWrite = (builder.allowWrite != null) ? builder.allowWrite : true;
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.allowWrite;
return this.mode == FieldMode.WRITE;
}
/**
* Return the {@linkplain FieldMode mode} that apply to this hint.
* @return the mode
*/
public FieldMode getMode() {
return this.mode;
}
/**
@@ -64,7 +75,7 @@ public final class FieldHint extends MemberHint {
private final String name;
@Nullable
private Boolean allowWrite;
private FieldMode mode;
private boolean allowUnsafeAccess;
@@ -77,9 +88,26 @@ public final class FieldHint extends MemberHint {
* 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) {
this.allowWrite = 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;
}

View File

@@ -0,0 +1,51 @@
/*
* 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
*/
boolean includes(@Nullable FieldMode other) {
return (other == null || this.ordinal() >= other.ordinal());
}
}

View File

@@ -127,12 +127,12 @@ public class ReflectionHints {
/**
* Register the need for reflection on the specified {@link Field},
* enabling write access.
* enabling {@link FieldMode#WRITE}.
* @param field the field that requires reflection
* @return {@code this}, to facilitate method chaining
*/
public ReflectionHints registerField(Field field) {
return registerField(field, fieldHint -> fieldHint.allowWrite(true));
return registerField(field, fieldHint -> fieldHint.withMode(FieldMode.WRITE));
}
/**
@@ -146,7 +146,6 @@ public class ReflectionHints {
typeHint -> typeHint.withField(field.getName(), fieldHint));
}
/**
* Register the need for reflection on the specified {@link Constructor},
* enabling {@link ExecutableMode#INVOKE}.

View File

@@ -29,6 +29,7 @@ import java.util.function.Predicate;
import org.springframework.aot.hint.ExecutableHint;
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.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
@@ -401,7 +402,7 @@ public class ReflectionHintsPredicates {
private boolean exactMatch(TypeHint typeHint) {
return typeHint.fields().anyMatch(fieldHint ->
this.field.getName().equals(fieldHint.getName())
&& (!this.allowWrite || this.allowWrite == fieldHint.isAllowWrite())
&& (!this.allowWrite || fieldHint.getMode() == FieldMode.WRITE)
&& (!this.allowUnsafeAccess || this.allowUnsafeAccess == fieldHint.isAllowUnsafeAccess()));
}
}

View File

@@ -26,6 +26,7 @@ import java.util.stream.Stream;
import org.springframework.aot.hint.ExecutableHint;
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.ReflectionHints;
import org.springframework.aot.hint.TypeHint;
@@ -74,8 +75,8 @@ class ReflectionHintsWriter {
private Map<String, Object> toAttributes(FieldHint hint) {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", hint.getName());
if (hint.isAllowWrite()) {
attributes.put("allowWrite", hint.isAllowWrite());
if (hint.getMode() == FieldMode.WRITE) {
attributes.put("allowWrite", true);
}
if (hint.isAllowUnsafeAccess()) {
attributes.put("allowUnsafeAccess", hint.isAllowUnsafeAccess());