lunes, 15 de noviembre de 2010

La constant pool, campos, métodos y el atributo code dentro del .class

Continuando con lo que tratábamos anteriormente, hablaremos ahora sobre los cuatro elementos mencionados en el título.

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:
    cp_info {
     u1 tag;
     u1 info[];
    }
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:
  • 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;
        }
    En donde tag tiene el valor 7 y name_index debe ser el índice de una localidad en la constant_pool cuyo contenido sea una estructura CONSTANT_Utf8_info que represente un nombre completo de una clase o interfaz.
  • 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;
        }
    En donde tag tiene el valor 8 y string_index debe apuntar a una localidad de la constant_pool cuyo contenido sesa una estructura CONSTANT_Utf8_info que represente una secuencia de caracteres con las que se inicializará el objeto String.}
  • CONSTANT_Integer_info: Esta estructura representa una constante numérica de 4 bytes:
        CONSTANT_Integer_info {
          u1 tag; 
          u4 bytes;
        }
    En donde tag tiene el valor 3 y bytes representa el valor de la constante.
  • 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;
        }
    Aquí tag tiene el valor 12 y name_index debe ser el índice válido de una localidad en la constant_pool cuyo contenido sea una estructura CONSTANT_Utf8_info que represente un nombre completo de una clase o interfaz. Por otro lado descriptor_index debe ser el índice válido de una localidad en la constant_pool cuyo contenido sea una estructura CONSTANT_Utf8_info que represente un descriptor.
  • 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.
Campos
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.
Métodos
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.
El atributo code
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.
Con los elementos anteriormente descritos podemos ya tomar un programa un poco más complejo en Java tal como:
//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