feat: support AbstractTranslet packer

This commit is contained in:
ReaJason
2025-11-20 00:36:25 +08:00
parent bd308fae10
commit 6b1a569be9
11 changed files with 219 additions and 1 deletions
@@ -0,0 +1,65 @@
package com.reajason.javaweb.asm;
import org.objectweb.asm.*;
public class ClassSuperClassUtils {
public static byte[] addSuperClass(byte[] bytes, String superClassName) {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new AddSuperClassAdapter(cw, superClassName.replace('.', '/'));
cr.accept(cv, 0);
return cw.toByteArray();
}
static class AddSuperClassAdapter extends ClassVisitor {
private final String newSuperName;
public AddSuperClassAdapter(ClassVisitor cv, String newSuperName) {
super(Opcodes.ASM9, cv);
this.newSuperName = newSuperName;
}
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
if (!"java/lang/Object".equals(superName)) {
throw new IllegalStateException(String.format(
"Cannot add superclass to class '%s': it already extends '%s'.", name, superName
));
}
super.visit(version, access, name, signature, newSuperName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if ("<init>".equals(name)) {
return new ChangeConstructorAdapter(mv, newSuperName);
}
return mv;
}
}
static class ChangeConstructorAdapter extends MethodVisitor {
private final String newSuperName;
public ChangeConstructorAdapter(MethodVisitor mv, String newSuperName) {
super(Opcodes.ASM9, mv);
this.newSuperName = newSuperName;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String descriptor, boolean isInterface) {
if (opcode == Opcodes.INVOKESPECIAL &&
"java/lang/Object".equals(owner) &&
"<init>".equals(name)) {
super.visitMethodInsn(opcode, newSuperName, name, descriptor, isInterface);
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
}
}
@@ -0,0 +1,37 @@
package com.reajason.javaweb.asm;
import net.bytebuddy.ByteBuddy;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassReader;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @author ReaJason
* @since 2025/11/19
*/
class ClassSuperClassUtilsTest {
@Test
void test() {
String superClassName = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
byte[] bytes = new ByteBuddy().redefine(EmptySuperClass.class).make().getBytes();
byte[] newBytes = ClassSuperClassUtils.addSuperClass(bytes, superClassName);
assertEquals("java/lang/Object", new ClassReader(bytes).getSuperName());
assertEquals(superClassName.replace(".", "/"), new ClassReader(newBytes).getSuperName());
}
@Test
void testException() {
String superClassName = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
byte[] bytes = new ByteBuddy().redefine(SuperClass.class).make().getBytes();
assertThrows(IllegalStateException.class, () -> ClassSuperClassUtils.addSuperClass(bytes, superClassName));
}
class EmptySuperClass {
}
class SuperClass extends EmptySuperClass {
}
}