diff --git a/src/main/java/moe/yushi/authlibinjector/transform/CallbackEntryPoint.java b/src/main/java/moe/yushi/authlibinjector/transform/CallbackEntryPoint.java deleted file mode 100644 index 933c5c8..0000000 --- a/src/main/java/moe/yushi/authlibinjector/transform/CallbackEntryPoint.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2019 Haowei Wen and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package moe.yushi.authlibinjector.transform; - -import java.lang.invoke.CallSite; -import java.lang.invoke.ConstantCallSite; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.MethodType; - -public final class CallbackEntryPoint { - private CallbackEntryPoint() { - } - - public static CallSite bootstrap(Lookup lookup, String name, MethodType type, String owner) throws ReflectiveOperationException { - return new ConstantCallSite( - lookup.findStatic( - ClassLoader.getSystemClassLoader().loadClass(owner), - name, - type)); - } -} diff --git a/src/main/java/moe/yushi/authlibinjector/transform/CallbackMetafactoryTransformer.java b/src/main/java/moe/yushi/authlibinjector/transform/CallbackMetafactoryTransformer.java new file mode 100644 index 0000000..3dd2f1f --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/transform/CallbackMetafactoryTransformer.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 Haowei Wen and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package moe.yushi.authlibinjector.transform; + +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ARETURN; +import static org.objectweb.asm.Opcodes.ASM7; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.NEW; +import java.util.Optional; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; + +class CallbackMetafactoryTransformer implements TransformUnit { + + @Override + public Optional transform(ClassLoader classLoader, String className, ClassVisitor writer, TransformContext context) { + return Optional.of(new ClassVisitor(ASM7, writer) { + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + + MethodVisitor mv = super.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, + CallbackSupport.METAFACTORY_NAME, + CallbackSupport.METAFACTORY_SIGNATURE, + null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, "java/lang/invoke/ConstantCallSite"); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + + context.markModified(); + } + }); + } + + @Override + public String toString() { + return "Callback Metafactory Transformer"; + } +} diff --git a/src/main/java/moe/yushi/authlibinjector/transform/CallbackSupport.java b/src/main/java/moe/yushi/authlibinjector/transform/CallbackSupport.java index 81aea3e..96d4f18 100644 --- a/src/main/java/moe/yushi/authlibinjector/transform/CallbackSupport.java +++ b/src/main/java/moe/yushi/authlibinjector/transform/CallbackSupport.java @@ -16,12 +16,8 @@ */ package moe.yushi.authlibinjector.transform; -import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; - import java.lang.reflect.Method; import java.lang.reflect.Modifier; - -import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; @@ -29,6 +25,9 @@ public final class CallbackSupport { private CallbackSupport() { } + static final String METAFACTORY_NAME = "__authlibinjector_metafactory"; + static final String METAFACTORY_SIGNATURE = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;"; + private static Method findCallbackMethod(Class owner, String methodName) { for (Method method : owner.getDeclaredMethods()) { int modifiers = method.getModifiers(); @@ -41,18 +40,11 @@ public final class CallbackSupport { throw new IllegalArgumentException("No such method: " + methodName); } - private static final Handle BOOTSTRAP_METHOD = new Handle( - H_INVOKESTATIC, - Type.getInternalName(CallbackEntryPoint.class), - "bootstrap", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;", - false); - public static void invoke(TransformContext ctx, MethodVisitor mv, Class owner, String methodName) { ctx.requireMinimumClassVersion(50); ctx.upgradeClassVersion(51); String descriptor = Type.getMethodDescriptor(findCallbackMethod(owner, methodName)); - mv.visitInvokeDynamicInsn(methodName, descriptor, BOOTSTRAP_METHOD, owner.getName()); + mv.visitInvokeDynamicInsn(methodName, descriptor, ctx.acquireCallbackMetafactory(), owner.getName()); } } diff --git a/src/main/java/moe/yushi/authlibinjector/transform/ClassTransformer.java b/src/main/java/moe/yushi/authlibinjector/transform/ClassTransformer.java index 91ed0fb..1511e64 100644 --- a/src/main/java/moe/yushi/authlibinjector/transform/ClassTransformer.java +++ b/src/main/java/moe/yushi/authlibinjector/transform/ClassTransformer.java @@ -17,6 +17,7 @@ package moe.yushi.authlibinjector.transform; import static java.util.Collections.emptyList; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; @@ -31,6 +32,7 @@ import java.util.logging.Level; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; import moe.yushi.authlibinjector.AuthlibInjector; import moe.yushi.authlibinjector.util.Logging; @@ -44,9 +46,16 @@ public class ClassTransformer implements ClassFileTransformer { private static class TransformContextImpl implements TransformContext { + private final String className; + public boolean modifiedMark; public int minVersionMark = -1; public int upgradedVersionMark = -1; + public boolean callbackMetafactoryRequested = false; + + public TransformContextImpl(String className) { + this.className = className; + } @Override public void markModified() { @@ -66,6 +75,17 @@ public class ClassTransformer implements ClassFileTransformer { this.upgradedVersionMark = version; } } + + @Override + public Handle acquireCallbackMetafactory() { + this.callbackMetafactoryRequested = true; + return new Handle( + H_INVOKESTATIC, + className.replace('.', '/'), + CallbackSupport.METAFACTORY_NAME, + CallbackSupport.METAFACTORY_SIGNATURE, + false); + } } private static class TransformHandle { @@ -77,6 +97,7 @@ public class ClassTransformer implements ClassFileTransformer { private List appliedTransformers; private int minVersion = -1; private int upgradedVersion = -1; + private boolean addCallbackMetafactory = false; public TransformHandle(ClassLoader classLoader, String className, byte[] classBuffer) { this.className = className; @@ -86,7 +107,7 @@ public class ClassTransformer implements ClassFileTransformer { public void accept(TransformUnit unit) { ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - TransformContextImpl ctx = new TransformContextImpl(); + TransformContextImpl ctx = new TransformContextImpl(className); Optional optionalVisitor = unit.transform(classLoader, className, writer, ctx); if (optionalVisitor.isPresent()) { @@ -105,6 +126,7 @@ public class ClassTransformer implements ClassFileTransformer { if (ctx.upgradedVersionMark > this.upgradedVersion) { this.upgradedVersion = ctx.upgradedVersionMark; } + this.addCallbackMetafactory |= ctx.callbackMetafactoryRequested; } } } @@ -113,6 +135,9 @@ public class ClassTransformer implements ClassFileTransformer { if (appliedTransformers == null || appliedTransformers.isEmpty()) { return Optional.empty(); } else { + if (addCallbackMetafactory) { + accept(new CallbackMetafactoryTransformer()); + } if (minVersion == -1 && upgradedVersion == -1) { return Optional.of(classBuffer); } else { diff --git a/src/main/java/moe/yushi/authlibinjector/transform/TransformContext.java b/src/main/java/moe/yushi/authlibinjector/transform/TransformContext.java index 8be3856..7151558 100644 --- a/src/main/java/moe/yushi/authlibinjector/transform/TransformContext.java +++ b/src/main/java/moe/yushi/authlibinjector/transform/TransformContext.java @@ -16,6 +16,8 @@ */ package moe.yushi.authlibinjector.transform; +import org.objectweb.asm.Handle; + public interface TransformContext { void markModified(); @@ -24,4 +26,5 @@ public interface TransformContext { void upgradeClassVersion(int version); + Handle acquireCallbackMetafactory(); }