380 likes | 507 Views
Java Bytecode. Software Básico. Mitsuo Takaki. Java Bytecode. O que é Bytecode Formato de um arquivo .class Principais comandos Motivações para aprender bytecode Principais Dificuldades. Java Bytecode. O que é Bytecode Formato de um arquivo .class Principais comandos
E N D
Java Bytecode Software Básico Mitsuo Takaki
Java Bytecode • O que é Bytecode • Formato de um arquivo .class • Principais comandos • Motivações para aprender bytecode • Principais Dificuldades
Java Bytecode • O que é Bytecode • Formato de um arquivo .class • Principais comandos • Motivações para aprender bytecode • 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.
Passos de Compilação de Java Compilador JVM compila interpreta .java .class
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 do nome bytecode).
Motivações • A interpretação de um código Java exigiria uma maior complexidade do interpretador. • Menor velocidade de interpretação. • Durante a compilação, otimizações são feitas. • 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.
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 a) { • if (a == 0) { • return 0; • } else { • return 1; • } • }
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 8: IRETURN Códigos Equivalentes • int foo() { • int result = 0; • while (result < 2) { • result++; • } • return result; • }
Java Bytecode • O que é Bytecode • Formato de um arquivo .class • Principais comandos • Motivações para aprender bytecode • 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. • ...
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: HelloWorld$1.class
Java Bytecode • O que é Bytecode • Formato de um arquivo .class • Principais comandos • Motivações para aprender bytecode • 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 • O que é Bytecode • Formato de um arquivo .class • Principais comandos • Motivações para aprender bytecode • 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 • AspectJ • Ferramenta de Orientação a Aspectos. • EclEmma • Mede cobertura dos testes. • JAD (JAva Decompiler) • Descompila classes Java.
Java Bytecode • O que é Bytecode • Formato de um arquivo .class • Principais comandos • Motivações para aprender bytecode • 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 • 1: ISTORE_1 • 2: GOTO 4 • 3: IINC 1,1 • 4: ILOAD_1 • 5: BIPUSH 2 • 6: IF_ICMPGE 3 • 7: ILOAD_1 • 8: IRETURN Corpo do loop 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 • 8: IRETURN 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 • 8: IRETURN
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 • 8: ILOAD_1 • 9: BIPUSH 2 • 10: IF_ICMPGE 3 • 11: ILOAD_1 • 12: IRETURN 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.
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 Principais Dificuldades - Ifs • void foo(int x, int y) { • if (x > y) { • ... • } else { • ... • } • }