我试图在运行时编译和加载动态生成的Java代码.由于ClassLoader::defineClass和Unsafe::DefineAnyMousClass在这种情况下都有严重的缺点,所以我try 使用hidden classes via Lookup::defineHiddenClass.这适用于我try 加载的所有类,除了那些调用lambda表达式或包含匿名类的类.
调用lambda表达式会引发以下异常:
Exception in thread "main" java.lang.NoClassDefFoundError: tests/HiddenClassLambdaTest$LambdaRunner/0x0000000800c04400
at tests.HiddenClassLambdaTest.main(HiddenClassLambdaTest.java:22)
Caused by: java.lang.ClassNotFoundException: tests.HiddenClassLambdaTest$LambdaRunner.0x0000000800c04400
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:636)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:182)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:519)
... 1 more
执行实例化匿名类的代码会引发以下错误:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
tests/HiddenClassLambdaTest$LambdaRunner+0x0000000800c00400.run()V @5: invokespecial
Reason:
Type 'tests/HiddenClassLambdaTest$LambdaRunner+0x0000000800c00400' (current frame, stack[2]) is not assignable to 'tests/HiddenClassLambdaTest$LambdaRunner'
Current Frame:
bci: @5
flags: { }
locals: { 'tests/HiddenClassLambdaTest$LambdaRunner+0x0000000800c00400' }
stack: { uninitialized 0, uninitialized 0, 'tests/HiddenClassLambdaTest$LambdaRunner+0x0000000800c00400' }
Bytecode:
0000000: bb00 1159 2ab7 0013 4cb1
at java.base/java.lang.ClassLoader.defineClass0(Native Method)
at java.base/java.lang.System$2.defineClass(System.java:2193)
at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2446)
at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClassAsLookup(MethodHandles.java:2427)
at java.base/java.lang.invoke.MethodHandles$Lookup.defineHiddenClass(MethodHandles.java:2133)
at tests.HiddenClassLambdaTest.main(HiddenClassLambdaTest.java:25)
这是一个重现问题的简短示例:
import java.lang.invoke.MethodHandles;
public class HiddenClassLambdaTest {
/** This class is to be loaded and executed as hidden class */
public static final class LambdaRunner implements Runnable {
@Override public void run() {
Runnable runnable = () -> System.out.println("Success");
runnable.run();
}
}
public static void main(String[] args) throws Throwable {
// Path to the class file of the nested class defined above
String nestedClassPath = HiddenClassLambdaTest.class.getTypeName().replace('.','/') + "$LambdaRunner.class";
// Class file content of the LambdaRunner class
byte[] classFileContents = HiddenClassLambdaTest.class.getClassLoader().getResourceAsStream(nestedClassPath).readAllBytes();
Class<?> lambdaRunnerClass = MethodHandles.lookup().defineHiddenClass(classFileContents, true).lookupClass();
Runnable lambdaRunnerInstance = (Runnable) lambdaRunnerClass.getConstructor().newInstance();
lambdaRunnerInstance.run();
}
}
我已经try 过用不同的JDK编译和运行代码,使用不同的方法创建隐藏类的新实例,搜索https://bugs.openjdk.java.net/个错误,搞乱字节码本身和其他一些事情.我不是Java内部构件方面的专家,所以我不确定自己是否理解了正确引入隐藏类的JEP.
我是不是做错了什么,这是不可能的,还是这是个错误?
编辑:JEP个州
迁移应考虑以下因素: 若要从隐藏类中的代码调用私有NestMate实例方法,请使用InvokeVirtual或InvokeInterface而不是invokSpecial.使用invokSpecial调用私有NestMate实例方法的生成的字节码将无法通过验证.invokSpecial只能用于调用私有的嵌套构造函数.
这可能是匿名类的问题.有没有一种编译代码的方法可以避免字节码中的invokespecial?