我正在为我的自定义编程语言开发 REPL。它在编译器之上实现,用于生成输入的字节码并使用该方法将其转换为Class<?>
实例。sun.misc.Unsafe.defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain)
相关代码如下所示(省略异常处理等无关部分):
void compileAndLoad(List<ICompilable> compilables)
{
List<Class<?>> classes = ...;
for (ICompilable c : compilables)
{
classes.add(compile(compilable));
}
for (Class<?> c : classes)
{
UNSAFE.ensureClassInitialized(c);
}
}
// CLASS_LOADER = Enclosing.class.getClassLoader()
// PROTECTION_DOMAIN = Enclosing.class.getClassLoader()
Class<?> compile(ICompilable compilable)
{
byte[] bytecode = genBytecode(compilable);
String name = compilable.getFullName() // e.g. 'foo.bar.Baz'
return UNSAFE.defineClass(name, bytes, 0, bytes.length, CLASS_LOADER, PROTECTION_DOMAIN);
}
假设输入需要编译和加载多个类。
> class A { interface B { }; func b() = new B { /* anonymous class */ } }
compilables
列表有内容
[ repl.Result_0, repl.Result_0$A, repl.Result_0$A$0, repl.Result_0$A$B ]
该类repl.Result_0$A
依赖于repl.Result_0$A$0
(匿名)类和repl.Result_0$B
类,并在字节码中引用它们的名称。使用 定义时Unsafe
,会出现以下错误:
java.lang.NoClassDefFoundError: repl/Result_0$A$B
at sun.misc.Unsafe.defineClass(Native Method)
at MyClass.compile(MyClass.java:42)
// ... snip
Caused by: java.lang.ClassNotFoundException: repl.Result_0$A$B
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 9 more
我知道这可以通过重新排序列表并repl.Result_0$A$B
首先定义来解决,但这不是一个通用的解决方案,因为也可以引用B -> A
。
有没有一种方法可以定义和加载多个类,Unsafe.defineClass
而不会导致未解析的类出现验证错误?