From 978dee0ffd712e1f49df2900e17d4b6c8f01987a Mon Sep 17 00:00:00 2001 From: Wagyourtail Date: Sun, 24 Nov 2024 19:21:54 -0600 Subject: [PATCH] fix java7downgrader --- .../jvmdg/providers/Java7Downgrader.java | 128 ++++++++++++++---- .../jvmdg/compile/shade/ReferenceGraph.java | 12 ++ .../map/FullyQualifiedMemberNameAndDesc.java | 14 ++ .../jvmdg/version/ReflectionReferences.java | 14 ++ .../test/integration/DowngradeTests.java | 5 + 5 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 src/shared/java/xyz/wagyourtail/jvmdg/version/ReflectionReferences.java diff --git a/java-api/src/main/java/xyz/wagyourtail/jvmdg/providers/Java7Downgrader.java b/java-api/src/main/java/xyz/wagyourtail/jvmdg/providers/Java7Downgrader.java index c3d7b80..285de57 100644 --- a/java-api/src/main/java/xyz/wagyourtail/jvmdg/providers/Java7Downgrader.java +++ b/java-api/src/main/java/xyz/wagyourtail/jvmdg/providers/Java7Downgrader.java @@ -1,23 +1,25 @@ package xyz.wagyourtail.jvmdg.providers; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import xyz.wagyourtail.jvmdg.ClassDowngrader; import xyz.wagyourtail.jvmdg.asm.ASMUtils; +import xyz.wagyourtail.jvmdg.asm.AnnotationUtils; import xyz.wagyourtail.jvmdg.j7.stub.J_L_Throwable; import xyz.wagyourtail.jvmdg.util.Function; import xyz.wagyourtail.jvmdg.util.IOFunction; import xyz.wagyourtail.jvmdg.util.Pair; +import xyz.wagyourtail.jvmdg.version.Ref; +import xyz.wagyourtail.jvmdg.version.ReflectionReferences; import xyz.wagyourtail.jvmdg.version.VersionProvider; +import xyz.wagyourtail.jvmdg.version.map.FullyQualifiedMemberNameAndDesc; import xyz.wagyourtail.jvmdg.version.map.MemberNameAndDesc; import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; public class Java7Downgrader extends VersionProvider { @@ -49,7 +51,8 @@ public ClassNode otherTransforms(ClassNode clazz, Set extra, Function Type methodType = stubClass(Type.getObjectType("java/lang/invoke/MethodType"), warnings); MethodNode clinit = null; - for (MethodNode method : clazz.methods) { + List reflectionRefList = new ArrayList<>(); + for (MethodNode method : new ArrayList<>(clazz.methods)) { if (method.name.equals("")) { clinit = method; } @@ -58,19 +61,22 @@ public ClassNode otherTransforms(ClassNode clazz, Set extra, Function AbstractInsnNode insn = method.instructions.get(i); if (insn.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN) { InvokeDynamicInsnNode indy = (InvokeDynamicInsnNode) insn; - String name = indyToMethodHandle(method, indy, clazz, addToClinit, callSiteType, handleType, lookupType, methodType); + String name = indyToMethod(method, indy, clazz, addToClinit, callSiteType, handleType, lookupType, methodType, reflectionRefList); InsnList insns = new InsnList(); - insns.add(new FieldInsnNode(Opcodes.GETSTATIC, clazz.name, name, handleType.getDescriptor())); - // TODO: fix if stubbing MethodHandle properly - insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, handlesType.getInternalName(), "invokeExact", indy.desc, false)); - method.instructions.insertBefore(indy, insns); - method.instructions.remove(indy); + + method.instructions.set(indy, new MethodInsnNode( + Opcodes.INVOKESTATIC, + clazz.name, + name, + indy.desc, + false + )); } if (insn.getType() == AbstractInsnNode.LDC_INSN) { assert insn instanceof LdcInsnNode; Object cst = ((LdcInsnNode) insn).cst; if (cst instanceof Handle) { - String name = handleToLookupField((Handle) cst, clazz, addToClinit, handleType, lookupType, methodType); + String name = handleToLookupField((Handle) cst, clazz, addToClinit, handleType, lookupType, methodType, reflectionRefList); method.instructions.set(insn, new FieldInsnNode(Opcodes.GETSTATIC, clazz.name, name, handleType.getDescriptor())); } else if (cst instanceof Type && ((Type) cst).getSort() == Type.METHOD) { String name = methodDescToLookupField((Type) cst, clazz, addToClinit, methodType); @@ -89,6 +95,32 @@ public ClassNode otherTransforms(ClassNode clazz, Set extra, Function clinit.visitEnd(); clazz.methods.add(clinit); } + if (clinit.visibleAnnotations == null) { + clinit.visibleAnnotations = new ArrayList<>(); + } + String reflectionRefs = Type.getType(ReflectionReferences.class).getDescriptor(); + String refType = Type.getType(Ref.class).getDescriptor(); + AnnotationNode node = null; + for (AnnotationNode a : clinit.visibleAnnotations) { + if (a.desc.equals(reflectionRefs)) { + node = a; + break; + } + } + if (node == null) { + node = new AnnotationNode(reflectionRefs); + clinit.visibleAnnotations.add(node); + node.values = new ArrayList(Arrays.asList("value", new ArrayList())); + } + List refs = ((List) node.values.get(1)); + for (FullyQualifiedMemberNameAndDesc ref : reflectionRefList) { + AnnotationNode refAnn = new AnnotationNode(refType); + refAnn.visit("value", ref.getOwner().getInternalName()); + refAnn.visit("member", ref.getName()); + refAnn.visit("desc", ref.getDesc().toString()); + refAnn.visitEnd(); + refs.add(refAnn); + } Handle lookup = stubHandle(clazz, clinit, extra, null, null, enableRuntime, memberResolver, superTypeResolver, warnings, new Handle( Opcodes.H_INVOKESTATIC, @@ -109,17 +141,19 @@ public ClassNode otherTransforms(ClassNode clazz, Set extra, Function return super.otherTransforms(clazz); } - public String indyToMethodHandle(MethodNode method, InvokeDynamicInsnNode indy, ClassNode clazz, InsnList addToClinit, Type callsiteType, Type handleType, Type lookupType, Type methodType) { + public String indyToMethod(MethodNode method, InvokeDynamicInsnNode indy, ClassNode clazz, InsnList addToClinit, Type callsiteType, Type handleType, Type lookupType, Type methodType, List reflectionRefList) { InvokeDynamicType it = new InvokeDynamicType(indy); for (FieldNode field : clazz.fields) { if (field instanceof IndyField && ((IndyField) field).indy.equals(it)) { return field.name; } } - IndyField indyField = new IndyField(it, callsiteType); + int count = clazz.fields.size(); + IndyField indyField = new IndyField(it, callsiteType, count); clazz.fields.add(indyField); addToClinit.add(new FieldInsnNode(Opcodes.GETSTATIC, clazz.name, "jvmdg$lookup", lookupType.getDescriptor())); + addToClinit.add(new LdcInsnNode(indy.name)); addToClinit.add(methodDescToMethodType(Type.getMethodType(indy.desc), methodType)); for (Object arg : indy.bsmArgs) { @@ -130,17 +164,18 @@ public String indyToMethodHandle(MethodNode method, InvokeDynamicInsnNode indy, addToClinit.add(new LdcInsnNode(arg)); } } else if (arg instanceof Handle) { - addToClinit.add(handleToLookupStack((Handle) arg, clazz, handleType, lookupType, methodType)); + addToClinit.add(handleToLookupStack((Handle) arg, clazz, handleType, lookupType, methodType, reflectionRefList)); } else { addToClinit.add(new LdcInsnNode(arg)); } } addToClinit.add(new MethodInsnNode( - Opcodes.INVOKEVIRTUAL, - callsiteType.getInternalName(), - "getTarget", - Type.getMethodDescriptor(handleType) + Opcodes.INVOKESTATIC, + indy.bsm.getOwner(), + indy.bsm.getName(), + indy.bsm.getDesc(), + indy.bsm.isInterface() )); addToClinit.add(new FieldInsnNode( @@ -150,23 +185,49 @@ public String indyToMethodHandle(MethodNode method, InvokeDynamicInsnNode indy, indyField.desc )); - return indyField.name; + IndyMethod indyMethod = new IndyMethod(it, count); + clazz.methods.add(indyMethod); + indyMethod.visitCode(); + indyMethod.visitFieldInsn(Opcodes.GETSTATIC, clazz.name, indyField.name, callsiteType.getDescriptor()); + indyMethod.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + callsiteType.getInternalName(), + "getTarget", + Type.getMethodDescriptor(handleType), + false + ); + + int i = 0; + for (Type arg : Type.getArgumentTypes(indy.desc)) { + indyMethod.visitVarInsn(arg.getOpcode(Opcodes.ILOAD), i); + i += arg.getSize(); + } + + // TODO: fix if stubbing MethodHandle properly + indyMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, handleType.getInternalName(), "invokeExact", indy.desc, false); + Type returnType = Type.getReturnType(indy.desc); + indyMethod.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); + indyMethod.visitMaxs(0, 0); + indyMethod.visitEnd(); + + return indyMethod.name; } - public String handleToLookupField(Handle handle, ClassNode clazz, InsnList addToClinit, Type handleType, Type lookupType, Type methodType) { + public String handleToLookupField(Handle handle, ClassNode clazz, InsnList addToClinit, Type handleType, Type lookupType, Type methodType, List reflectionRefList) { for (FieldNode field : clazz.fields) { if (field instanceof HandleField && handle.equals(((HandleField) field).handle)) { return field.name; } } - HandleField field = new HandleField(handle, handleType); + HandleField field = new HandleField(handle, handleType, clazz.fields.size()); clazz.fields.add(field); - addToClinit.add(handleToLookupStack(handle, clazz, handleType, lookupType, methodType)); + addToClinit.add(handleToLookupStack(handle, clazz, handleType, lookupType, methodType, reflectionRefList)); addToClinit.add(new FieldInsnNode(Opcodes.PUTSTATIC, clazz.name, field.name, field.desc)); return field.name; } - public InsnList handleToLookupStack(Handle handle, ClassNode clazz, Type handleType, Type lookupType, Type methodType) { + public InsnList handleToLookupStack(Handle handle, ClassNode clazz, Type handleType, Type lookupType, Type methodType, List reflectionRefList) { + reflectionRefList.add(FullyQualifiedMemberNameAndDesc.of(handle)); InsnList insns = new InsnList(); insns.add(new FieldInsnNode(Opcodes.GETSTATIC, clazz.name, "jvmdg$lookup", lookupType.getDescriptor())); insns.add(new LdcInsnNode(Type.getObjectType(handle.getOwner()))); @@ -298,6 +359,17 @@ public static InsnList methodDescToMethodType(Type desc, Type methodType) { return l; } + public static class IndyMethod extends MethodNode { + private final InvokeDynamicType indy; + + + public IndyMethod(InvokeDynamicType indy, int count) { + super(Opcodes.ASM9, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, "jvmdg$indy$" + indy.indy.name + "$" + count, indy.indy.desc, null, null); + this.indy = indy; + } + + } + public static class MethodTypeField extends FieldNode { private final Type type; @@ -310,8 +382,8 @@ public MethodTypeField(Type type, String name, Type methodType) { public static class HandleField extends FieldNode { private final Handle handle; - public HandleField(Handle handle, Type handleType) { - super(Opcodes.ASM9, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "jvmdg$handle$" + handle.getOwner().replace("/", "_") + "$" + handle.getName(), handleType.getDescriptor(), null, null); + public HandleField(Handle handle, Type handleType, int count) { + super(Opcodes.ASM9, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "jvmdg$handle$" + handle.getName() + "$" + count, handleType.getDescriptor(), null, null); this.handle = handle; } @@ -320,8 +392,8 @@ public HandleField(Handle handle, Type handleType) { public static class IndyField extends FieldNode { private final InvokeDynamicType indy; - public IndyField(InvokeDynamicType indy, Type callsiteType) { - super(Opcodes.ASM9, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, indy.indy.name, callsiteType.getDescriptor(), null, null); + public IndyField(InvokeDynamicType indy, Type callsiteType, int count) { + super(Opcodes.ASM9, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "jvmdg$indy$" + indy.indy.name + "$" + count, callsiteType.getDescriptor(), null, null); this.indy = indy; } diff --git a/src/main/java/xyz/wagyourtail/jvmdg/compile/shade/ReferenceGraph.java b/src/main/java/xyz/wagyourtail/jvmdg/compile/shade/ReferenceGraph.java index 9aa1d3f..48a1f9e 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/compile/shade/ReferenceGraph.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/compile/shade/ReferenceGraph.java @@ -12,6 +12,8 @@ import xyz.wagyourtail.jvmdg.util.IOConsumer; import xyz.wagyourtail.jvmdg.util.Pair; import xyz.wagyourtail.jvmdg.util.Utils; +import xyz.wagyourtail.jvmdg.version.Ref; +import xyz.wagyourtail.jvmdg.version.ReflectionReferences; import xyz.wagyourtail.jvmdg.version.RequiresResource; import xyz.wagyourtail.jvmdg.version.map.FullyQualifiedMemberNameAndDesc; import xyz.wagyourtail.jvmdg.version.map.MemberNameAndDesc; @@ -484,6 +486,16 @@ public void scan(ClassNode classNode, Filter filter) { throw new RuntimeException(e); } } + if (annotation.desc.equals(Type.getDescriptor(ReflectionReferences.class))) { + try { + ReflectionReferences refs = AnnotationUtils.createAnnotation(annotation); + for (Ref ref : refs.value()) { + requiresMember(methodMember, FullyQualifiedMemberNameAndDesc.of(ref) , filter, null); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } } } diff --git a/src/main/java/xyz/wagyourtail/jvmdg/version/map/FullyQualifiedMemberNameAndDesc.java b/src/main/java/xyz/wagyourtail/jvmdg/version/map/FullyQualifiedMemberNameAndDesc.java index 6ddaaf0..8a57811 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/version/map/FullyQualifiedMemberNameAndDesc.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/version/map/FullyQualifiedMemberNameAndDesc.java @@ -4,6 +4,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.MethodInsnNode; +import xyz.wagyourtail.jvmdg.version.Ref; import java.lang.reflect.Method; import java.util.Objects; @@ -19,6 +20,19 @@ public FullyQualifiedMemberNameAndDesc(Type owner, String name, Type desc) { this.desc = desc; } + public static FullyQualifiedMemberNameAndDesc of(Ref ref) { + String owner; + if (ref.value().startsWith("L") && ref.value().endsWith(";")) { + owner = ref.value().substring(1, ref.value().length() - 1); + } else { + owner = ref.value(); + } + if (ref.member().isEmpty()) { + return new FullyQualifiedMemberNameAndDesc(Type.getObjectType(owner), null, null); + } + return new FullyQualifiedMemberNameAndDesc(Type.getObjectType(owner), ref.member(), Type.getType(ref.desc())); + } + public static FullyQualifiedMemberNameAndDesc of(Method method) { return new FullyQualifiedMemberNameAndDesc(Type.getType(method.getDeclaringClass()), method.getName(), Type.getType(method)); } diff --git a/src/shared/java/xyz/wagyourtail/jvmdg/version/ReflectionReferences.java b/src/shared/java/xyz/wagyourtail/jvmdg/version/ReflectionReferences.java new file mode 100644 index 0000000..2eb5501 --- /dev/null +++ b/src/shared/java/xyz/wagyourtail/jvmdg/version/ReflectionReferences.java @@ -0,0 +1,14 @@ +package xyz.wagyourtail.jvmdg.version; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ReflectionReferences { + + Ref[] value(); + +} diff --git a/testing/src/test/java/xyz/wagyourtail/jvmdg/test/integration/DowngradeTests.java b/testing/src/test/java/xyz/wagyourtail/jvmdg/test/integration/DowngradeTests.java index 0a2f698..8ea4e77 100644 --- a/testing/src/test/java/xyz/wagyourtail/jvmdg/test/integration/DowngradeTests.java +++ b/testing/src/test/java/xyz/wagyourtail/jvmdg/test/integration/DowngradeTests.java @@ -50,6 +50,11 @@ private static Stream flags() { flags.logLevel = Logger.Level.FATAL; return Stream.of( +// new FlagsAndRunner(JavaRunner.JavaVersion.V1_8, flags.copy(e -> { +// e.classVersion = JavaRunner.JavaVersion.V1_5.toOpcode(); +// e.shadeInlining = true; +// e.debugSkipStubs = Set.of(JavaRunner.JavaVersion.V1_8.toOpcode()); +// })), new FlagsAndRunner(JavaRunner.JavaVersion.V1_8, flags.copy(e -> { e.classVersion = JavaRunner.JavaVersion.V1_8.toOpcode(); e.shadeInlining = true;