La constant pool
Las instrucciones de la JVM hacen referencia a la información simbólica que se almacena en la constant_pool. Todas las entradas de esta tabla tienen el siguiente formato general:
Cada elemento en la constant_pool debe empezar con un tag de un byte que indica el typo de entrada. El contenido del array info variará con el valor de tag. Los valores válidos para el tag y sus respectivos valores se muestran aquí. Las únicas estructuras que nos interesan se describen a continuación:cp_info {
u1 tag;
u1 info[];
}
- CONSTANT_Class_info: Una estructura de este tipo se usa para representar a una clase o interfaz. Tiene la siguiente forma:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
- CONSTANT_Fieldref_info, CONSTANT_Methodref_info: Los campos y métodos son representados por estructuras similares:
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info{
u1 tag;
u2 class_index;
u2 name_and_type_index;
En donde tag tiene el valor 9 para un Fieldref y 10 para Methodref. class_index debe ser un índice a una posición en la constant_pool cuyo contenido es una estructura CONSTANT_Class_info que representa a la clase que contiene la declaración del método y campo. Finalmente name_and_type_index debe ser el índice de una localidad en la constant_pool cuyo contenido sea una estructura CONSTANT_NameAndType_info que indica el nombre y el descriptor de un campo o méotod. El único caso especial de una estructura CONSTANT_Methodref_info es cuando esta empieza con un signo menor que (<), en cuyo caso el nombre debe ser el nombre especial <init> que representa el método inicializador de un objeto.}
- CONSTANT_String_info: Esta estructura representa constant objects de tipo String.
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
- CONSTANT_Integer_info: Esta estructura representa una constante numérica de 4 bytes:
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
- CONSTANT_NameAndType_info: Esta estructura representa un campo o un método sin indicar a qué clase o interface pertenece:
CONSTANT_NameAndtype_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
- CONSTANT_Utf8_info: Esta estructura se utiliza para representar cadenas de caracteres constantes. La Sección 4.4.7 de la JVM specification habla sobre los detalles de la codificación de caracteres. La estructura consiste en:
CONSTANT_Utf8_info {
u1 tag;
u2 length
u1 bytes[length];
En esta estructura el valor de tag es 1, length da el numero de bytes en el arreglo bytes, y bytes contiene los bytes de la cadena.}
Cada field se describe por un campo field_info con la siguiente estructura:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}En donde cada elemento se describe como sigue:
- access_flags: Al igual que las flags para las clases, este elemento es una máscara usada para denotar los permisos de acceso y propiedades de este campo. La descripción de las opciones se encuentra en esta tabla. Para nuestros fines nos basta con que todos los fields sean públicos, por lo que todos nuestros access_flags se estableceran como 0x0001.
- name_index: el valor de este campo debe ser el índice de una localidad de la constant_pool cuyo valor sea una estructura CONSTANT_Utf8_info que represente el nombre de un field almacenado como un identificador.
- descriptor_index: el valor de este campo debe ser un índice cuyo destino sea una CONSTANT_Utf8_info que contenga el descriptor de este campo.
- attributes_count y attributes[]: similar al caso de los atributos de la clase, estos atributos no son de interés para el compilador que intentamos desarrollar.
Cada método, incluyendo a los métodos de inicialización, se describen por una estructura method_info como sigue:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 atributes_count;
attribute_info attributes[attributes_count];
}En donde los elementos contiene la siguiente estructura:
- access_flags: similar que para los campos, nos interesaremos únicamente en métodos públicos, por lo que estableceremos el valor de este campo a 0x0001. Las demás opciones se encuentran aquí.
- name_index: el contenido de la dirección apuntada por este campo en la constant_pool debe ser una estructura CONSTANT_Utf8_info que represente el nombre de un método en Java o bien los nombres espciales de métodos <init> o <clinit> (no entraremos en detalle).
- descriptor_index: el valor de este elemento debe ser el índice de una localidad en la constant_pool que contenga una estructura CONSTANT_Utf8_info con el descriptor del método.
- attributes_count y attributes[]: Los valores de estos campos son la cantidad de atributos en el método y la tabla con dichos atributos. En el caso de los métodos estamos especialmente interesados en el atributo code, del que hablaremos a continuación.
Este atributo es de tamaño variable y se utiliza en la tabla de attributos de las estructuras method_info. Contiene las instrucciones de la JVM y la información auxiliar para un solo metodo, ya sea de instancia o de inicialización. La estructura de este atributo es la siguiente:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}Nos interesarán los siguientes elementos:
- attribute_name_index: Debe ser un apuntador a una estructura CONSTANT_Utf8_info que represente la cadena "Code".
- attribute_length: Este campo contiene el largo del attribute, excluyendo los seis bytes iniciales.
- max_stack: este valor indica la profundiad máxima que puede alcanzar el método en cualquier punto durante su ejecución.
- max_locals: este valor indica el número de variables locales que deben reservarse en el arreglo de un frame asociado a éste método, incluyendo la svariables usadas para pasar parámetros durante su invocación.
- code_length: este valor indica el número de bytes en el arreglo code para éste método. Este arreglo no debe estar vacío.
- code[]: este arreglo provee los bytes actuales de código de la JVM que implementan el método. Los detalles acerca de las restricciones para el contenido de este arreglo se describen en la Sección 4.8 Constraints on Java Virtual Machine Code de la JVM specification.
//StructA.java public class StructA{ public int A = 3; public int getA(){ return A; } }Y analizar los distintos elementos que obtenemos al decompilarlo, ya sea utilizando las herramientas de oolong o javap:
//StructA.class.Dump 000000 cafebabe magic = ca fe ba be 000004 0000 minor version = 0 000006 0032 major version = 50 000008 0013 19 constants 00000a 0a0004000f 1. Methodref class #4 name-and-type #15 00000f 0900030010 2. Fieldref class #3 name-and-type #16 000014 070011 3. Class name #17 000017 070012 4. Class name #18 00001a 010001 5. UTF length=1 00001d 41 A 00001e 010001 6. UTF length=1 000021 49 I 000022 010006 7. UTF length=6 000025 3c696e69743e <init> 00002b 010003 8. UTF length=3 00002e 282956 ()V 000031 010004 9. UTF length=4 000034 436f6465 Code 000038 01000f 10. UTF length=15 00003b 4c696e654e756d6265725461626c65 LineNumberTable 00004a 010004 11. UTF length=4 00004d 67657441 getA 000051 010003 12. UTF length=3 000054 282949 ()I 000057 01000a 13. UTF length=10 00005a 536f7572636546696c65 SourceFile 000064 01000c 14. UTF length=12 000067 537472756374412e6a617661 StructA.java 000073 0c00070008 15. NameAndType name #7 descriptor #8 000078 0c00050006 16. NameAndType name #5 descriptor #6 00007d 010007 17. UTF length=7 000080 53747275637441 StructA 000087 010010 18. UTF length=16 00008a 6a6176612f6c616e672f4f626a656374 java/lang/Object 00009a 0021 access_flags = 33 00009c 0003 this = #3 00009e 0004 super = #4 0000a0 0000 0 interfaces 0000a2 0001 1 fields Field 0: 0000a4 0001 access flags = 1 0000a6 0005 name = #5<A> 0000a8 0006 descriptor = #6<I> 0000aa 0000 0 field/method attributes: 0000ac 0002 2 methods Method 0: 0000ae 0001 access flags = 1 0000b0 0007 name = #7<<init>> 0000b2 0008 descriptor = #8<()V> 0000b4 0001 1 field/method attributes: field/method attribute 0 0000b6 0009 name = #9<Code> 0000b8 00000026 length = 38 0000bc 0002 max stack: 2 0000be 0001 max locals: 1 0000c0 0000000a code length: 10 0000c4 2a 0 aload_0 0000c5 b70001 1 invokespecial #1 0000c8 2a 4 aload_0 0000c9 06 5 iconst_3 0000ca b50002 6 putfield #2 0000cd b1 9 return 0000ce 0000 0 exception table entries: 0000d0 0001 1 code attributes: code attribute 0: 0000d2 000a name = #10<LineNumberTable> 0000d4 0000000a length = 10 Line number table: 0000d8 0002 length = 2 0000da 00000001 start pc: 0 line number: 1 0000de 00040002 start pc: 4 line number: 2 Method 1: 0000e2 0001 access flags = 1 0000e4 000b name = #11<getA> 0000e6 000c descriptor = #12<()I> 0000e8 0001 1 field/method attributes: field/method attribute 0 0000ea 0009 name = #9<Code> 0000ec 0000001d length = 29 0000f0 0001 max stack: 1 0000f2 0001 max locals: 1 0000f4 00000005 code length: 5 0000f8 2a 0 aload_0 0000f9 b40002 1 getfield #2 0000fc ac 4 ireturn 0000fd 0000 0 exception table entries: 0000ff 0001 1 code attributes: code attribute 0: 000101 000a name = #10<LineNumberTable> 000103 00000006 length = 6 Line number table: 000107 0001 length = 1 000109 00000004 start pc: 0 line number: 4 00010d 0001 1 classfile attributes Attribute 0: 00010f 000d name = #13<SourceFile> 000111 00000002 length = 2 000115 000e sourcefile index = #14 Done.Por ejemplo, vemos que la declaración de la variable de instancia public int A se tradujo en el campo Fieldref_class de la línea 6, que asocia este campo a la clase descrita en la línea 3 (cuyo nombre está en la posición 17, StructA) y al NameAndType de la posición 16 (que relaciona el nombre A con el descriptor I, el nombre y descripción de este campo).
También podemos notar, aunque todavía no sepamos los detalles sobre el set de instrucciones de la JVM, que la asignación para la variable de instancia A se lleva a cabo en las líneas 60 y 61 que se encuentran dentro del método <init>, a saber el constructor de esta clase.
Y hablando de métodos podemos también notar la diferencia entre los descriptores para el método constructor (()V) y para el método getA() definido por nosotros (()I).
Lo que sigue es entrar en detalle sobre el set de instrucciones de la JVM. Hasta aquí por el momento.
No hay comentarios:
Publicar un comentario