Oto robocza "hello world" wykonane z ObjectWeb ASM (biblioteki, które polecam):
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[] {});
}
}
Aby wygenerować kod, znalazłem bardzo przydatny Bytecode Outline for Eclipse plug-in. Chociaż można użyć ASMifier (w zestawie z ASM) tak:
ClassReader cr = new ClassReader(new FileInputStream("HelloWorld.class"));
cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
w czasie wykonywania, jeśli chcesz uzyskać Class
obiekt dla tworzonej klasy, można załadować swoją klasę przez rozszerzenie programu ładującego klasy i publikowania (poprzez inną metodę, na przykład), metodę defineClass
i zapewnienie klasy jako tablicy bajtów, jak podano w przykładzie.
Można również obsługiwać utworzoną klasę z interfejsem, jak w poniższym przykładzie:
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");
}
}
bawić.
PS: Mogę dodać komentarze do kodu, jeśli nie są wystarczająco jasne. Nie zrobiłem tego, ponieważ odpowiedź jest już zbyt długa. Niemniej jednak moją propozycją jest próba debugowania.
Czy DSL zostało już zaprojektowane/wyspecyfikowane? Czy rozwijasz DSL równolegle z parserem i tłumaczem? – Stobor
@Stobor: określono język i zapisano parser. – vitaut