manipulação de bytecodes java
DESCRIPTION
Manipulação de Bytecodes Java. Giuliano Mega. Introdução. O que é bytecode ? Código intermediário Semelhante aos códigos de montagem ( assembly ) Byte-code – opcodes de 1 byte Processadores virtuais (Virtual Machines) Quem usa? Java (JVM), Smalltalk (Slang), .NET (CLR). Introdução. - PowerPoint PPT PresentationTRANSCRIPT
1
Manipulação de Bytecodes Java
Giuliano Mega
2
Introdução
• O que é bytecode?• Código intermediário• Semelhante aos códigos de
montagem (assembly)• Byte-code – opcodes de 1 byte• Processadores virtuais (Virtual
Machines)• Quem usa?
• Java (JVM), Smalltalk (Slang), .NET (CLR)
3
Introdução
• Assuntos em pauta• Por que?
• Reflexão• Como?
• JVM• Bytecodes• Técnicas de compilação• Arcabouços
• Suposição• Você conhece Java• Você sabe o que é um Class Loader
4
Reflexão Computacional
• Sistemas computacionais• Sistema de computador que responde
perguntas ou dá apoio a certas ações sobre algum domínio
• Sistema representa o seu domínio por meio de estruturas de dados
• Representação causalmente conexa
5
Reflexão Computacional (cont.)
• Meta-sistema• Domínio = outro sistema.• Sistema-objeto.
• Sistema reflexivo• Meta-sistema causalmente conexo cujo
sistema-objeto é ele próprio. • Conseqüência
• Pode modificar aspectos de sua execução como efeito dela mesma.
6
Arquiteturas Reflexivas
• Linguagem dá acesso a estruturas que modificam aspectos da interpretação da linguagem.
• Interpretadores metacirculares e variantes• Forma muito explorada de implementação;• poderosa e garantida;• conexão causal garantida pela circularidade.
programa
interpretador
7
Arquiteturas Reflexivas (cont.)
• Reflexão [1]• Ad hoc• Facilidades providas pela linguagem• Arquitetura reflexiva:
• Reconhece reflexão como conceito;• provê meios para fazê-la.• Finalidade aberta (open-ended).
• Alguns exemplos:• 3-LISP,BROWN (funcionais)• META-PROLOG, FOL (lógicas)• TEIRESIAS, SOAR (regras)• 3-KRS, SuperEgo (OO)
• Prá que que serve afinal?
8
Reflexão - aplicações
• Aplicações• depuração [1];• prog. concorrente [2];• backtracking [3];• futures [4];• AOP [5];• etc.
• Tudo isso no nível da linguagem• Sem extensões sinistras
ao interpretador
9
Reflexão Java
• Afinal, Java é “reflexiva”?• Não conta com uma arquitetura
reflexiva.• Segunda categoria
• Ausência de meta-nível claro• Introspecção confusa
• Limitada• Class loaders• Proxies dinâmicos (JDK 1.3)
• Justificativa: desempenho, type-safety [6].
10
Reflexão Java (cont.)
• Programadores Java também precisam de reflexão• logging, estatísticas, tracing, depuração,
persistência, AOP...• Reflexão comportamental/estrutural
• Proxies dinâmicos• Factories• Não-ortogonais• Aplicações já prontas – difícil.
11
Reflexão Java (cont.)
• “Reflexão” em tempo de compilação (Open Java [7] e cia.)• Não é reflexão, é metaprogramação.
• Reflexão em tempo de carga.• Class loaders• Portável entre JVMs• Dizem que é reflexão estrutural• Opinião pessoal: forçada de barra
• Formas desajeitadas e não-portáveis de reflexão comportamental/estrutural pela JVMTI (Sun).
• Java e adaptação não combinam.• Flexibilidade => adaptação [8].
12
Manipulação em Tempo de Carga
• Forma mais comum e aceita• Portável• Não requer código fonte• “Fácil” e “eficiente”
class SRLoader extends ClassLoader { public Class loadClass(String name) { byte[] bytecode = readClassFile(name); <mexa no bytecode o quanto quiser> return resolveClass(defineClass(bytecode)); }}
13
JVM – formato das classes
Formato de classes Java [9]
14
JVM – básico
• JVM é um processador virtual• pilhas, “registradores”
• Preocupação com segurança• verificação• opcodes tipados (tipos primitivos)
15
JVM - A linguagem de montagem
• Um pequeno exemplo:
• Estáticos:• Tamanho máximo da pilha• Número de variáveis locais (slots)
16
Manipulação Revisitada
• Manipular classes:• Complexo• Muitas dependências• Classe = array de bytes• Necessário interpretar os dados
• Arcabouços (BCEL e muitos outros):
17
Arcabouços de Manipulação
• Forma OO de representar/manipular o bytecode
• Variam em grau de abstração, tamanho, desempenho...• ByteCode Engineering Library* (BCEL) [10]• ASM [11]• Java Assistant* (Javassist) [12]• Code Generation LIBrary (CGLIB)• SERP• JOIE• etc.
18
O Java Assistant (Javassist)
• Nosso foco: manipulação de alto nível• Intuitivo• Fácil de usar• Dificilmente produz código inválido
• Exemplo:• Medir desempenho (interceptadores): private String buildString(int length) { String result = ""; for (int i = 0; i < length; i++){ result += (char)(i%26 + 'a'); } return result; }
19
Javassist (cont.)
• Desempenho
• Class Loader• O Javassist já provê um esqueleto
• javassist.Loader extends java.lang.ClassLoader• Você só escreve os ganchos
• interface javassist.Translator
private String buildString(int length) { long start = System.currentTimeMillis(); String result = ""; for (int i = 0; i < length; i++) { result += (char)(i%26 + 'a'); } System.out.println("Call to buildString took " + (System.currentTimeMillis()-start) + " ms."); return result; }
20
Javassist (cont.)
• Translator
• Restrições• variáveis locais (lembre-se do
bytecode...)• como faço para implementar o exemplo?• “expressão idiomática Javassist” prá isso
public interface Translator { public void start(ClassPool pool) throws NotFoundException, CannotCompileException; public void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException;}
21
Javassist (cont.) CtClass clas = pool.get(cname);CtMethod[] meths = clas.getDeclaredMethods();for (int i = 0; i < meths.length; i++) { CtMethod mold = methds[i]; String nname = mname+"$impl"; /* 1) Muda o nome do método e duplica com o nome antigo */ mold.setName(nname); CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null);
/* 2) Cria o embrulho-cronômetro para o método original */ String type = mold.getReturnType().getName(); StringBuffer body = new StringBuffer(); body.append("{\nlong start = System.currentTimeMillis();\n"); if (!"void".equals(type)) { body.append(type + " result = "); } body.append(nname + "($$);\n"); ...
/* 3) Substitui o código do método embrulhado */ mnew.setBody(body.toString()); clas.addMethod(mnew);}
22
Javassist (cont.)
• A técnica envolve ainda um “lançador”• Em geral provido pelo arcabouço
• Limitações• Não é muito flexível• Classes internas e anônimas• Código original vai parar em outro
método• Lento• Modificações internas
• Introspecção limitada• Proposta não é essa
• Tem uma API para bytecodes.
23
A ByteCode Engineering Library (BCEL)
• Transforma a classe Java num grafo• ByteCodes orientados a objeto
• Poderoso• Mais “intuitivo” que o Javassist
• Prá quem conhece bytecodes• Mais rápido que o Javassist
• Exemplo:• Instrumentar todos os métodos Run de
todos os Runnables
24
BCEL (cont.)
public JavaClass modifyClass(JavaClass jc) { /* 1) Testa se a classe é instância de Runnable */ JavaClass runnableIntf = Repository.lookupClass("java.lang.Runnable"); if(!jc.instanceOf(runnableIntf)) return jc; Method [] methods = jc.getMethods(); int i = 0; for(i = 0; i < methods.length; i++){ if(methods[i].getName().equals("run")) break; } /* 2) Cria as versões maleáveis (de-serializa) e as modifica */ ClassGen newClass = new ClassGen(jc); ConstantPoolGen cg = newClass.getConstantPool(); MethodGen mg = new MethodGen(m, newClass.getClassName(), cg); newClass.removeMethod(methods[i]); ...
/* 3) Calcula dados estáticos, re-serializa o bytecode */ mg.setMaxStack(); mg.setMaxLocals(); newClass.addMethod(mg.getMethod()); return newClass.getJavaClass();}
25
BCEL (cont.)
InstructionList il = new InstructionList(); InstructionFactory iFactory = new InstructionFactory(nc);
il.append(iFactory.createFieldAccess("java.lang.System", "out", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC)); il.append(new PUSH(cg, "Método \"run\" chamado na classe " + jc.getName()); il.append(iFactory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
InstructionList old_il = mg.getInstructionList(); InstructionHandle [] handles = old_il.getInstructionHandles(); old_il.insert(handles[0], il); mg.setInstructionList(old_il);
26
BCEL (cont.)
• Problemas• Ainda é lento;• pode ser muito complexo;• manipulação direta de bytecodes;• muito fácil gerar código inválido.
27
ASM
• Visitor pattern • Não seria/de-seria o grafo• Composição de visitors• Substituição
• Muito rápido• 700% mais rápido que o BCEL• 1100% mais rápido que o SERP
• Compacto • 25 kb vs. 350 kb do BCEL
• Lembra um pull parser prá XML.• Não alivia muito em manipulações
complexas.
28
Verificação de Bytecode
• O que há de errado com este (pseudo) bytecode?00 aload_0
01 invokevirtual “MyClass#test()”
02 ifne 05
03 aload_0
04 invokevirtual “MyClass#test()”
05 return
Rode isso e...:
java.lang.VerifyError: stack sizes differ (1 != 0).
29
Verificação de Bytecode (cont.)
• Esse código não passa pelo verificador (pág. 12)
• Verificador• Algoritmo de análise de fluxo• Prova um teorema ou arruma um contra-
exemplo• Problemas de decidibildade
• Restrições mais fortes do que o necessário.• Rejeita código “válido”.
30
Verificação de Bytecode (cont.)
00 aload_0
01 invokevirtual “MyClass.test()”
02 ifne 05
03 aload_0
04 invokevirtual “MyClass.test()”
05 return
• Análise de tipos + stack merging = código seguro+ chatice.
• Merging em laços.
31
Verificação de Bytecode (cont.)
• Compilador “burla” o verificador• Sempre produz código válido;• equivalente ao código-fonte;• nós não temos o código-fonte.
• Transformações difíceis• Armadilhas (traps);• implementadas com subrotinas.
32
Problemas Adicionais
• Class Loader que instrumenta:• Tem que definir a classe;• saber o que “deixar passar” (i.e.
java.lang.*);• getSystemClassloader();• a opção -Dendorsed.dirs=...;
• Limitação: classes de sistema.
33
Conclusão
• Java: reflexão deficitária.• Desempenho.• Segurança.
• Manipulação:• Reflexão estrutural limitada.
• Manipulação em tempo de carga:• em acordo com a filosofia Java.
• Arcabouços:• facilitam a tarefa.• compromisso entre flexibilidade,
complexidade e desempenho.
34
Referências:
[1] P. Maes. Concepts and Experiments in Computational Reflection. In Proceedings of the OOPSLA’87. pages 147-155. Orlando, Florida.
[2] B. Foote. Objects, Reflection and Open Languages. In Proceedings of the 1992 ECOOP Workshop on Object-Oriented reflection and Meta-Level Architectures. Utrecht, Netherlands
[3] W. R. LaLonde and M. V. Gulik. Building a Backtracking Facility in Smalltalk Without Kernel Support. In Proceedings of the OOPSLA’88. pages 105-122. San Diego, California.
[4] B. Foote and R. Johnson. Reflective Facilities in Smalltalk-80. In Proceedings of the OOPSLA’89. pages 327-335. New Orleans, Louisiana.
[5] S. Kojarski and D. Lorentz. AOP as a First-class reflective mechanism. In Companion to the OOPSLA’04. pages 216 – 217. Vancouver, Canada.
[6] S. Liang and G. Bracha. Dynamic Class Loading in the Java Virtual Machine. In Proceedings of the OOPSLA’98. pages 36-44. Vancouver, Canada.
[7] M. Tsubori, S. Chiba et. al. OpenJava: A Class-based Macro System for Java. In Reflection and Software Engineering, LNCS 1826, Springer-Verlag, 2000. /15
35
Referências (cont.)
[8] B. Foote. Class Warfare: Classes vs. Prototypes. In Proceedings of the OOPSLA’89 Workshop on Objects without Classes, New Orleans, LA.
[9] T. Lindholm and F. Yellin. The Java Virtual Machine Specification. Addison-Wesley, 2nd edition, 1999.
[10] BCEL website. http://apache.jakarta.org/bcel. [june 9, 2005][11] E. Bruneton, R. Lenglet and T. Coupaye. ASM: a code manipulation tool to
implement adaptable systems. In ACM SIGOPS Adaptable and extensible component systems. 2002, Grenoble, France.
[12] Javassist website. http://www.csg.is.titech.ac.jp/~chiba/javassist [june 8, 2005]
36
Dúvidas?