Java Bytecode Software Bsico Mitsuo Takaki Java Bytecode
Java Bytecode Software Básico Mitsuo Takaki
Java Bytecode 1. O que é Bytecode 2. Formato de um arquivo. class 3. Principais comandos 4. Motivações para aprender bytecode 5. Principais Dificuldades
Java Bytecode 1. O que é Bytecode 2. Formato de um arquivo. class 3. Principais comandos 4. Motivações para aprender bytecode 5. Principais Dificuldades
Passos de Compilação de Java A compilação de um código java segue os seguintes passos: Recebe um código Java (alto nível). Compila para uma linguagem intermediária (baixo nível). Este procedimento é conhecido como tradução. Após esta compilação, o código é interpretado.
. java inte com rpr e pila ta Passos de Compilação de Java Compilador . class JVM
O que é Java Bytecode? Java Bytecode é a linguagem intermediária à qual um código java é modificado. Utiliza apenas comandos simplificados e baseados em pilha. Muito semelhante a Assembly. Linguagem baseada em pilha. Cada opcode tem o tamanho de 1 byte (origem
Motivações A interpretação de um código Java exigiria uma maior complexidade do interpretador. Durante a compilação, otimizações são feitas. Menor velocidade de interpretação. Códigos mais eficientes são gerados. Portabilidade É possível executar o código em várias plataformas por ser interpretado.
Motivações Simplificações são realizadas: Short, char e boolean se tornam inteiros. Laços são simplificados For, while, do. . . while são iguais, apenas se comportam de forma diferente.
Códigos Equivalentes Dois códigos podem realizar uma mesma tarefa usando diferentes linguagens. Desta forma, é possível manter a semântica do programa original, apenas seu código é simplificado.
Códigos Equivalentes int foo(int a) { if (a == 0) { return 0; } else { return 1; } } int foo(int a): 0: ILOAD_1 1: IFEQ 4 2: BIPUSH 1 3: IRETURN 4: BIPUSH 0 5: IRETURN
Códigos Equivalentes int foo() { int result = 0; 0: BIPUSH 0 while (result < 2) { 1: ISTORE_1 result++; } int foo(): 2: GOTO 4 } 3: IINC 1, 1 return result; 4: ILOAD_1 5: BIPUSH 2 6: IF_ICMPGE 3 7: ILOAD_1
Java Bytecode 1. O que é Bytecode 2. Formato de um arquivo. class 3. Principais comandos 4. Motivações para aprender bytecode 5. Principais Dificuldades
Formato de um Arquivo. class O arquivo compilado (. class) armazena as informações específica da classe. Métodos (privado, protegido, publico). Campos. Atributos. Constant Pool. .
Formato de um Arquivo. class
Constant Pool Local onde estão armazenadas todas as constantes. Nome das classes usadas. Nome dos métodos. Valores iniciais. .
Inner Class As inner class, apesar de estarem dentro de uma classe, após a compilação são “extraidas” e um. class é criado para as mesmas. – É criado algo como: Hello. World$1. class
Java Bytecode 1. O que é Bytecode 2. Formato de um arquivo. class 3. Principais comandos 4. Motivações para aprender bytecode 5. Principais Dificuldades
Principais Comandos Aritméticos (inteiros): IADD – soma. ISUB – subtração. IMUL – multiplicação. IDIV – divisão. .
Principais Comandos Pilha: BIPUSH – empilha uma constante (inteiro). POP – remove da pilha (independente de tipo). LDC – empilha uma constant (string ou double). .
Principais Comandos Mudança de fluxo: IFEQ, IFNE, IFLT, IFLE, IFGT, IFGE Compara um inteiro que está na pilha com zero. Caso o resultado seja verdadeiro, modifica o fluxo para o endereço especificado como parâmetro. GOTO Mudança de fluxo incondicional (modifica o fluxo sem nenhuma verificação).
Principais Comandos Chamada de métodos: INVOKESTATIC, INVOKEINTERFACE, INVOKEVIRTUAL, INVOKESPECIAL. Chama um método de acordo com o seu modificador (estático, método de uma interface, método de uma instância)
Parâmetros Alguns comandos usam parâmetros na pilha. INVOKEVIRTUAL precisa que a referência do objeto esteja na pilha. Além disto, alguns comandos podem possuir mais de um parâmetro. IINC 1, 1 – soma o valor 1 a variável com índice 1.
Java Bytecode 1. O que é Bytecode 2. Formato de um arquivo. class 3. Principais comandos 4. Motivações para aprender bytecode 5. Principais Dificuldades
Por que aprender bytecode? Para se ter proficiência em Java, não é necessário aprender bytecode. Da mesma forma que para aprender uma linguagem de alto nível, não é necessário aprender linguagem de máquina. Existem poucas bibliotecas que permitem a manipulação de bytecode. BCEL, biblioteca da Apache, não está mais sendo desenvolvida e possui muitos bugs.
Por que aprender bytecode? Apenas em casos específicos será necessário o conhecimento de bytecode. No desenvolvimento de sistemas, dificilmente será necessário conhecer alguma coisa sobre bytecode. O compilador de Java já realiza otimizações eficientes. Escrever manualmente um código bytecode possivelmente será menos eficiente que um código gerado pelo compilador.
Motivações Manipular bytecode permite introduzir novas funcionalidades a códigos “fechados” (instrumentação). Alguns sistemas são disponibilizados sem código fonte, a modificação destes códigos se torna impossível sem a manipulação de bytecode. Debugging. É possível introduzir traces no código para identificação de bugs e análise de fluxo.
Motivações Análise de cobertura de testes unitários. Ferramentas que analizam a cobertura de testes de unidade utilizam instrumentação para a inserção de traces no código. Modificação de operadores de fluxo de controle. É possível escrever geradores automáticos de teste de mutação.
Ferramentas que Usam Manipulação de Bytecode • Aspect. J – • Ecl. Emma – • Ferramenta de Orientação a Aspectos. Mede cobertura dos testes. JAD (JAva Decompiler) – Descompila classes Java.
Java Bytecode 1. O que é Bytecode 2. Formato de um arquivo. class 3. Principais comandos 4. Motivações para aprender bytecode 5. Principais Dificuldades
Principais Dificuldades - Loops • Devido as suas simplificações, não é possível distinguir um while, do. . . while e for. • Por padrão, um loop é toda estrutura de mudança de fluxo condicional que possui um fluxo no sentido inverso.
Principais Dificuldades - Loops • Todo loop é composto por um operador de fluxo condicional. – • Condição de parada. É difícil identificar o inicio e o fim de loop.
Principais Dificuldades - Loops • Para identificar um loop é necessário realizar uma análise do fluxo do programa. • Ai ser encontrado um operador de mudança de fluxo condicional que mude o fluxo no sentido contrário, foi encontrado o fim do bloco do loop. • O operador de condição de parada sempre aponta para o inicio do bloco.
Principais Dificuldades - Loops int foo(): 0: BIPUSH 0 Corpo do loop 1: ISTORE_1 2: GOTO 4 3: IINC 1, 1 4: ILOAD_1 5: BIPUSH 2 6: IF_ICMPGE 3 7: ILOAD_1 Condição de parada
Modificando um Loop int foo(): 0: BIPUSH 0 1: ISTORE_1 2: GOTO 4 3: IINC 1, 1 4: ILOAD_1 5: BIPUSH 2 6: IF_ICMPGE 3 7: ILOAD_1 BIPUSH 2 IADD ISTORE_1
Modificando um Loop int foo(): 0: BIPUSH 0 1: ISTORE_1 2: GOTO 4 3: IINC 1, 1 4: ILOAD_1 5: BIPUSH 2 6: IF_ICMPGE 3 7: ILOAD_1
Modificando um Loop int foo(): 0: BIPUSH 0 1: ISTORE_1 2: GOTO 11 3: ILOAD_1 4: BIPUSH 2 5: IADD 6: ISTORE_1 7: IINC 1, 1 Atualiza endereços do operadores de mudança de fluxo.
Principais Dificuldades - Ifs • Nem todo operador condicional de mudança de fluxo pode ser automáticamente traduzido como um if. – • Como visto anteriormente, este pode ser a condição de parada de um loop. Em um caso de if. . . else não existem 2 operadores, apenas um.
Principais Dificuldades - Ifs void foo(int x, int y) { if (x > y) {. . . } else {. . . } } void foo(int x, int y): 0: ILOAD_1 1: ILOAD_2 2: IF_ICMPLE 5 3: bloco do if 4: GOTO 6 5: bloco do else 6: RETURN
- Slides: 38