Skip to content

Commit

Permalink
Change: Don't generate classes via Mixin.
Browse files Browse the repository at this point in the history
This breaks in dev on certain Forge versions, so we have to go via the stdlib instead.
Thankfully we don't need to reference any non-JDK classes in our generated ones.
  • Loading branch information
LlamaLad7 committed Mar 27, 2023
1 parent 16f522e commit e149b5c
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperationInjectionInfo;
import com.llamalad7.mixinextras.sugar.impl.SugarApplicatorExtension;
import com.llamalad7.mixinextras.sugar.impl.SugarWrapperInjectionInfo;
import com.llamalad7.mixinextras.sugar.impl.ref.LocalRefClassGenerator;
import com.llamalad7.mixinextras.utils.MixinInternals;
import com.llamalad7.mixinextras.utils.PackageUtils;
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
Expand Down Expand Up @@ -42,8 +41,6 @@ static void initialize(boolean runtime) {
MixinInternals.registerExtension(new SugarApplicatorExtension());
MixinInternals.registerExtension(new WrapOperationApplicatorExtension());

MixinInternals.registerClassGenerator(LocalRefClassGenerator::new);

PackageUtils.init();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void inject(Target target, InjectionNode node) {
}

private void initAndLoadLocalRef(Target target, InjectionNode node, int index) {
String refName = LocalRefClassGenerator.getInstance().getForType(mixin, targetLocalType);
String refName = LocalRefClassGenerator.getForType(targetLocalType);
int refIndex = getOrCreateRef(target, node, index, refName);
target.insns.insertBefore(node.getCurrentTarget(), new VarInsnNode(Opcodes.ALOAD, refIndex));
}
Expand All @@ -93,7 +93,7 @@ private int getOrCreateRef(Target target, InjectionNode node, int index, String
// Make and store the reference object with the local's current value.
InsnList before = new InsnList();
LocalRefUtils.generateWrapping(
mixin, before, targetLocalType,
before, targetLocalType,
() -> before.add(new VarInsnNode(targetLocalType.getOpcode(Opcodes.ILOAD), index))
);
before.add(new VarInsnNode(Opcodes.ASTORE, refIndex));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void prepare(Target target, InjectionNodes.InjectionNode node) {
void inject(Target target, InjectionNodes.InjectionNode node) {
if (needsSetup) {
InsnList init = new InsnList();
LocalRefUtils.generateWrapping(mixin, init, innerType, () -> init.add(new InsnNode(ASMUtils.getDummyOpcodeForType(innerType))));
LocalRefUtils.generateWrapping(init, innerType, () -> init.add(new InsnNode(ASMUtils.getDummyOpcodeForType(innerType))));
init.add(new VarInsnNode(Opcodes.ASTORE, localRefIndex));
target.insertBefore(target.insns.getFirst(), init);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,104 +1,75 @@
package com.llamalad7.mixinextras.sugar.impl.ref;

import com.llamalad7.mixinextras.sugar.ref.*;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import com.llamalad7.mixinextras.utils.ClassGenUtils;
import com.llamalad7.mixinextras.utils.PackageUtils;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.transformer.ext.IClassGenerator;
import org.spongepowered.asm.service.ISyntheticClassInfo;
import org.spongepowered.asm.util.IConsumer;

import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;

/**
* We must generate implementations of {@link LocalRef} and co. at runtime so they implement all the required interfaces.
* These objects will be shared between handlers that possibly use different relocations of MixinExtras.
*/
public class LocalRefClassGenerator implements IClassGenerator {
private static LocalRefClassGenerator INSTANCE;
public class LocalRefClassGenerator {
private static final String IMPL_PACKAGE = StringUtils.substringBeforeLast(LocalRefClassGenerator.class.getName(), ".").replace('.', '/');
private static final Map<Class<?>, String> interfaceToImpl = new HashMap<>();

private final IConsumer<ISyntheticClassInfo> registry;
private final Map<Class<?>, LocalRefClassInfo> interfaceToInfo = new HashMap<>();
private final Map<String, LocalRefClassInfo> nameToInfo = new HashMap<>();

public LocalRefClassGenerator(IConsumer<ISyntheticClassInfo> registry) {
this.registry = registry;
INSTANCE = this;
}

@Override
public String getName() {
return "MixinExtras LocalRefImpl";
}

@Override
public boolean generate(String name, ClassNode classNode) {
LocalRefClassInfo info = nameToInfo.get(name);
if (info == null) {
return false;
}
classNode.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, info.getName(), null,
Type.getInternalName(Object.class), null);
this.generateClass(classNode, info);
classNode.visitEnd();
info.markAsLoaded();
return true;
}

public String getForType(IMixinInfo mixin, Type type) {
public static String getForType(Type type) {
Class<?> refInterface = LocalRefUtils.getInterfaceFor(type);
LocalRefClassInfo info = interfaceToInfo.get(refInterface);
if (info != null) {
return info.getName();
String owner = interfaceToImpl.get(refInterface);
if (owner != null) {
return owner;
}
owner = IMPL_PACKAGE + '/' + StringUtils.substringAfterLast(refInterface.getName(), ".") + "Impl";
String desc = type.getDescriptor();
info = new LocalRefClassInfo(mixin, refInterface, desc.length() == 1 ? desc : Type.getDescriptor(Object.class));
interfaceToInfo.put(refInterface, info);
nameToInfo.put(info.getClassName(), info);
registry.accept(info);
return info.getName();
String innerDesc = desc.length() == 1 ? desc : Type.getDescriptor(Object.class);
interfaceToImpl.put(refInterface, owner);
ClassNode node = new ClassNode();
node.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, owner, null, Type.getInternalName(Object.class), null);
generateClass(node, owner, innerDesc, refInterface.getName());
ClassGenUtils.defineClass(node, MethodHandles.lookup());
return owner;
}

private void generateClass(ClassNode node, LocalRefClassInfo info) {
for (String name : PackageUtils.getAllClassNames(info.getInterfaceName())) {
private static void generateClass(ClassNode node, String owner, String innerDesc, String interfaceName) {
for (String name : PackageUtils.getAllClassNames(interfaceName)) {
node.interfaces.add(name.replace('.', '/'));
}
node.visitField(Opcodes.ACC_PRIVATE, "value", info.getDesc(), null, null);
node.visitField(Opcodes.ACC_PRIVATE, "value", innerDesc, null, null);

MethodVisitor ctor = node.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(" + info.getDesc() + ")V", null, null);
MethodVisitor ctor = node.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(" + innerDesc + ")V", null, null);
ctor.visitCode();
ctor.visitVarInsn(Opcodes.ALOAD, 0);
ctor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false);
ctor.visitVarInsn(Opcodes.ALOAD, 0);
ctor.visitVarInsn(Type.getType(info.getDesc()).getOpcode(Opcodes.ILOAD), 1);
ctor.visitFieldInsn(Opcodes.PUTFIELD, info.getName(), "value", info.getDesc());
ctor.visitVarInsn(Type.getType(innerDesc).getOpcode(Opcodes.ILOAD), 1);
ctor.visitFieldInsn(Opcodes.PUTFIELD, owner, "value", innerDesc);
ctor.visitInsn(Opcodes.RETURN);
ctor.visitMaxs(3, 3);
ctor.visitEnd();

MethodVisitor getter = node.visitMethod(Opcodes.ACC_PUBLIC, "get", "()" + info.getDesc(), null, null);
MethodVisitor getter = node.visitMethod(Opcodes.ACC_PUBLIC, "get", "()" + innerDesc, null, null);
getter.visitCode();
getter.visitVarInsn(Opcodes.ALOAD, 0);
getter.visitFieldInsn(Opcodes.GETFIELD, info.getName(), "value", info.getDesc());
getter.visitInsn(Type.getType(info.getDesc()).getOpcode(Opcodes.IRETURN));
getter.visitFieldInsn(Opcodes.GETFIELD, owner, "value", innerDesc);
getter.visitInsn(Type.getType(innerDesc).getOpcode(Opcodes.IRETURN));
getter.visitMaxs(2, 1);
getter.visitEnd();

MethodVisitor setter = node.visitMethod(Opcodes.ACC_PUBLIC, "set", "(" + info.getDesc() + ")V", null, null);
MethodVisitor setter = node.visitMethod(Opcodes.ACC_PUBLIC, "set", "(" + innerDesc + ")V", null, null);
setter.visitCode();
setter.visitVarInsn(Opcodes.ALOAD, 0);
setter.visitVarInsn(Type.getType(info.getDesc()).getOpcode(Opcodes.ILOAD), 1);
setter.visitFieldInsn(Opcodes.PUTFIELD, info.getName(), "value", info.getDesc());
setter.visitVarInsn(Type.getType(innerDesc).getOpcode(Opcodes.ILOAD), 1);
setter.visitFieldInsn(Opcodes.PUTFIELD, owner, "value", innerDesc);
setter.visitInsn(Opcodes.RETURN);
setter.visitMaxs(3, 3);
setter.visitEnd();
}

public static LocalRefClassGenerator getInstance() {
return INSTANCE;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;

public class LocalRefUtils {
public static Class<?> getInterfaceFor(Type type) {
Expand Down Expand Up @@ -52,8 +51,8 @@ public static Type getTargetType(Type type, Type generic) {
}
}

public static void generateWrapping(IMixinInfo mixin, InsnList insns, Type innerType, Runnable load) {
String refImpl = LocalRefClassGenerator.getInstance().getForType(mixin, innerType);
public static void generateWrapping(InsnList insns, Type innerType, Runnable load) {
String refImpl = LocalRefClassGenerator.getForType(innerType);

insns.add(new TypeInsnNode(Opcodes.NEW, refImpl));
insns.add(new InsnNode(Opcodes.DUP));
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/com/llamalad7/mixinextras/utils/ClassGenUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.llamalad7.mixinextras.utils;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import sun.misc.Unsafe;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;

public class ClassGenUtils {
private static final Definer DEFINER;

static {
Definer theDefiner;
try {
Unsafe.class.getMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
theDefiner = (name, bytes, scope) -> unsafe.defineClass(name, bytes, 0, bytes.length, scope.lookupClass().getClassLoader(), scope.lookupClass().getProtectionDomain());
} catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e1) {
try {
//noinspection JavaReflectionMemberAccess
Method defineClass = MethodHandles.Lookup.class.getMethod("defineClass", byte[].class);
theDefiner = (name, bytes, scope) -> {
try {
//noinspection PrimitiveArrayArgumentToVarargsMethod
defineClass.invoke(scope, bytes);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
} catch (NoSuchMethodException e2) {
RuntimeException e = new RuntimeException("Could not resolve class definer! Please report to LlamaLad7.");
e.addSuppressed(e1);
e.addSuppressed(e2);
throw e;
}
}
DEFINER = theDefiner;
}

public static void defineClass(ClassNode node, MethodHandles.Lookup scope) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
node.accept(writer);
DEFINER.define(node.name.replace('/', '.'), writer.toByteArray(), scope);
}

@FunctionalInterface
private interface Definer {
void define(String name, byte[] bytes, MethodHandles.Lookup scope);
}
}
20 changes: 0 additions & 20 deletions src/main/java/com/llamalad7/mixinextras/utils/MixinInternals.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,18 @@
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.injection.code.Injector;
import org.spongepowered.asm.mixin.injection.invoke.arg.ArgsClassGenerator;
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode;
import org.spongepowered.asm.mixin.injection.struct.Target;
import org.spongepowered.asm.mixin.transformer.IMixinTransformer;
import org.spongepowered.asm.mixin.transformer.ext.Extensions;
import org.spongepowered.asm.mixin.transformer.ext.IClassGenerator;
import org.spongepowered.asm.mixin.transformer.ext.IExtension;
import org.spongepowered.asm.mixin.transformer.ext.ITargetClassContext;
import org.spongepowered.asm.service.ISyntheticClassInfo;
import org.spongepowered.asm.util.IConsumer;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;

/**
* Mumfrey, look away.
Expand All @@ -34,7 +29,6 @@ public class MixinInternals {
private static final Field EXTENSIONS_FIELD;
private static final Field ACTIVE_EXTENSIONS_FIELD;
private static final Field INJECTION_INFO_TARGET_NODES_FIELD;
private static final Field ARGS_CLASS_GENERATOR_REGISTRY_FIELD;
private static final Field INJECTION_NODE_DECORATIONS_FIELD;
private static final Field INJECTION_INFO_INJECTOR_FIELD;

Expand All @@ -55,8 +49,6 @@ public class MixinInternals {
EXTENSIONS_FIELD.setAccessible(true);
ACTIVE_EXTENSIONS_FIELD = Extensions.class.getDeclaredField("activeExtensions");
ACTIVE_EXTENSIONS_FIELD.setAccessible(true);
ARGS_CLASS_GENERATOR_REGISTRY_FIELD = ArgsClassGenerator.class.getDeclaredField("registry");
ARGS_CLASS_GENERATOR_REGISTRY_FIELD.setAccessible(true);
INJECTION_NODE_DECORATIONS_FIELD = InjectionNode.class.getDeclaredField("decorations");
INJECTION_NODE_DECORATIONS_FIELD.setAccessible(true);
INJECTION_INFO_INJECTOR_FIELD = InjectionInfo.class.getDeclaredField("injector");
Expand Down Expand Up @@ -122,18 +114,6 @@ private static void addExtension(List<IExtension> extensions, IExtension newExte
}
}

public static void registerClassGenerator(Function<IConsumer<ISyntheticClassInfo>, IClassGenerator> classGenerator) {
try {
IMixinTransformer transformer = (IMixinTransformer) MixinEnvironment.getDefaultEnvironment().getActiveTransformer();
Extensions extensions = (Extensions) transformer.getExtensions();
ArgsClassGenerator argsClassGenerator = extensions.getGenerator(ArgsClassGenerator.class);
IConsumer<ISyntheticClassInfo> registry = (IConsumer<ISyntheticClassInfo>) ARGS_CLASS_GENERATOR_REGISTRY_FIELD.get(argsClassGenerator);
extensions.add(classGenerator.apply(registry));
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to use mixin internals, please report to LlamaLad7!", e);
}
}

public static Map<String, Object> getDecorations(InjectionNode node) {
try {
return (Map<String, Object>) INJECTION_NODE_DECORATIONS_FIELD.get(node);
Expand Down

0 comments on commit e149b5c

Please sign in to comment.