Niby nic, a jednak. Wymyśliłem sobie, że pewien program w Javie zamiast wypluwać XML’a, który później musi być przyjęty jako konfiguracja innego programu w Javie będzie generował bytecode klasy z wypełnionymi danymi, tak, żeby nie trzeba bylo tracić ani czasu ani zasobów na konwersje XML’a do jakiegos znośnego formatu.
Jak zwykle trzeba sie na początek troche porozglądać, żeby sprawdzić jakie są dostępne narzędzia. Do głowy przyszło mi wykorzystywane w springu i hibernate cglib, ale strone mają jakąś toporną, więc szukałem dalej. Moje wcześniejsze poszukiwania opcode’ów w php , zwróciło moją uwagę w kierunku specyfikacji Virtualnej Machiny
, no i rzuciłem ją na tapetę, ale że to kobyła i do tego nudna jak flaki z olejem, to postanowiłem wypróbować czegoś w praktyce.
Poszukiwania przywiodły mnie do bilioteki asm.
Pozostało tylko pisać, ale asm oferuje jeszcze jedno świetne narzędzie, plugin do eclipse’a pozwalający:
- podejrzeć bytecode danej klasy
- podejrzeć kod samego ASM potrzebny do wygenerowania aktualnie wybranego fragmentu kodu
Szczególnie druga opcja daje spore pole do popisu. Można po prostu napisać kawałek kodu w edytorze, zaznaczyć go i zobaczyć jakie instrukcje trzeba wykonać, żeby coś identycznego wypluć jako bytecode. Dla mnie świetna sprawa do pokombinowania troche, świetnej zabawy, a przy okazji szybkiego zdobywania wiedzy.
Oto wynik:
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.aspectj.org.objectweb.asm.ClassWriter;
import org.aspectj.org.objectweb.asm.Label;
import org.aspectj.org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Constants;
public class Bla implements Constants {
public Bla(){
}
public void bla(String s){
System.out.println(s);
}
public static void bla(int s){}
public static void main(String[] args) throws Exception {
ClassWriter cw = new ClassWriter(false);
cw.visit(
V1_5, // version
ACC_PUBLIC+ACC_SUPER, // access
"asm1/Notifier", // class name
null, // signature
"java/lang/Object", // super class
(String[])null // interfaces
);
MethodVisitor mv;
mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLocalVariable("this", "LBla;", null, l0, l2, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
mv = cw.visitMethod( ACC_PUBLIC,
"notify", // method name
"(Ljava/lang/String;)V", // description
"", // method descriptor
new String[]{} // exceptions
);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
cw.visitEnd();
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("/tmp/asm1/Notifier.class")));
out.write(cw.toByteArray());
out.flush();
out.close();
Class c = Class.forName("asm1.Notifier");
Object o = c.newInstance();
Method m = c.getMethod("notify", new Class[]{String.class});
m.invoke(o, new Object[]{"Hello World!"});
m.invoke(o, new Object[]{"Some other text
"});
}
}
Jak widać, kod np konstruktora jest żywcem przepisany z okienka „bytecode”, ale co najważniejsze TO DZIAŁA!
Pare pomocnych wskazówek:
- odpalenie skompilowanego programu spod Eclipse poprzez run, nie zadziałało, pokazał mi błedy z zarządzaniem zabezpieczeniami i tyle sie skończylo, ale po odpowiednim dostarczeniu argumentów z wiersza poleceń (używając -Xbootclasspath/p: orac -classpath) działa jak złoto
- sam „bytecode plugin” nie działa w wersji Eclipse’a 3.4 i trzeba sobie dodać nowy URL do listy źródeł oprogramowania, a na liście pojawią się pluginy dla tejże wersji
- jak zapewne widać moja klasa Bla, posłużyła za szablon instrukcji jakie trzeba wykonać, żeby wypisać poprawny bytecode (metoda bla oraz konstruktor) a piszę o tym po to, żeby zaczęcić do tejże metody