2010-11-09 6 views
11

Relacionado con: Is there a way to obtain the bytecode for a class at runtime?Java: Obtener código de bytes de la clase en tiempo de ejecución desde el interior de la misma JVM

estoy añadiendo durabilidad a Clojure, y estoy finalmente en el punto en que estoy listo para agregar funciones. En Clojure, las funciones se compilan en bytes en clases con métodos de invocación (entre otros). De esta manera, las funciones son de primera clase. Para que sean duraderos, necesito serializar y deserializar estas clases. ¿Cómo obtengo el bytecode para la clase sin tener acceso al archivo .class?

Corrígeme si me equivoco, pero usar un agente requiere generar una VM separada con el agente que se conecta a la primera VM. Necesito hacerlo desde la misma máquina virtual.

No es suficiente utilizar Serializable para establecer y obtener el objeto Class. Al deserializar, necesito cargar la clase, y en instancias posteriores de VM, puede haber una colisión de nombre de clase. Necesito modificar el bytecode para cambiar el nombre de la clase a algo único en el tiempo de deserialización/carga de clase.

+0

No soy de ninguna manera un experto en el tema, pero podría valer la pena tratar de persistir la función * definiciones * en lugar del bytecode subyacente. a continuación, puede simplemente recompilar las funciones a bytecode cuando las vuelva a cargar. – mikera

Respuesta

4

Puede escribir su propio ClassLoader y piratear un esquema que registra el bytecode cuando se cargan las clases.

Debería sobrescribir findClass para buscar el archivo de clase usted mismo, cargarlo en la memoria, guardar los datos en algún lugar (para una posterior serialización), luego llamar al defineClass para definir esa clase en la JVM.

+0

p.s. Creo que los agentes se ejecutan en la misma máquina virtual que el programa principal, por lo que pueden funcionar para usted. –

+0

Javassist ya implementa este esquema y ofrece un acceso algo conveniente al bytecode en cuestión. BCEL también podría hacer algo similar. – Blaisorblade

+0

La implementación mínima es bastante trivial: https://gist.github.com/Felk/ed4375d27c755e21d0e6893847286d93 (Sé que esta publicación tiene 7 años) – Felk

3

menos que esté ejecutando código a través de un cargador de clases complicado, debe ser capaz de hacer algo como esto:

Class<?> clazz = .... 
String className = clazz.getCanonicalName(); // e.g. "foo.Bar" 
String resourceName = ... // map className to a resource name; e.g. "/foo/Bar.class" 
InputStream is = clazz.getClassLoader.getResourceAsStream(resourceName); 

Esto le da una manija en el contenido del archivo ".class" ... si se puede encontrar

Advertencias. Algunos cargadores de clases podrían:

    no dejar que
  • para abrir los recursos ".class" en absoluto,
  • le dará un flujo de código de bytes cifrados o
  • darle códigos de bytes que son ni exactamente lo que se está ejecutando, debido a alguna transformación sobre la marcha realizada por el cargador de clases.

Si este enfoque no funciona, está prácticamente sin opciones porque la JVM no proporciona una forma de acceder a los códigos de byte reales que se cargaron.

+1

Por desgracia, esto solo funciona si la clase .class se escribe en el disco como un recurso en primer lugar. Clojure no hace esto. – alyssackwan

+0

También tiene problemas cuando la clase se define dentro de otra. Ejemplo: 'clase Foo {clase Bar {}}' debería convertirse en "/Foo$Bar.class" – iliis

+1

'clazz.getResourceAsStream ('/' + clazz.getName(). Replace ('.', '/') + ".class") 'funciona con clases internas e incluso con clases bootstrap que tienen un ClassLoader' nulo '. – Holger

1

También puede usar la API de instrumentación de Java para esto. Obtiene acceso a los bytes del archivo de clase antes de invocar defineClass. ¡Tú también puedes cambiarlos!

Cuestiones relacionadas