From edb695900950f069405083fd2df321063f481267 Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:01:43 +0200 Subject: [PATCH] Get rid of CapabilityTokenSubclass and ObjectHolderDefinalize (#53) --- .../common/asm/CapabilityTokenSubclass.java | 120 ------------------ .../common/asm/ObjectHolderDefinalize.java | 110 ---------------- .../asm}/RuntimeDistCleaner.java | 2 +- .../net/neoforged/fml/loading/FMLLoader.java | 2 +- ...odlauncher.serviceapi.ILaunchPluginService | 6 +- 5 files changed, 4 insertions(+), 236 deletions(-) delete mode 100644 loader/src/main/java/net/neoforged/fml/common/asm/CapabilityTokenSubclass.java delete mode 100644 loader/src/main/java/net/neoforged/fml/common/asm/ObjectHolderDefinalize.java rename loader/src/main/java/net/neoforged/fml/{loading => common/asm}/RuntimeDistCleaner.java (99%) diff --git a/loader/src/main/java/net/neoforged/fml/common/asm/CapabilityTokenSubclass.java b/loader/src/main/java/net/neoforged/fml/common/asm/CapabilityTokenSubclass.java deleted file mode 100644 index 376951eb1..000000000 --- a/loader/src/main/java/net/neoforged/fml/common/asm/CapabilityTokenSubclass.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml.common.asm; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.EnumSet; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.signature.SignatureReader; -import org.objectweb.asm.signature.SignatureVisitor; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; - -/** - * Implements getType() in CapabilityToken subclasses. - * - * Using the class's signature to determine the generic type of TypeToken, and then implements getType() using that value. - *
- * Example:
- *
- *  new CapabilityToken<String>(){}
- *  Has the signature "CapabilityToken<Ljava/lang/String;>"
- *
- *  Implements the method:
- *  public String getType() {
- *    return "java/lang/String";
- *  }
- * 
- */ -public class CapabilityTokenSubclass implements ILaunchPluginService { - - private final String FUNC_NAME = "getType"; - private final String FUNC_DESC = "()Ljava/lang/String;"; - private final String CAP_INJECT = "net/neoforged/neoforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading. - - @Override - public String name() { - return "capability_token_subclass"; - } - - private static final EnumSet YAY = EnumSet.of(Phase.AFTER); - private static final EnumSet NAY = EnumSet.noneOf(Phase.class); - - @Override - public EnumSet handlesClass(Type classType, boolean isEmpty) - { - return isEmpty ? NAY : YAY; - } - - @Override - public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) - { - if (CAP_INJECT.equals(classNode.name)) - { - for (MethodNode mtd : classNode.methods) - { - if (FUNC_NAME.equals(mtd.name) && FUNC_DESC.equals(mtd.desc)) - { - mtd.access &= ~Opcodes.ACC_FINAL; // We have it final in code so people don't override it, cuz that'd be stupid, and make our transformer more complicated. - } - } - return ComputeFlags.SIMPLE_REWRITE; - } - else if (CAP_INJECT.equals(classNode.superName)) - { - Holder cls = new Holder(); - - SignatureReader reader = new SignatureReader(classNode.signature); // Having a node version of this would probably be useful. - reader.accept(new SignatureVisitor(Opcodes.ASM9) - { - Deque stack = new ArrayDeque<>(); - - @Override - public void visitClassType(final String name) - { - stack.push(name); - } - - @Override - public void visitInnerClassType(final String name) - { - stack.push(stack.pop() + '$' + name); - } - - @Override - public void visitEnd() - { - var val = stack.pop(); - if (!stack.isEmpty() && CAP_INJECT.equals(stack.peek())) - cls.value = val; - } - }); - - if (cls.value == null) - throw new IllegalStateException("Could not find signature for CapabilityToken on " + classNode.name + " from " + classNode.signature); - - var mtd = classNode.visitMethod(Opcodes.ACC_PUBLIC, FUNC_NAME, FUNC_DESC, null, new String[0]); - mtd.visitLdcInsn(cls.value); - mtd.visitInsn(Opcodes.ARETURN); - mtd.visitEnd(); - return ComputeFlags.COMPUTE_MAXS; - } - else - { - return ComputeFlags.NO_REWRITE; - } - } - - private static class Holder { - String value; - } - -} diff --git a/loader/src/main/java/net/neoforged/fml/common/asm/ObjectHolderDefinalize.java b/loader/src/main/java/net/neoforged/fml/common/asm/ObjectHolderDefinalize.java deleted file mode 100644 index 6fdc5e391..000000000 --- a/loader/src/main/java/net/neoforged/fml/common/asm/ObjectHolderDefinalize.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml.common.asm; - -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; - -/** - * Removes the final modifier from fields with the @ObjectHolder annotation, prevents the JITer from in lining them so our runtime replacements can work. - * Will also de-finalize all fields in on class level annotations. - */ -public class ObjectHolderDefinalize implements ILaunchPluginService { - // Hardcoded map of vanilla classes that should have object holders for each field of the given registry type. - // IMPORTANT: Updates to this collection must be reflected in ObjectHolderRegistry. Duplicated cuz classloaders, yay! - // Classnames are validated in ObjectHolderRegistry. - private static final Map VANILLA_OBJECT_HOLDERS = Stream.of( - new VanillaObjectHolderData("net.minecraft.world.level.block.Blocks", "block", "net.minecraft.world.level.block.Block"), - new VanillaObjectHolderData("net.minecraft.world.item.Items", "item", "net.minecraft.world.item.Item"), - new VanillaObjectHolderData("net.minecraft.world.item.enchantment.Enchantments", "enchantment", "net.minecraft.world.item.enchantment.Enchantment"), - new VanillaObjectHolderData("net.minecraft.world.effect.MobEffects", "mob_effect", "net.minecraft.world.effect.MobEffect"), - new VanillaObjectHolderData("net.minecraft.core.particles.ParticleTypes", "particle_type", "net.minecraft.core.particles.ParticleType"), - new VanillaObjectHolderData("net.minecraft.sounds.SoundEvents", "sound_event", "net.minecraft.sounds.SoundEvent") - ).collect(Collectors.toMap(VanillaObjectHolderData::holderClass, Function.identity())); - private final String OBJECT_HOLDER = "Lnet/neoforged/neoforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading. - - @Override - public String name() { - return "object_holder_definalize"; - } - - private static final EnumSet YAY = EnumSet.of(Phase.AFTER); - private static final EnumSet NAY = EnumSet.noneOf(Phase.class); - - @Override - public EnumSet handlesClass(Type classType, boolean isEmpty) - { - return isEmpty ? NAY : YAY; - } - - private boolean hasHolder(List lst) - { - return lst != null && lst.stream().anyMatch(n -> n.desc.equals(OBJECT_HOLDER)); - } - - private String getValue(List lst) - { - AnnotationNode ann = lst.stream().filter(n -> n.desc.equals(OBJECT_HOLDER)).findFirst().get(); - if (ann.values != null) - { - for (int x = 0; x < ann.values.size() - 1; x += 2) { - if (ann.values.get(x).equals("value")) { - return (String)ann.values.get(x + 1); - } - } - } - return null; - } - - @Override - public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) - { - final AtomicBoolean changes = new AtomicBoolean(); - //Must be public static finals, and non-array objects - final int flags = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; - - //Fix Annotated Fields before injecting from class level - classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f -> - { - int prev = f.access; - f.access &= ~Opcodes.ACC_FINAL; //Strip final - f.access |= Opcodes.ACC_SYNTHETIC; //Add Synthetic so we can check in runtime. ? Good idea? - changes.compareAndSet(false, prev != f.access); - }); - - if (VANILLA_OBJECT_HOLDERS.containsKey(classType.getClassName())) //Class level, de-finalize all fields and add @ObjectHolder to them! - { - classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L")).forEach(f -> - { - int prev = f.access; - f.access &= ~Opcodes.ACC_FINAL; - f.access |= Opcodes.ACC_SYNTHETIC; - /*if (!hasHolder(f.visibleAnnotations)) //Add field level annotation, doesn't do anything until after we figure out how ASMDataTable is gatherered - { - if (value == null) - f.visitAnnotation(OBJECT_HOLDER, true); - else - f.visitAnnotation(OBJECT_HOLDER, true).visit("value", value + ":" + f.name.toLowerCase()); - }*/ - changes.compareAndSet(false, prev != f.access); - }); - } - return changes.get() ? ComputeFlags.SIMPLE_REWRITE : ComputeFlags.NO_REWRITE; - } - - private record VanillaObjectHolderData(String holderClass, String registryName, String registryType) {} -} diff --git a/loader/src/main/java/net/neoforged/fml/loading/RuntimeDistCleaner.java b/loader/src/main/java/net/neoforged/fml/common/asm/RuntimeDistCleaner.java similarity index 99% rename from loader/src/main/java/net/neoforged/fml/loading/RuntimeDistCleaner.java rename to loader/src/main/java/net/neoforged/fml/common/asm/RuntimeDistCleaner.java index 22146883c..7fef92a35 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/RuntimeDistCleaner.java +++ b/loader/src/main/java/net/neoforged/fml/common/asm/RuntimeDistCleaner.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: LGPL-2.1-only */ -package net.neoforged.fml.loading; +package net.neoforged.fml.common.asm; import java.util.ArrayList; import java.util.Collections; diff --git a/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java b/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java index 9e86b7dd0..a1efb7d2e 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java +++ b/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java @@ -11,6 +11,7 @@ import cpw.mods.modlauncher.util.ServiceLoaderUtils; import net.neoforged.accesstransformer.api.AccessTransformerEngine; import net.neoforged.accesstransformer.ml.AccessTransformerService; +import net.neoforged.fml.common.asm.RuntimeDistCleaner; import net.neoforged.fml.loading.mixin.DeferredMixinConfigRegistration; import net.neoforged.fml.loading.moddiscovery.BackgroundScanHandler; import net.neoforged.fml.loading.moddiscovery.ModDiscoverer; @@ -20,7 +21,6 @@ import net.neoforged.fml.loading.targets.CommonLaunchHandler; import net.neoforged.neoforgespi.Environment; import net.neoforged.neoforgespi.coremod.ICoreModProvider; -import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import java.io.IOException; diff --git a/loader/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService b/loader/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService index 22f91d676..580f7de7c 100644 --- a/loader/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService +++ b/loader/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService @@ -1,5 +1,3 @@ net.neoforged.fml.loading.log4j.SLF4JFixerLaunchPluginService -net.neoforged.fml.loading.RuntimeDistCleaner -net.neoforged.fml.common.asm.RuntimeEnumExtender -net.neoforged.fml.common.asm.ObjectHolderDefinalize -net.neoforged.fml.common.asm.CapabilityTokenSubclass \ No newline at end of file +net.neoforged.fml.common.asm.RuntimeDistCleaner +net.neoforged.fml.common.asm.RuntimeEnumExtender \ No newline at end of file