tabla quién que puede programa plataformas maquina intérprete interprete extension estructura escrito ejemplos ejecutar diferentes cómo codigo java jvm code-generation bytecode compiler-construction

quién - ¿Cómo emitir y ejecutar bytecode Java en tiempo de ejecución?



¿cómo es que se puede ejecutar un programa escrito en java en diferentes plataformas? (4)

Estoy escribiendo un intérprete en Java para un lenguaje específico de dominio con algunas capacidades de scripting. Ya he implementado un analizador y ahora necesito hacer un backend. Para este fin, estoy considerando escribir mi propio intérprete (ya sea trabajando con árboles sintácticos abstractos o con algunos códigos de bytes personalizados) o JVM objetivo (emitir y ejecutar código byte Java en tiempo de ejecución).

¿Podría alguien con más experiencia en esta área decir qué tan factible es el enfoque de la JVM de orientación y qué bibliotecas recomendaría utilizar para emitir bytecode de Java?


Aquí hay un "hola mundo" de trabajo hecho con ObjectWeb ASM (una biblioteca que recomiendo):

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

Para generar el código, encontré Bytecode Outline para Eclipse plug-in muy útil. Aunque podría usar el ASMifier (incluido con ASM) de esta manera:

ClassReader cr = new ClassReader(new FileInputStream("HelloWorld.class")); cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);

En tiempo de ejecución, si necesita obtener el objeto Class para la clase creada, puede cargar su clase extendiendo un cargador de clases y publicando (a través de otro método, por ejemplo) el método defineClass y proporcionando la clase como una matriz de bytes, como se muestra en el ejemplo.

También puede manejar la clase creada con una interfaz, como en este ejemplo:

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"); } }

Que te diviertas.

PD: puedo agregar comentarios al código si no lo suficientemente claro. No lo hice porque la respuesta ya es demasiado larga. Sin embargo, mi sugerencia para usted es intentar depurarla.


Desde una perspectiva diferente, le pregunto si considera usar XText . Esto está diseñado para permitirle crear DSL, editor de código con finalización de código, compilador, generador de código, etc. Creo que es realmente genial y tengo una buena documentation . Vale la pena echarle un vistazo. Puede crear un compilador basado fácilmente en él para su DSL.


Echa un vistazo a Jetbrains MPS . Construido por chicos que nos trajeron IDEA.