Added documentation to ObfuscationReflectionHelper adn deprecated index based functions. (#5893)

This commit is contained in:
Cadiboo 2019-07-09 12:44:21 +10:00 committed by LexManos
parent 61bea2bfda
commit 3fe08f149d
1 changed files with 160 additions and 38 deletions

View File

@ -21,11 +21,10 @@ package net.minecraftforge.fml.common;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import cpw.mods.modlauncher.api.INameMappingService;
import net.minecraftforge.fml.loading.FMLLoader;
@ -44,31 +43,50 @@ import com.google.common.base.Preconditions;
*
* In other cases, AccessTransformers may be used.
*
* All field names should be passed in as SRG names, and this will automatically resolve if MCP mappings are detected.
* All field and method names should be passed in as SRG names, and this will automatically resolve if MCP mappings are detected.
*
*/
@SuppressWarnings({"serial", "unchecked"})
@SuppressWarnings({"serial", "unchecked", "unused", "WeakerAccess"})
public class ObfuscationReflectionHelper
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker REFLECTION = MarkerManager.getMarker("REFLECTION");
private static final Map<String, String> map = new HashMap<>();
private static boolean loaded = false;
/**
* Remaps a name using the SRG naming function
* @param domain The {@link INameMappingService.Domain} to use to remap the name.
* @param name The name to try and remap.
* @return The remapped name, or the original name if it couldn't be remapped.
*/
@Nonnull
public static String remapName(INameMappingService.Domain domain, String name)
{
return FMLLoader.getNameFunction("srg").map(f->f.apply(domain, name)).orElse(name);
}
/**
* Gets the value a field with the specified index in the given class.
* Note: For performance, use {@link #findField(Class, int)} if you are getting the value more than once.
* <p>
* Throws an exception if the field is not found or the value of the field cannot be gotten.
*
* @param classToAccess The class to find the field on.
* @param instance The instance of the {@code classToAccess}.
* @param fieldIndex The index of the field in the {@code classToAccess}.
* @param <T> The type of the value.
* @param <E> The type of the {@code classToAccess}.
* @return The value of the field with the specified index in the {@code classToAccess}.
* @throws UnableToAccessFieldException If there was a problem getting the field or the value.
* @deprecated Use {@link #getPrivateValue(Class, Object, String)} because field indices change a lot more often than field names do.
* TODO: Remove in 1.15
*/
@Deprecated
@Nullable
public static <T, E> T getPrivateValue(Class<? super E> classToAccess, E instance, int fieldIndex)
{
try
{
Field f = classToAccess.getDeclaredFields()[fieldIndex];
f.setAccessible(true);
return (T)f.get(instance);
return (T) findField(classToAccess, fieldIndex).get(instance);
}
catch (Exception e)
{
@ -77,11 +95,27 @@ public class ObfuscationReflectionHelper
}
}
/**
* Gets the value a field with the specified name in the given class.
* Note: For performance, use {@link #findField(Class, String)} if you are getting the value more than once.
* <p>
* Throws an exception if the field is not found or the value of the field cannot be gotten.
*
* @param classToAccess The class to find the field on.
* @param instance The instance of the {@code classToAccess}.
* @param fieldName The SRG (unmapped) name of the field to find (e.g. "field_181725_a").
* @param <T> The type of the value.
* @param <E> The type of the {@code classToAccess}.
* @return The value of the field with the specified name in the {@code classToAccess}.
* @throws UnableToAccessFieldException If there was a problem getting the field.
* @throws UnableToAccessFieldException If there was a problem getting the value.
*/
@Nullable
public static <T, E> T getPrivateValue(Class<? super E> classToAccess, E instance, String fieldName)
{
try
{
return (T)findField(classToAccess, remapName(INameMappingService.Domain.FIELD, fieldName)).get(instance);
return (T) findField(classToAccess, fieldName).get(instance);
}
catch (UnableToFindFieldException e)
{
@ -95,13 +129,28 @@ public class ObfuscationReflectionHelper
}
}
public static <T, E> void setPrivateValue(Class<? super T> classToAccess, T instance, E value, int fieldIndex)
/**
* Sets the value a field with the specified index in the given class.
* Note: For performance, use {@link #findField(Class, int)} if you are setting the value more than once.
* <p>
* Throws an exception if the field is not found or the value of the field cannot be set.
*
* @param classToAccess The class to find the field on.
* @param instance The instance of the {@code classToAccess}.
* @param value The new value for the field
* @param fieldIndex The index of the field in the {@code classToAccess}.
* @param <T> The type of the value.
* @param <E> The type of the {@code classToAccess}.
* @throws UnableToAccessFieldException If there was a problem setting the value of the field.
* @deprecated Use {@link #setPrivateValue(Class, Object, Object, String)} because field indices change a lot more often than field names do.
* TODO: Remove in 1.15
*/
@Deprecated
public static <T, E> void setPrivateValue(@Nonnull final Class<? super T> classToAccess, @Nonnull final T instance, @Nullable final E value, int fieldIndex)
{
try
{
Field f = classToAccess.getDeclaredFields()[fieldIndex];
f.setAccessible(true);
f.set(instance, value);
findField(classToAccess, fieldIndex).set(instance, value);
}
catch (IllegalAccessException e)
{
@ -110,11 +159,26 @@ public class ObfuscationReflectionHelper
}
}
public static <T, E> void setPrivateValue(Class<? super T> classToAccess, T instance, E value, String fieldName)
/**
* Sets the value a field with the specified name in the given class.
* Note: For performance, use {@link #findField(Class, String)} if you are setting the value more than once.
* <p>
* Throws an exception if the field is not found or the value of the field cannot be set.
*
* @param classToAccess The class to find the field on.
* @param instance The instance of the {@code classToAccess}.
* @param value The new value for the field
* @param fieldName The name of the field in the {@code classToAccess}.
* @param <T> The type of the value.
* @param <E> The type of the {@code classToAccess}.
* @throws UnableToFindFieldException If there was a problem getting the field.
* @throws UnableToAccessFieldException If there was a problem setting the value of the field.
*/
public static <T, E> void setPrivateValue(@Nonnull final Class<? super T> classToAccess, @Nonnull final T instance, @Nullable final E value, @Nonnull final String fieldName)
{
try
{
findField(classToAccess, remapName(INameMappingService.Domain.FIELD, fieldName)).set(instance, value);
findField(classToAccess, fieldName).set(instance, value);
}
catch (UnableToFindFieldException e)
{
@ -135,16 +199,22 @@ public class ObfuscationReflectionHelper
* Throws an exception if the method is not found.
*
* @param clazz The class to find the method on.
* @param methodName The SRG (obfuscated) name of the method to find(e.g. "func_12820_D").
* @param methodName The SRG (unmapped) name of the method to find (e.g. "func_12820_D").
* @param parameterTypes The parameter types of the method to find.
* @return The method with the specified name and parameters in the given class.
* @throws NullPointerException If {@code clazz} is null.
* @throws NullPointerException If {@code methodName} is null.
* @throws IllegalArgumentException If {@code methodName} is empty.
* @throws NullPointerException If {@code parameterTypes} is null.
* @throws UnableToFindMethodException If the method could not be found.
*/
@Nonnull
public static Method findMethod(@Nonnull Class<?> clazz, @Nonnull String methodName, Class<?>... parameterTypes)
public static Method findMethod(@Nonnull final Class<?> clazz, @Nonnull final String methodName, @Nonnull final Class<?>... parameterTypes)
{
Preconditions.checkNotNull(clazz);
Preconditions.checkNotNull(methodName);
Preconditions.checkArgument(!methodName.isEmpty(), "Method name cannot be empty");
Preconditions.checkNotNull(clazz, "Class to find method on cannot be null.");
Preconditions.checkNotNull(methodName, "Name of method to find cannot be null.");
Preconditions.checkArgument(!methodName.isEmpty(), "Name of method to find cannot be empty.");
Preconditions.checkNotNull(parameterTypes, "Parameter types of method to find cannot be null.");
try
{
@ -159,32 +229,35 @@ public class ObfuscationReflectionHelper
}
/**
* Finds a constructor in the specified class that has matching parameter types.
* Finds a constructor with the specified parameter types in the given class and makes it accessible.
* Note: For performance, store the returned value and avoid calling this repeatedly.
* <p>
* Throws an exception if the constructor is not found.
*
* @param klass The class to find the constructor in
* @param clazz The class to find the constructor in.
* @param parameterTypes The parameter types of the constructor.
* @param <T> The type
* @return The constructor
* @throws NullPointerException if {@code klass} is null
* @throws NullPointerException if {@code parameterTypes} is null
* @throws UnknownConstructorException if the constructor could not be found
* @param <T> The type.
* @return The constructor with the specified parameters in the given class.
* @throws NullPointerException If {@code clazz} is null.
* @throws NullPointerException If {@code parameterTypes} is null.
* @throws UnknownConstructorException If the constructor could not be found.
*/
@Nonnull
public static <T> Constructor<T> findConstructor(@Nonnull final Class<T> klass, @Nonnull final Class<?>... parameterTypes)
public static <T> Constructor<T> findConstructor(@Nonnull final Class<T> clazz, @Nonnull final Class<?>... parameterTypes)
{
Preconditions.checkNotNull(klass, "class");
Preconditions.checkNotNull(parameterTypes, "parameter types");
Preconditions.checkNotNull(clazz, "Class to find constructor on cannot be null.");
Preconditions.checkNotNull(parameterTypes, "Parameter types of constructor to find cannot be null.");
try
{
Constructor<T> constructor = klass.getDeclaredConstructor(parameterTypes);
Constructor<T> constructor = clazz.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return constructor;
}
catch (final NoSuchMethodException e)
{
final StringBuilder desc = new StringBuilder();
desc.append(klass.getSimpleName());
desc.append(clazz.getSimpleName());
StringJoiner joiner = new StringJoiner(", ", "(", ")");
for (Class<?> type : parameterTypes)
@ -193,15 +266,35 @@ public class ObfuscationReflectionHelper
}
desc.append(joiner);
throw new UnknownConstructorException("Could not find constructor '" + desc.toString() + "' in " + klass);
throw new UnknownConstructorException("Could not find constructor '" + desc.toString() + "' in " + clazz);
}
}
private static Field findField(Class<?> clazz, String name)
/**
* Finds a field with the specified name in the given class and makes it accessible.
* Note: For performance, store the returned value and avoid calling this repeatedly.
* <p>
* Throws an exception if the field is not found.
*
* @param clazz The class to find the field on.
* @param fieldName The SRG (unmapped) name of the field to find (e.g. "field_181725_a").
* @param <T> The type.
* @return The constructor with the specified parameters in the given class.
* @throws NullPointerException If {@code clazz} is null.
* @throws NullPointerException If {@code fieldName} is null.
* @throws IllegalArgumentException If {@code fieldName} is empty.
* @throws UnableToFindFieldException If the field could not be found.
*/
@Nonnull
public static <T> Field findField(@Nonnull final Class<? super T> clazz, @Nonnull final String fieldName)
{
Preconditions.checkNotNull(clazz, "Class to find field on cannot be null.");
Preconditions.checkNotNull(fieldName, "Name of field to find cannot be null.");
Preconditions.checkArgument(!fieldName.isEmpty(), "Name of field to find cannot be empty.");
try
{
Field f = clazz.getDeclaredField(name);
Field f = clazz.getDeclaredField(remapName(INameMappingService.Domain.FIELD, fieldName));
f.setAccessible(true);
return f;
}
@ -211,6 +304,35 @@ public class ObfuscationReflectionHelper
}
}
/**
* Finds a field with the specified index in the given class and makes it accessible.
* Note: For performance, store the returned value and avoid calling this repeatedly.
* <p>
* Throws an exception if the field is not found.
*
* @param clazz The class to find the field on.
* @param fieldIndex The index of the field on the class
* @param <T> The type.
* @return The constructor with the specified parameters in the given class.
* @throws NullPointerException If {@code clazz} is null.
* @throws UnableToFindFieldException If the field could not be found.
* @deprecated Use {@link #findField(Class, String)} because field indices change a lot more often than field names do.
* TODO: Remove in 1.15
*/
@Deprecated
@Nonnull
public static <T> Field findField(final Class<? super T> clazz, final int fieldIndex) {
Preconditions.checkNotNull(clazz, "Class to find field on cannot be null.");
try {
final Field f = clazz.getDeclaredFields()[fieldIndex];
f.setAccessible(true);
return f;
} catch (Exception e) {
throw new UnableToFindFieldException(e);
}
}
public static class UnableToAccessFieldException extends RuntimeException
{
private UnableToAccessFieldException(Exception e)