-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Client/Server Only Methods/Fields in Cfg
- Loading branch information
1 parent
a51316b
commit aa16cee
Showing
6 changed files
with
651 additions
and
1 deletion.
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
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
134 changes: 134 additions & 0 deletions
134
src/main/java/com/nomiceu/nomilabs/core/LabsTransformer.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,134 @@ | ||
package com.nomiceu.nomilabs.core; | ||
|
||
import java.util.*; | ||
|
||
import net.minecraft.launchwrapper.IClassTransformer; | ||
import net.minecraftforge.fml.common.FMLLog; | ||
import net.minecraftforge.fml.relauncher.FMLLaunchHandler; | ||
import net.minecraftforge.fml.relauncher.Side; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.apache.logging.log4j.Level; | ||
import org.objectweb.asm.*; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.FieldNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
|
||
import com.nomiceu.nomilabs.config.LabsConfig; | ||
|
||
/** | ||
* @apiNote Inspired by net.minecraftforge.fml.common.asm.transformers.SideTransformer | ||
* <p> | ||
* Allows for Client/Server Only Methods to be specified via Config. | ||
*/ | ||
@SuppressWarnings({ "unused", "deprecation" }) | ||
public class LabsTransformer implements IClassTransformer, Opcodes { | ||
|
||
// Map of Class -> Pair Of Obf Mapping Lists, Left is Fields, Right is Methods | ||
private static Map<String, Pair<List<ObfMapping>, List<ObfMapping>>> toRemove; | ||
|
||
/** | ||
* Must setup here so FMLCommonHandler has time to load. | ||
*/ | ||
private static void setupToRemove() { | ||
if (toRemove != null) return; | ||
|
||
// Can't use Object2ObjectOpenHashMap as in ASM stage | ||
toRemove = new HashMap<>(); | ||
String[] methodsRemove; | ||
String[] fieldsRemove; | ||
|
||
if (FMLLaunchHandler.side() == Side.SERVER) { | ||
// Remove CLIENT side methods & fields | ||
fieldsRemove = LabsConfig.advanced.clientSideFields; | ||
methodsRemove = LabsConfig.advanced.clientSideMethods; | ||
} else { | ||
// Remove SERVER side methods & fields | ||
fieldsRemove = LabsConfig.advanced.serverSideFields; | ||
methodsRemove = LabsConfig.advanced.serverSideMethods; | ||
} | ||
|
||
for (var field : fieldsRemove) { | ||
var parts = field.split("@"); | ||
if (parts.length != 2) | ||
throw new IllegalStateException("Invalid Field Remove: " + field + | ||
"! Must have Two Parts, Seperated by '@', Got " + parts.length + "!"); | ||
|
||
toRemove.computeIfAbsent(parts[0], (k) -> Pair.of(new ArrayList<>(), new ArrayList<>())) | ||
.getLeft().add(new ObfMapping(parts[0], parts[1])); | ||
} | ||
|
||
for (var method : methodsRemove) { | ||
var parts = method.split("@"); | ||
if (parts.length != 3) | ||
throw new IllegalStateException("Invalid Method Remove: " + method + | ||
"! Must have Three Parts, Seperated by '@', Got " + parts.length + "!"); | ||
|
||
toRemove.computeIfAbsent(parts[0], (k) -> Pair.of(new ArrayList<>(), new ArrayList<>())) | ||
.getRight().add(new ObfMapping(parts[0], parts[1], parts[2])); | ||
} | ||
|
||
FMLLog.log("LabsASM", Level.DEBUG, "Computed To Remove: %s", toRemove); | ||
} | ||
|
||
@Override | ||
public byte[] transform(String name, String transformedName, byte[] basicClass) { | ||
setupToRemove(); | ||
|
||
String internalName = transformedName.replace('.', '/'); | ||
if (!toRemove.containsKey(internalName)) return basicClass; | ||
|
||
var pair = toRemove.get(internalName); | ||
var fieldsRemove = pair.getLeft(); | ||
var methodsRemove = pair.getRight(); | ||
|
||
ClassNode classNode = new ClassNode(); | ||
ClassReader classReader = new ClassReader(basicClass); | ||
classReader.accept(classNode, 0); | ||
|
||
for (var toRemove : fieldsRemove) { | ||
Iterator<FieldNode> fields = classNode.fields.iterator(); | ||
boolean found = false; | ||
while (fields.hasNext()) { | ||
FieldNode field = fields.next(); | ||
if (!toRemove.s_name.equals(field.name)) continue; | ||
|
||
FMLLog.log("LabsASM", Level.WARN, "Removing Field: %s.%s", classNode.name, field.name); | ||
fields.remove(); | ||
found = true; | ||
break; | ||
} | ||
|
||
if (!found) | ||
throw new IllegalStateException("Could not find Field " + classNode.name + "." + toRemove.s_name + "!"); | ||
} | ||
|
||
var gatherer = new LambdaGatherer(); | ||
for (var toRemove : methodsRemove) { | ||
Iterator<MethodNode> methods = classNode.methods.iterator(); | ||
boolean found = false; | ||
while (methods.hasNext()) { | ||
MethodNode method = methods.next(); | ||
if (!toRemove.matches(method)) continue; | ||
|
||
FMLLog.log("LabsASM", Level.WARN, "Removing Method: %s.%s%s", classNode.name, method.name, | ||
method.desc); | ||
methods.remove(); | ||
gatherer.accept(method); | ||
found = true; | ||
break; | ||
} | ||
|
||
if (!found) | ||
throw new IllegalStateException( | ||
"Could not find Method " + classNode.name + "." + toRemove.s_name + toRemove.s_desc + "!"); | ||
} | ||
|
||
LambdaGatherer.removeDynamicLambdaMethods(gatherer, classNode); | ||
|
||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); | ||
classNode.accept(writer); | ||
|
||
return writer.toByteArray(); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
src/main/java/com/nomiceu/nomilabs/core/LambdaGatherer.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,81 @@ | ||
package com.nomiceu.nomilabs.core; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.ListIterator; | ||
|
||
import net.minecraftforge.fml.common.FMLLog; | ||
|
||
import org.apache.logging.log4j.Level; | ||
import org.objectweb.asm.Handle; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
|
||
/** | ||
* @apiNote net.minecraftforge.fml.common.asm.transformers.SideTransformer.LambdaGatherer | ||
*/ | ||
@SuppressWarnings("deprecation") | ||
public class LambdaGatherer extends MethodVisitor { | ||
|
||
private static final Handle META_FACTORY = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", | ||
"metafactory", | ||
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", | ||
false); | ||
private final List<Handle> dynamicLambdaHandles = new ArrayList<>(); | ||
|
||
public LambdaGatherer() { | ||
super(Opcodes.ASM5); | ||
} | ||
|
||
public void accept(MethodNode method) { | ||
ListIterator<AbstractInsnNode> insnNodeIterator = method.instructions.iterator(); | ||
while (insnNodeIterator.hasNext()) { | ||
AbstractInsnNode insnNode = insnNodeIterator.next(); | ||
if (insnNode.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN) { | ||
insnNode.accept(this); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { | ||
if (META_FACTORY.equals(bsm)) { | ||
Handle dynamicLambdaHandle = (Handle) bsmArgs[1]; | ||
dynamicLambdaHandles.add(dynamicLambdaHandle); | ||
} | ||
} | ||
|
||
public List<Handle> getDynamicLambdaHandles() { | ||
return dynamicLambdaHandles; | ||
} | ||
|
||
/** | ||
* Remove dynamic synthetic lambda methods that are inside of removed methods. | ||
*/ | ||
public static void removeDynamicLambdaMethods(LambdaGatherer gatherer, ClassNode classNode) { | ||
for (List<Handle> dynamicLambdaHandles = gatherer.getDynamicLambdaHandles(); !dynamicLambdaHandles | ||
.isEmpty(); dynamicLambdaHandles = gatherer.getDynamicLambdaHandles()) { | ||
gatherer = new LambdaGatherer(); | ||
var methods = classNode.methods.iterator(); | ||
while (methods.hasNext()) { | ||
MethodNode method = methods.next(); | ||
if ((method.access & Opcodes.ACC_SYNTHETIC) == 0) continue; | ||
|
||
for (Handle dynamicLambdaHandle : dynamicLambdaHandles) { | ||
if (method.name.equals(dynamicLambdaHandle.getName()) && | ||
method.desc.equals(dynamicLambdaHandle.getDesc())) { | ||
|
||
FMLLog.log("LabsASM", Level.WARN, "Removing Method: %s.%s%s", classNode.name, method.name, | ||
method.desc); | ||
|
||
methods.remove(); | ||
gatherer.accept(method); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.