feat: support GroovyTransformJar packer

This commit is contained in:
ReaJason
2025-12-08 01:43:41 +08:00
parent c5cdb03a9f
commit d4e7a25255
10 changed files with 259 additions and 27 deletions
@@ -0,0 +1,99 @@
package com.reajason.javaweb.asm;
import org.objectweb.asm.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author ReaJason
* @since 2025/12/6
*/
public class ClassAnnotationUtils {
public static byte[] setAnnotation(byte[] bytes, String annotationClassName) {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new AddAnnotationClassVisitor(cw, annotationClassName);
cr.accept(cv, 0);
return cw.toByteArray();
}
static class AddAnnotationClassVisitor extends ClassVisitor {
private final String annotationClassName;
public AddAnnotationClassVisitor(ClassVisitor cv, String annotationClassName) {
super(Opcodes.ASM9, cv);
this.annotationClassName = annotationClassName.replace('.', '/');
}
@Override
public void visit(
int version, int access, String name,
String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
super.visitAnnotation(
"L" + annotationClassName + ";",
true
).visitEnd();
}
}
public static List<AnnotationInfo> getAnnotations(byte[] classBytes) {
ClassReader cr = new ClassReader(classBytes);
AnnotationCollectingVisitor cv = new AnnotationCollectingVisitor();
cr.accept(cv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return cv.getAnnotations();
}
public static class AnnotationInfo {
public final String desc;
public final Map<String, Object> values = new HashMap<>();
public AnnotationInfo(String desc) {
this.desc = desc;
}
}
public static class AnnotationCollectingVisitor extends ClassVisitor {
private final List<AnnotationInfo> annotations = new ArrayList<>();
public AnnotationCollectingVisitor() {
super(Opcodes.ASM9);
}
public List<AnnotationInfo> getAnnotations() {
return annotations;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
AnnotationInfo info = new AnnotationInfo(descriptor);
annotations.add(info);
return new AnnotationVisitor(Opcodes.ASM9) {
@Override
public void visit(String name, Object value) {
info.values.put(name, value);
}
@Override
public AnnotationVisitor visitArray(String name) {
List<Object> array = new ArrayList<>();
info.values.put(name, array);
return new AnnotationVisitor(Opcodes.ASM9) {
@Override
public void visit(String name, Object value) {
array.add(value);
}
};
}
};
}
}
}
@@ -0,0 +1,28 @@
package com.reajason.javaweb.asm;
import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2025/12/6
*/
class ClassAnnotationUtilsTest {
@Test
@SneakyThrows
void test() {
String interfaceName = "javax.script.ScriptEngineFactory";
byte[] bytes = new ByteBuddy().redefine(ClassInterfaceUtilsTest.EmptyInterface.class).make().getBytes();
List<ClassAnnotationUtils.AnnotationInfo> rawAnnotations = ClassAnnotationUtils.getAnnotations(bytes);
byte[] newBytes = ClassAnnotationUtils.setAnnotation(bytes, interfaceName);
List<ClassAnnotationUtils.AnnotationInfo> annotations = ClassAnnotationUtils.getAnnotations(newBytes);
assertEquals(0, rawAnnotations.size());
assertEquals(1, annotations.size());
assertEquals("Ljavax/script/ScriptEngineFactory;", annotations.get(0).desc);
}
}