This repository has been archived by the owner on Jun 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Runtime remapping * Clean up, and throw exception when it's the issue. * Message when reflection is accessed but not loaded
- Loading branch information
1 parent
2c314bf
commit 4e3d2a3
Showing
11 changed files
with
620 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
archivesBaseName = "patchwork-mappings" | ||
version = getSubprojectVersion(project, "0.1.0") | ||
|
||
dependencies { | ||
implementation "org.cadixdev:lorenz:0.5.4" | ||
} |
25 changes: 25 additions & 0 deletions
25
patchwork-mappings/src/main/java/cpw/mods/modlauncher/api/INameMappingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Minecraft Forge, Patchwork Project | ||
* Copyright (c) 2016-2020, 2019-2020 | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation version 2.1 | ||
* of the License. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
|
||
package cpw.mods.modlauncher.api; | ||
|
||
@SuppressWarnings("unused") | ||
public interface INameMappingService { | ||
enum Domain { CLASS, METHOD, FIELD } | ||
} |
243 changes: 243 additions & 0 deletions
243
...ork-mappings/src/main/java/net/minecraftforge/fml/common/ObfuscationReflectionHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
/* | ||
* Minecraft Forge, Patchwork Project | ||
* Copyright (c) 2016-2020, 2019-2020 | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation version 2.1 | ||
* of the License. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
|
||
package net.minecraftforge.fml.common; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.util.StringJoiner; | ||
|
||
import com.google.common.base.Preconditions; | ||
import cpw.mods.modlauncher.api.INameMappingService; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.apache.logging.log4j.Marker; | ||
import org.apache.logging.log4j.MarkerManager; | ||
|
||
import net.patchworkmc.api.mappings.PatchworkRemappingService; | ||
|
||
/** | ||
* A reimplementation of MinecraftForge's ObfuscationReflectionHelper in a way | ||
* that will make sure that the names are remapped. | ||
* <p> | ||
* If you're a Fabric mod that depends on Patchwork API, this class will not do | ||
* what you expect. Use {@link PatchworkRemappingService} instead. | ||
*/ | ||
@SuppressWarnings("unused") // Since our implementation is different from Forge we don't use all of the parameters | ||
@Deprecated | ||
public class ObfuscationReflectionHelper { | ||
private static final Logger LOGGER = LogManager.getLogger(); | ||
private static final Marker REFLECTION = MarkerManager.getMarker("REFLECTION"); | ||
|
||
/** | ||
* 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. | ||
*/ | ||
public static String remapName(INameMappingService.Domain domain, String name) { | ||
// Patchwork intentionally always returns the input, we need an actual | ||
// reference to the Class because intermediary doesn't have the unique | ||
// name guarantee like SRG does. | ||
return name; | ||
} | ||
|
||
/** | ||
* 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. | ||
*/ | ||
public static <T, E> T getPrivateValue(Class<? super E> classToAccess, E instance, String fieldName) { | ||
try { | ||
return (T) findField(classToAccess, fieldName).get(instance); | ||
} catch (UnableToFindFieldException e) { | ||
LOGGER.error(REFLECTION, "Unable to locate field {} ({}) on type {}", fieldName, PatchworkRemappingService.remapFieldName(classToAccess, fieldName), classToAccess.getName(), e); | ||
throw e; | ||
} catch (IllegalAccessException e) { | ||
LOGGER.error(REFLECTION, "Unable to access field {} ({}) on type {}", fieldName, PatchworkRemappingService.remapFieldName(classToAccess, fieldName), classToAccess.getName(), e); | ||
throw new UnableToAccessFieldException(e); | ||
} | ||
} | ||
|
||
/** | ||
* 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( | ||
final Class<? super T> classToAccess, | ||
final T instance, | ||
final E value, | ||
final String fieldName) { | ||
try { | ||
findField(classToAccess, fieldName).set(instance, value); | ||
} catch (UnableToFindFieldException e) { | ||
LOGGER.error("Unable to locate any field {} on type {}", fieldName, classToAccess.getName(), e); | ||
throw e; | ||
} catch (IllegalAccessException e) { | ||
LOGGER.error("Unable to set any field {} on type {}", fieldName, classToAccess.getName(), e); | ||
throw new UnableToAccessFieldException(e); | ||
} | ||
} | ||
|
||
/** | ||
* Finds a method with the specified name and parameters 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 method is not found. | ||
* | ||
* @param clazz The class to find the method on. | ||
* @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. | ||
*/ | ||
public static Method findMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterTypes) { | ||
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 { | ||
Method m = clazz.getDeclaredMethod(PatchworkRemappingService.remapMethodName(clazz, methodName), parameterTypes); | ||
m.setAccessible(true); | ||
return m; | ||
} catch (Exception e) { | ||
throw new UnableToFindMethodException(e); | ||
} | ||
} | ||
|
||
/** | ||
* 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 clazz The class to find the constructor in. | ||
* @param parameterTypes The parameter types of the constructor. | ||
* @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. | ||
*/ | ||
public static <T> Constructor<T> findConstructor(final Class<T> clazz, final Class<?>... parameterTypes) { | ||
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 = clazz.getDeclaredConstructor(parameterTypes); | ||
constructor.setAccessible(true); | ||
return constructor; | ||
} catch (final NoSuchMethodException e) { | ||
final StringBuilder desc = new StringBuilder(); | ||
desc.append(clazz.getSimpleName()); | ||
|
||
StringJoiner joiner = new StringJoiner(", ", "(", ")"); | ||
|
||
for (Class<?> type : parameterTypes) { | ||
joiner.add(type.getSimpleName()); | ||
} | ||
|
||
desc.append(joiner); | ||
throw new UnknownConstructorException("Could not find constructor '" + desc.toString() + "' in " + clazz); | ||
} | ||
} | ||
|
||
/** | ||
* 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. | ||
*/ | ||
public static <T> Field findField( | ||
final Class<? super T> clazz, | ||
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(PatchworkRemappingService.remapFieldName(clazz, fieldName)); | ||
f.setAccessible(true); | ||
return f; | ||
} catch (Exception e) { | ||
throw new UnableToFindFieldException(e); | ||
} | ||
} | ||
|
||
public static class UnableToAccessFieldException extends RuntimeException { | ||
private UnableToAccessFieldException(Exception e) { | ||
super(e); | ||
} | ||
} | ||
|
||
public static class UnableToFindFieldException extends RuntimeException { | ||
private UnableToFindFieldException(Exception e) { | ||
super(e); | ||
} | ||
} | ||
|
||
public static class UnableToFindMethodException extends RuntimeException { | ||
public UnableToFindMethodException(Throwable failed) { | ||
super(failed); | ||
} | ||
} | ||
|
||
public static class UnknownConstructorException extends RuntimeException { | ||
public UnknownConstructorException(final String message) { | ||
super(message); | ||
} | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
patchwork-mappings/src/main/java/net/patchworkmc/api/mappings/PatchworkRemappingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Minecraft Forge, Patchwork Project | ||
* Copyright (c) 2016-2020, 2019-2020 | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation version 2.1 | ||
* of the License. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
|
||
package net.patchworkmc.api.mappings; | ||
|
||
import java.util.HashMap; | ||
|
||
import org.cadixdev.lorenz.MappingSet; | ||
import org.cadixdev.lorenz.model.Mapping; | ||
import org.cadixdev.lorenz.model.MethodMapping; | ||
|
||
import net.patchworkmc.impl.mappings.PatchworkMappings; | ||
|
||
/** | ||
* A class that Patchwork and mods that depend on it can use to map SRG names to | ||
* whatever the runtime mappings are. | ||
*/ | ||
public class PatchworkRemappingService { | ||
/** | ||
* Cache for method lookups so we can avoid doing a search every time. | ||
*/ | ||
private static final HashMap<Class<?>, HashMap<String, String>> methodCache = new HashMap<>(); | ||
|
||
public static String remapMethodName(Class<?> clazz, String srgName) { | ||
HashMap<String, String> cache = methodCache.computeIfAbsent(clazz, ignored -> new HashMap<>()); | ||
|
||
if (cache.containsKey(srgName)) { | ||
return cache.get(srgName); | ||
} | ||
|
||
MappingSet runtime2srg = PatchworkMappings.getMappingGenerator().getRuntimeToSrgMappings(); | ||
String runtimeName = runtime2srg.getClassMapping(clazz.getName()) | ||
.map(classMapping -> { | ||
for (MethodMapping methodMapping : classMapping.getMethodMappings()) { | ||
if (methodMapping.getDeobfuscatedName().equals(srgName)) { | ||
return methodMapping.getObfuscatedName(); | ||
} | ||
} | ||
|
||
return srgName; | ||
}).orElse(srgName); | ||
cache.put(srgName, runtimeName); | ||
return runtimeName; | ||
} | ||
|
||
public static String remapFieldName(Class<?> clazz, String srgName) { | ||
MappingSet runtime2srg = PatchworkMappings.getMappingGenerator().getRuntimeToSrgMappings(); | ||
MappingSet srg2runtime = PatchworkMappings.getMappingGenerator().getSrgToRuntimeMappings(); | ||
return runtime2srg.getClassMapping(clazz.getName()) | ||
.map(classMapping -> srg2runtime.getClassMapping(classMapping.getObfuscatedName()) | ||
.map(srgClassMapping -> srgClassMapping.getFieldMapping(srgName).map(Mapping::getDeobfuscatedName).orElse(srgName)) | ||
.orElseThrow(() -> new IllegalStateException("PatchworkRemappingService tried to map class " + clazz.getName() | ||
+ " from runtime -> srg -> runtime, but the last step doesn't exist."))) | ||
.orElse(srgName); | ||
} | ||
} |
Oops, something went wrong.