代码之家  ›  专栏  ›  技术社区  ›  vitaut

如何在运行时发出和执行Java字节码?

  •  31
  • vitaut  · 技术社区  · 15 年前

    我正在用Java为一种具有脚本功能的领域特定语言编写一个解释器。我已经实现了一个解析器,现在需要做一个后端。为此,我考虑编写自己的解释器(使用抽象语法树或一些自定义字节码)或目标JVM(在运行时发出并执行Java字节码)。

    在这方面有更多经验的人能否说,以JVM为目标的方法是多么可行,您建议使用什么库来发送Java字节码?

    4 回复  |  直到 15 年前
        1
  •  31
  •   Martín Schonaker    11 年前

    这是一个工作的“你好世界”与 ObjectWeb ASM

    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[] {});
        }
    }
    

    为了生成代码,我发现非常有用 Bytecode Outline for Eclipse 插件。尽管您可以像这样使用asmier(包括在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");
        }
    }
    

    玩得高兴。

        2
  •  15
  •   Bozho    15 年前

    我建议你看看这些图书馆:

        3
  •  9
  •   Peter Knego    15 年前

    退房 Jetbrains MPS . 由给我们带来灵感的人建造的。

        4
  •  2
  •   Gábor Lipták    15 年前

    从另一个角度我问你是否考虑使用 XText documentation . 值得一看。您可以轻松地为DSL创建一个基于它的编译器。