猿问

带有 ASM 的 JVM INVOKESPECIAL 私有构造函数

我正在使用 ASM 生成一些字节码并动态执行它。但是有一种情况我需要调用私有构造函数,但我不知道如何。我知道可以通过反射(setAccessible)调用私有构造函数,但是我怎样才能直接在字节码/jvm中做到这一点?


mv.visitMethodInsn(

        INVOKESPECIAL, target.byteCodeName(), "<init>", "()V", false

    )

当这段代码被 JVM 执行时,它会抛出 java.lang.IllegalAccessError。


陪伴而非守候
浏览 198回答 1
1回答

青春有我

反射是调用无关类的私有构造函数的唯一合法方式。但是,每次都进行反射调用当然不是一个好主意。解决办法是invokedynamic。它允许将调用站点绑定到构造函数(通过反射获得)一次,然后在没有开销的情况下调用它。这是一个例子。import org.objectweb.asm.*;import java.lang.invoke.*;import java.lang.reflect.Constructor;import static org.objectweb.asm.Opcodes.*;public class InvokeGenerator extends ClassLoader {&nbsp; &nbsp; private static Class<?> generate() {&nbsp; &nbsp; &nbsp; &nbsp; ClassWriter cv = new ClassWriter(ClassWriter.COMPUTE_FRAMES);&nbsp; &nbsp; &nbsp; &nbsp; cv.visit(V1_7, ACC_PUBLIC, "InvokeImpl", null, "java/lang/Object", null);&nbsp; &nbsp; &nbsp; &nbsp; MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);&nbsp; &nbsp; &nbsp; &nbsp; mv.visitCode();&nbsp; &nbsp; &nbsp; &nbsp; mv.visitVarInsn(ALOAD, 0);&nbsp; &nbsp; &nbsp; &nbsp; mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);&nbsp; &nbsp; &nbsp; &nbsp; // Generate INVOKEDYNAMIC instead of NEW+INVOKESPECIAL.&nbsp; &nbsp; &nbsp; &nbsp; // This will instantiate the target class by calling its private constructor.&nbsp; &nbsp; &nbsp; &nbsp; // Bootstrap method is called just once to link this call site.&nbsp; &nbsp; &nbsp; &nbsp; mv.visitInvokeDynamicInsn("invoke", "()LInvokeGenerator$Target;",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new Handle(H_INVOKESTATIC, "InvokeGenerator", "bootstrap", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false));&nbsp; &nbsp; &nbsp; &nbsp; // Here we have newly constructed instance of InvokeGenerator.Target&nbsp; &nbsp; &nbsp; &nbsp; mv.visitInsn(POP);&nbsp; &nbsp; &nbsp; &nbsp; mv.visitInsn(RETURN);&nbsp; &nbsp; &nbsp; &nbsp; mv.visitMaxs(0, 0);&nbsp; &nbsp; &nbsp; &nbsp; mv.visitEnd();&nbsp; &nbsp; &nbsp; &nbsp; cv.visitEnd();&nbsp; &nbsp; &nbsp; &nbsp; byte[] classData = cv.toByteArray();&nbsp; &nbsp; &nbsp; &nbsp; return new InvokeGenerator().defineClass(null, classData, 0, classData.length);&nbsp; &nbsp; }&nbsp; &nbsp; public static void main(String[] args) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; Class<?> cls = generate();&nbsp; &nbsp; &nbsp; &nbsp; cls.newInstance();&nbsp; &nbsp; }&nbsp; &nbsp; public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; // Derive the constructor signature from the signature of this INVOKEDYNAMIC&nbsp; &nbsp; &nbsp; &nbsp; Constructor c = type.returnType().getDeclaredConstructor(type.parameterArray());&nbsp; &nbsp; &nbsp; &nbsp; c.setAccessible(true);&nbsp; &nbsp; &nbsp; &nbsp; // Convert Constructor to MethodHandle which will serve as a target of INVOKEDYNAMIC&nbsp; &nbsp; &nbsp; &nbsp; MethodHandle mh = lookup.unreflectConstructor(c);&nbsp; &nbsp; &nbsp; &nbsp; return new ConstantCallSite(mh);&nbsp; &nbsp; }&nbsp; &nbsp; public static class Target {&nbsp; &nbsp; &nbsp; &nbsp; private Target() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println("Private constructor called");&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}在 JDK 9 之前,还有另一种肮脏的 hack。如果您从 继承生成的类sun.reflect.MagicAccessorImpl,JVM 将跳过访问检查并允许调用任何私有方法或构造函数。但是 JDK 9 中对私有 API 的封装使得执行此技巧变得困难。此外,MagicAccessorImpl它特定于 HotSpot JVM,不应该适用于其他实现。所以我绝对不会推荐这种选择。
随时随地看视频慕课网APP

相关分类

Java
我要回答