いくつかのスクリプト機能を備えたドメイン固有言語用にJavaでインタープリターを作成しています。すでにパーサーを実装しており、バックエンドを実行する必要があります。この目的のために、次のいずれかを検討しています。独自のインタープリター(抽象構文ツリーまたはいくつかのカスタムバイトコードを使用)またはターゲットJVM(実行時にJavaバイトコード)を発行して実行する)を作成します。
この分野の経験が豊富な人は、JVMをターゲットにするアプローチがどれほど実行可能であり、Javaバイトコード)を発行するためにどのライブラリを使用することをお勧めしますか?
これは、 ObjectWeb ASM (私が推奨するライブラリ)で作成された動作中の「helloworld」です。
package hello;
import Java.lang.reflect.Method;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class HelloWorldASM implements Opcodes {
public static byte[] compile(String name) {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "hello/HelloWorld", null,
"Java/lang/Object", null);
cw.visitSource("HelloWorld.Java", null);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(4, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "Java/lang/Object", "<init>",
"()V");
mv.visitInsn(RETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "Lhello/HelloWorld;", null, l0, l1,
0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(7, l0);
mv.visitFieldInsn(GETSTATIC, "Java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn(String.format("Hello, %s!", name));
mv.visitMethodInsn(INVOKEVIRTUAL, "Java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(8, l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLocalVariable("args", "[Ljava/lang/String;", null, l0, l2,
0);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
public static class DynamicClassLoader extends ClassLoader {
public Class<?> define(String className, byte[] bytecode) {
return super.defineClass(className, bytecode, 0, bytecode.length);
}
};
public static void main(String[] args) throws Exception {
DynamicClassLoader loader = new DynamicClassLoader();
Class<?> helloWorldClass = loader.define("hello.HelloWorld",
compile("Test"));
Method method = helloWorldClass.getMethod("main", String[].class);
method.invoke(null, (Object) new String[] {});
}
}
コードを生成するために、私は非常に便利であることがわかりました Eclipseのバイトコードアウトライン プラグイン。次のようにASMifier(ASMに含まれている)を使用できますが、次のようになります。
ClassReader cr = new ClassReader(new FileInputStream("HelloWorld.class"));
cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
実行時に、作成されたクラスのClass
オブジェクトを取得する必要がある場合は、クラスローダーを拡張し、(たとえば、別のメソッドを介して)defineClass
メソッドを公開することでクラスをロードできます。例にリストされているように、クラスをバイト配列として提供します。
次の例のように、作成されたクラスをインターフェイスで処理することもできます。
package hello;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class HelloWorldPlugin implements Opcodes {
public static interface Plugin {
void sayHello(String name);
}
public static byte[] compile() {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "hello/MyClass", null,
"Java/lang/Object",
new String[] { "hello/HelloWorldPlugin$Plugin" });
cw.visitInnerClass("hello/HelloWorldPlugin$Plugin",
"hello/HelloWorldPlugin", "Plugin", ACC_PUBLIC + ACC_STATIC
+ ACC_ABSTRACT + ACC_INTERFACE);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(5, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "Java/lang/Object", "<init>",
"()V");
mv.visitInsn(RETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "Lhello/MyClass;", null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "sayHello",
"(Ljava/lang/String;)V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(9, l0);
mv.visitFieldInsn(GETSTATIC, "Java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitTypeInsn(NEW, "Java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitLdcInsn("Hello, ");
mv.visitMethodInsn(INVOKESPECIAL, "Java/lang/StringBuilder",
"<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "Java/lang/StringBuilder",
"append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(INVOKEVIRTUAL, "Java/lang/StringBuilder",
"toString", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKEVIRTUAL, "Java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(10, l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLocalVariable("this", "Lhello/MyClass;", null, l0, l2, 0);
mv.visitLocalVariable("name", "Ljava/lang/String;", null, l0, l2,
1);
mv.visitMaxs(4, 2);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
public static class DynamicClassLoader extends ClassLoader {
public DynamicClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> define(String className, byte[] bytecode) {
return super.defineClass(className, bytecode, 0, bytecode.length);
}
};
public static void main(String[] args) throws Exception {
DynamicClassLoader loader = new DynamicClassLoader(Thread
.currentThread().getContextClassLoader());
Class<?> helloWorldClass = loader.define("hello.MyClass", compile());
Plugin plugin = (Plugin) helloWorldClass.newInstance();
plugin.sayHello("Test");
}
}
楽しんで。
PS:十分に明確でない場合は、コードにコメントを追加できます。答えがすでに長すぎるので、私はしませんでした。それにもかかわらず、あなたへの私の提案はそれをデバッグしてみることです。
これらのライブラリをご覧になることをお勧めします。
チェックアウト Jetbrains MPS 。私たちにIDEAをもたらした人たちによって建てられました。