From 91b220fec6ffa69aa79051dcbbc5f816cd81a978 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 9 Dec 2023 21:43:44 +0000 Subject: [PATCH] Transformer now supports Field primitive sets and gets + cleanup --- .../hackery/ReflectionHackery.java | 206 ++++++++++++++---- .../ReflectionFieldTransformer.java | 85 +++++--- 2 files changed, 221 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/cleanroommc/hackery/ReflectionHackery.java b/src/main/java/com/cleanroommc/hackery/ReflectionHackery.java index 6517f98d0..f4bc5d365 100644 --- a/src/main/java/com/cleanroommc/hackery/ReflectionHackery.java +++ b/src/main/java/com/cleanroommc/hackery/ReflectionHackery.java @@ -1,9 +1,6 @@ package com.cleanroommc.hackery; -import com.cleanroommc.hackery.enums.EnumHackery; import jdk.internal.misc.Unsafe; -import net.minecraftforge.common.util.EnumHelper; -import net.minecraftforge.fml.common.FMLLog; import javax.annotation.Nullable; import java.lang.reflect.*; @@ -58,45 +55,85 @@ public static void stripFieldOfModifier(Field field, int modifierFlag) throws Il field$modifiers.setInt(field, field.getModifiers() & ~modifierFlag); } - public static void setField(Field field, Object owner, Object value) throws IllegalAccessException { if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); putHelper(field.getType(), unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); } else { putHelper(field.getType(), owner, unsafe.objectFieldOffset(field), value); } } - private static void putHelper(Class clazz, Object owner, long offset, Object value) { - if (clazz.equals(int.class)) { - unsafe.putInt(owner, offset, (int) value); - return; + public static void setBooleanField(Field field, Object owner, boolean value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putBoolean(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putBoolean(owner, unsafe.objectFieldOffset(field), value); } - if (clazz.equals(byte.class)) { - unsafe.putByte(owner, offset, (byte) value); - return; + } + + public static void setByteField(Field field, Object owner, byte value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putByte(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putByte(owner, unsafe.objectFieldOffset(field), value); } - if (clazz.equals(long.class)) { - unsafe.putLong(owner, offset, (long) value); - return; + } + + public static void setCharField(Field field, Object owner, char value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putChar(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putChar(owner, unsafe.objectFieldOffset(field), value); } - if (clazz.equals(float.class)) { - unsafe.putFloat(owner, offset, (float) value); - return; + } + + public static void setShortField(Field field, Object owner, short value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putShort(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putShort(owner, unsafe.objectFieldOffset(field), value); } - if (clazz.equals(double.class)) { - unsafe.putDouble(owner, offset, (double) value); - return; + } + + public static void setIntField(Field field, Object owner, int value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putInt(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putInt(owner, unsafe.objectFieldOffset(field), value); } - if (clazz.equals(boolean.class)) { - unsafe.putBoolean(owner, offset, (boolean) value); - return; + } + + public static void setLongField(Field field, Object owner, long value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putLong(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putLong(owner, unsafe.objectFieldOffset(field), value); } - if (clazz.equals(char.class)) { - unsafe.putChar(owner, offset, (char) value); - return; + } + + public static void setFloatField(Field field, Object owner, float value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putFloat(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putFloat(owner, unsafe.objectFieldOffset(field), value); + } + } + + public static void setDoubleField(Field field, Object owner, double value) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + unsafe.putDouble(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value); + } else { + unsafe.putDouble(owner, unsafe.objectFieldOffset(field), value); } - unsafe.putObject(owner, offset, value); } public static Object getField(Field field, Object owner) throws IllegalAccessException { @@ -108,29 +145,116 @@ public static Object getField(Field field, Object owner) throws IllegalAccessExc } } + public static boolean getBooleanField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getBoolean(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getBoolean(owner, unsafe.objectFieldOffset(field)); + } + } + + public static byte getByteField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getByte(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getByte(owner, unsafe.objectFieldOffset(field)); + } + } + + public static char getCharField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getChar(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getChar(owner, unsafe.objectFieldOffset(field)); + } + } + + public static short getShortField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getShort(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getShort(owner, unsafe.objectFieldOffset(field)); + } + } + + public static int getIntField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getInt(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getInt(owner, unsafe.objectFieldOffset(field)); + } + } + + public static long getLongField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getLong(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getLong(owner, unsafe.objectFieldOffset(field)); + } + } + + public static float getFloatField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getFloat(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getFloat(owner, unsafe.objectFieldOffset(field)); + } + } + + public static double getDoubleField(Field field, Object owner) throws IllegalAccessException { + if (Modifier.isStatic(field.getModifiers())) { + unsafe.ensureClassInitialized(field.getDeclaringClass()); + return unsafe.getDouble(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); + } else { + return unsafe.getDouble(owner, unsafe.objectFieldOffset(field)); + } + } + + private static void putHelper(Class clazz, Object owner, long offset, Object value) { + if (clazz == Integer.TYPE) { + unsafe.putInt(owner, offset, (int) value); + } else if (clazz == Byte.TYPE) { + unsafe.putByte(owner, offset, (byte) value); + } else if (clazz == Long.TYPE) { + unsafe.putLong(owner, offset, (long) value); + } else if (clazz == Float.TYPE) { + unsafe.putFloat(owner, offset, (float) value); + } else if (clazz == Double.TYPE) { + unsafe.putDouble(owner, offset, (double) value); + } else if (clazz == Boolean.TYPE) { + unsafe.putBoolean(owner, offset, (boolean) value); + } else if (clazz == Character.TYPE) { + unsafe.putChar(owner, offset, (char) value); + } else { + unsafe.putObject(owner, offset, value); + } + } + private static Object getHelper(Class clazz, Object owner, long offset) { - if (clazz.equals(int.class)) { + if (clazz == Integer.TYPE) { return unsafe.getInt(owner, offset); - } - if (clazz.equals(byte.class)) { + } else if (clazz == Byte.TYPE) { return unsafe.getByte(owner, offset); - } - if (clazz.equals(long.class)) { + } else if (clazz == Long.TYPE) { return unsafe.getLong(owner, offset); - } - if (clazz.equals(float.class)) { + } else if (clazz == Float.TYPE) { return unsafe.getFloat(owner, offset); - } - if (clazz.equals(double.class)) { + } else if (clazz == Double.TYPE) { return unsafe.getDouble(owner, offset); - } - if (clazz.equals(boolean.class)) { + } else if (clazz == Boolean.TYPE) { return unsafe.getBoolean(owner, offset); - } - if (clazz.equals(char.class)) { + } else if (clazz == Character.TYPE) { return unsafe.getChar(owner, offset); + } else { + return unsafe.getObject(owner, offset); } - return unsafe.getObject(owner, offset); } } diff --git a/src/main/java/net/minecraftforge/fml/common/asm/transformers/ReflectionFieldTransformer.java b/src/main/java/net/minecraftforge/fml/common/asm/transformers/ReflectionFieldTransformer.java index d3e2770c6..850d894a3 100644 --- a/src/main/java/net/minecraftforge/fml/common/asm/transformers/ReflectionFieldTransformer.java +++ b/src/main/java/net/minecraftforge/fml/common/asm/transformers/ReflectionFieldTransformer.java @@ -5,51 +5,77 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.*; import java.util.List; -import java.util.stream.Stream; -public class ReflectionFieldTransformer implements IClassTransformer { - private static final List excludeList = Stream.of("com.cleanroommc.hackery", "org.spongepowered", "net.minecraft", "com.google", "com.ibm.icu", "io.netty", "com.sun", "it.unimi.dsi", "oshi", "org.slf4j", "com.mojang", "zone.rong", "kotlin").toList(); +public class ReflectionFieldTransformer implements IClassTransformer +{ + private static final List EXCLUSIONS = List.of("com.cleanroommc.hackery", "org.spongepowered", "net.minecraft", "com.google", "com.ibm.icu", "io.netty", "com.sun", "it.unimi.dsi", "oshi", "org.slf4j", "com.mojang", "zone.rong", "kotlin"); + private static final String OUR_REFLECTION_CLASS = "com/cleanroommc/hackery/ReflectionHackery"; + private static String replaceInstruction(InsnList insnList, MethodInsnNode oldNode, String methodName, String methodDescriptor) + { + insnList.set(oldNode, new MethodInsnNode(Opcodes.INVOKESTATIC, OUR_REFLECTION_CLASS, methodName, methodDescriptor)); + return oldNode.name + " -> " + methodName; + } @Override - public byte[] transform(String s, String s1, byte[] bytes) { - if (bytes == null) { + public byte[] transform(String s, String s1, byte[] bytes) + { + if (bytes == null) + { return null; } - for (String str: excludeList) { + for (String str: EXCLUSIONS) + { if (s1.startsWith(str)) + { return bytes; + } } - //System.out.println(s1); ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); - if (classNode.methods != null) { - for (MethodNode methodNode : classNode.methods) { - if (methodNode.instructions != null) { - for (AbstractInsnNode insnNode : methodNode.instructions) { - if (insnNode.getType() == AbstractInsnNode.METHOD_INSN) { - if (insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { - if (((MethodInsnNode)insnNode).owner.equals("java/lang/reflect/Field")) { - if (((MethodInsnNode)insnNode).name.equals("set")) { - methodNode.instructions.insert(insnNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/cleanroommc/hackery/ReflectionHackery", "setField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;Ljava/lang/Object;)V")); - methodNode.instructions.remove(insnNode); - FMLLog.log.info(s1 + "'s SET transforming"); - } - - if (((MethodInsnNode)insnNode).name.equals("get")) { - methodNode.instructions.insert(insnNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/cleanroommc/hackery/ReflectionHackery", "getField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;")); - methodNode.instructions.remove(insnNode); - FMLLog.log.info(s1 + "'s GET transforming"); - } + if (classNode.methods != null) + { + for (MethodNode methodNode : classNode.methods) + { + InsnList instructions = methodNode.instructions; + if (instructions != null) + { + for (AbstractInsnNode insnNode : instructions) + { + if (insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL && insnNode instanceof MethodInsnNode methodInsnNode) + { + if ("java/lang/reflect/Field".equals(methodInsnNode.owner)) + { + String result = switch (methodInsnNode.name) + { + case "set" -> replaceInstruction(instructions, methodInsnNode, "setField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;Ljava/lang/Object;)V"); + case "get" -> replaceInstruction(instructions, methodInsnNode, "getField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;"); + case "setBoolean" -> replaceInstruction(instructions, methodInsnNode, "setBooleanField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;Z)V"); + case "getBoolean" -> replaceInstruction(instructions, methodInsnNode, "getBooleanField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Z"); + case "setByte" -> replaceInstruction(instructions, methodInsnNode, "setByteField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;B)V"); + case "getByte" -> replaceInstruction(instructions, methodInsnNode, "getByteField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)B"); + case "setChar" -> replaceInstruction(instructions, methodInsnNode, "setCharField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;C)V"); + case "getChar" -> replaceInstruction(instructions, methodInsnNode, "getCharField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)C"); + case "setShort" -> replaceInstruction(instructions, methodInsnNode, "setShortField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;S)V"); + case "getShort" -> replaceInstruction(instructions, methodInsnNode, "getShortField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)S"); + case "setInt" -> replaceInstruction(instructions, methodInsnNode, "setIntField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;I)V"); + case "getInt" -> replaceInstruction(instructions, methodInsnNode, "getIntField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)I"); + case "setLong" -> replaceInstruction(instructions, methodInsnNode, "setLongField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;J)V"); + case "getLong" -> replaceInstruction(instructions, methodInsnNode, "getLongField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J"); + case "setFloat" -> replaceInstruction(instructions, methodInsnNode, "setFloatField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;F)V"); + case "getFloat" -> replaceInstruction(instructions, methodInsnNode, "getFloatField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)F"); + case "setDouble" -> replaceInstruction(instructions, methodInsnNode, "setDoubleField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;D)V"); + case "getDouble" -> replaceInstruction(instructions, methodInsnNode, "getDoubleField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)D"); + default -> null; + }; + if (result != null) { + FMLLog.log.info("[{}]: Transformed {}", s1, result); } } } @@ -63,4 +89,5 @@ public byte[] transform(String s, String s1, byte[] bytes) { classNode.accept(classWriter); return classWriter.toByteArray(); } + }