Gorący temat, JVM jest wszędzie. Lubimy go czy nie, znaczna część rynku pracy to Java i, co raz częściej Scala i Kotlin. Programowanie na Androida to JVM. Proszę nie mylić JVM z Javą. Nie mam zamiaru chwalić ani potępiać tego języka (nie lubię go, ale wiem, że jest taki w konkretnym celu i szanuję to). To co tu napiszę to część wspólna implementacji Javy, Scali, Kotlina, Clojure i innych. Istnieje też wiele implementacji innych języków w oparciu o JVM, m.in. Common Lisp, Haskell, PHP, JavaScript, Scheme, Prolog, SmallTalk (pełna lista), dlatego użytkownicy tych języków, również mogą chcieć skorzystać. Mało kto, wie, ale JVM jest zaimplementowany również w krzemie (jako CPU Pico Java lub rozszerzenie dla specyfikacji ARM).
Nie mam zamiaru pisać o konkretnej implementacji JVM, bardziej o korzyściach z posiadania binarki z kodem bajtowym dla JVM. Tak więc nie będzie implementacji kompilacji w locie (ang. Just In Time compilation) czy śmieciarza (garbage collector), za to będzie porównanie korzyści z binarkami zawierającymi kod maszynowy (np. x86 lub ARM). Nie będę też pisać na temat biblioteki standardowej Javy, choć jest przydatna, nie jest konieczna (przynajmniej w teorii). Podobnie jak możliwa jest konwersja binarki z kodu bajtowego do kodu maszynowego.
Podstawową jednostką kodu dla JVM jest klasa. Każda klasa ma oddzielny plik z kodem bajtowym. Pliki klas są pakowane zipem wraz z krótkim manifestem i to jest plik .jar, w JVM jest to format wspólny dla bibliotek i aplikacji. Jednak na Androidzie pliki .jar są używane tylko na etapie kompilacji, w gotowym pliku .apk mamy kod umieszczony w alternatywnym formacie .dex. Plik .apk też jest archiwum zip, oprócz jednego lub wielu plików .dex, zawiera również zasoby (grafikę, specyfikację UI, lokalizacje, itp.) i manifest.
W metadanych znajdziemy wszystkie zależności (klasy, metody), a wszystkie metody i wartości stałe są wyodrębnione i nazwane. To zupełnie inaczej niż w przypadku typowego kodu maszynowego, który często jest dystrybuowany bez takich metadanych. Wbudowany system obsługi błędów, korzystający z tych metadanych skutecznie zachęca dystrybutora do zamieszczania pełnych metadanych w binarce, co stanowi istotną wartość dla użytkownika, który chciałby zrozumieć co aplikacja robi.
Co więcej, znajdziemy więcej, bo formaty takie jak ELF (Linux) czy PE (Windows, UEFI) nie przewidują takich informacji jak oznaczenia łapanych wyjątków. Dodatkowo zestaw dostępnych rozkazów w JVM jest wysokopoziomowy, zawiera instrukcje do tworzenia obiektów, obsługi tablic, wywołania metod, itp. Dzięki temu jest znacznie prosty w użyciu i analizie. Co więcej, podobnie jak w przypadku procesorów ARM, każdy rozkaz ma ten sam rozmiar w bajtach. W procesorach x86 jest inaczej i jest to powszechnie używane do dodatkowego zaciemniania kodu.
Odpowiednikiem uniksowego objdump jest javap -v i jest znacznie bardziej użyteczne niż w przypadku kodu maszynowego. Oto wycinki dezasemblacji jednej z klas, które napisałem w Scali:
~ $ javap -v Shell.class
Classfile /home/lew/shared/tools/dver/target/scala-2.10/classes/me/leo/dver/
Shell.class
Last modified 2019-05-14; size 8293 bytes
MD5 checksum 1c78033c36d816400ff09f64be2c17a7
Compiled from "html.scala"
public class me.leo.dver.Shell implements me.leo.dver.Iface
minor version: 0
major version: 50
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Utf8 me/leo/dver/Shell
#2 = Class #1 // me/leo/dver/Shell
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 me/leo/dver/Iface
#6 = Class #5 // me/leo/dver/Iface
#7 = Utf8 html.scala
#8 = Utf8 Lscala/reflect/ScalaSignature;
#9 = Utf8 bytes
// [...]
#18 = NameAndType #13:#17 // merge:(Lme/leo/dver/Iface;Lscal
a/collection/immutable/List;)Lscala/collection/immutable/List;
#19 = Methodref #16.#18 // me/leo/dver/Iface$class.merge:(
Lme/leo/dver/Iface;Lscala/collection/immutable/List;)Lscala/collection/immut
able/List;
#20 = Utf8 this
#21 = Utf8 Lme/leo/dver/Shell;
#22 = Utf8 ifaces
#23 = Utf8 Lscala/collection/immutable/List;
#24 = Utf8 jsFun
#25 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang
/String;
#26 = Utf8 (Lme/leo/dver/Iface;Ljava/lang/String;Ljava/lang
/String;)Ljava/lang/String;
#27 = NameAndType #24:#26 // jsFun:(Lme/leo/dver/Iface;Ljava
/lang/String;Ljava/lang/String;)Ljava/lang/String;
#28 = Methodref #16.#27 // me/leo/dver/Iface$class.jsFun:(
Lme/leo/dver/Iface;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#29 = Utf8 name
#30 = Utf8 body
#31 = Utf8 (Ljava/lang/String;Ljava/lang/String;Ljava/lang/
String;)Ljava/lang/String;
#32 = Utf8 (Lme/leo/dver/Iface;Ljava/lang/String;Ljava/lang
/String;Ljava/lang/String;)Ljava/lang/String;
#33 = NameAndType #24:#32 // jsFun:(Lme/leo/dver/Iface;Ljava
/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#34 = Methodref #16.#33 // me/leo/dver/Iface$class.jsFun:(
Lme/leo/dver/Iface;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lj
ava/lang/String;
// [....]
#130 = Class #129 // me/leo/dver/Shell$$anonfun$2
#131 = Methodref #130.#112 // me/leo/dver/Shell$$anonfun$2."<
init>":(Lme/leo/dver/Shell;)V
#132 = Utf8 foldLeft
#133 = Utf8 (Ljava/lang/Object;Lscala/Function2;)Ljava/lang/
Object;
#134 = NameAndType #132:#133 // foldLeft:(Ljava/lang/Object;Lsc
ala/Function2;)Ljava/lang/Object;
#135 = InterfaceMethodref #125.#134 // scala/collection/LinearSeqOptim
ized.foldLeft:(Ljava/lang/Object;Lscala/Function2;)Ljava/lang/Object;
#136 = Utf8 refresh_output
#137 = String #136 // refresh_output
#138 = Utf8 asFun$default$2
#139 = NameAndType #138:#75 // asFun$default$2:()Ljava/lang/St
ring;
#140 = Methodref #127.#139 // me/leo/dver/Js.asFun$default$2:
()Ljava/lang/String;
// [...]
#151 = Utf8 ev.keyCode == 13
#152 = String #151 // ev.keyCode == 13
#153 = Utf8 (Ljava/lang/String;)V
#154 = NameAndType #78:#153 // "<init>":(Ljava/lang/String;)V
#155 = Methodref #127.#154 // me/leo/dver/Js."<init>":(Ljava/
lang/String;)V
#156 = Utf8 me/leo/dver/JsVar
#157 = Class #156 // me/leo/dver/JsVar
#158 = Utf8 cmd
#159 = String #158 // cmd
#160 = Methodref #157.#154 // me/leo/dver/JsVar."<init>":(Lja
va/lang/String;)V
// [...]
#381 = Utf8 ()Lscala/collection/immutable/Map<Ljava/lang/Str
ing;Ljava/lang/String;>;
#382 = Utf8 ()Lscala/collection/immutable/List<Lme/leo/dver/
Tag;>;
#383 = Utf8 SourceFile
#384 = Utf8 InnerClasses
#385 = Utf8 RuntimeVisibleAnnotations
#386 = Utf8 ScalaSig
{
// Definicja pola klasy:
public final java.lang.String me$leo$dver$Shell$$path;
descriptor: Ljava/lang/String;
flags: ACC_PUBLIC, ACC_FINAL
// Proste definicje metod:
public scala.collection.immutable.List<me.leo.dver.Tag> merge(scala.collec
tion.immutable.List<me.leo.dver.Iface>);
descriptor: (Lscala/collection/immutable/List;)Lscala/collection/immutab
le/List;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokestatic #19 // Method me/leo/dver/Iface$cl
ass.merge:(Lme/leo/dver/Iface;Lscala/collection/immutable/List;)Lscala/colle
ction/immutable/List;
5: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lme/leo/dver/Shell;
0 6 1 ifaces Lscala/collection/immutable/List;
LineNumberTable:
line 302: 0
Signature: #380 // (Lscala/collection/immutable/
List<Lme/leo/dver/Iface;>;)Lscala/collection/immutable/List<Lme/leo/dver/Tag
;>;
public java.lang.String jsFun(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: aload_2
3: invokestatic #28 // Method me/leo/dver/Iface$cl
ass.jsFun:(Lme/leo/dver/Iface;Ljava/lang/String;Ljava/lang/String;)Ljava/lan
g/String;
6: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lme/leo/dver/Shell;
0 7 1 name Ljava/lang/String;
0 7 2 body Ljava/lang/String;
LineNumberTable:
line 302: 0
public java.lang.String jsFun(java.lang.String, java.lang.String, java.lan
g.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljav
a/lang/String;
flags: ACC_PUBLIC
Code:
stack=4, locals=4, args_size=4
0: aload_0
1: aload_1
2: aload_2
3: aload_3
4: invokestatic #34 // Method me/leo/dver/Iface$cl
ass.jsFun:(Lme/leo/dver/Iface;Ljava/lang/String;Ljava/lang/String;Ljava/lang
/String;)Ljava/lang/String;
7: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lme/leo/dver/Shell;
0 8 1 name Ljava/lang/String;
0 8 2 args Ljava/lang/String;
0 8 3 body Ljava/lang/String;
LineNumberTable:
line 302: 0
public java.lang.String jsId(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokestatic #40 // Method me/leo/dver/Iface$cl
ass.jsId:(Lme/leo/dver/Iface;Ljava/lang/String;)Ljava/lang/String;
5: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lme/leo/dver/Shell;
0 6 1 id Ljava/lang/String;
LineNumberTable:
line 302: 0
public java.lang.String fileContent(java.io.File);
descriptor: (Ljava/io/File;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokestatic #46 // Method me/leo/dver/Iface$cl
ass.fileContent:(Lme/leo/dver/Iface;Ljava/io/File;)Ljava/lang/String;
5: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lme/leo/dver/Shell;
0 6 1 f Ljava/io/File;
LineNumberTable:
line 302: 0
public me.leo.dver.Js writeTo(java.lang.String, me.leo.dver.Js);
descriptor: (Ljava/lang/String;Lme/leo/dver/Js;)Lme/leo/dver/Js;
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: aload_2
3: invokestatic #53 // Method me/leo/dver/Iface$cl
ass.writeTo:(Lme/leo/dver/Iface;Ljava/lang/String;Lme/leo/dver/Js;)Lme/leo/d
ver/Js;
6: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lme/leo/dver/Shell;
0 7 1 inputId Ljava/lang/String;
0 7 2 text Lme/leo/dver/Js;
LineNumberTable:
line 302: 0
public me.leo.dver.Js literalTab(java.lang.String);
descriptor: (Ljava/lang/String;)Lme/leo/dver/Js;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokestatic #61 // Method me/leo/dver/Iface$cl
ass.literalTab:(Lme/leo/dver/Iface;Ljava/lang/String;)Lme/leo/dver/Js;
5: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lme/leo/dver/Shell;
0 6 1 inputId Ljava/lang/String;
LineNumberTable:
line 302: 0
// [...] Tu bardziej niskopoziomowy kod:
public java.lang.String js();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=14, locals=13, args_size=1
0: new #77 // class scala/collection/muta
ble/StringBuilder
3: dup
4: invokespecial #81 // Method scala/collection/mut
able/StringBuilder."<init>":()V
7: getstatic #87 // Field scala/collection/immu
table/List$.MODULE$:Lscala/collection/immutable/List$;
10: getstatic #92 // Field scala/Predef$.MODULE$
:Lscala/Predef$;
13: iconst_2
14: anewarray #94 // class java/lang/String
17: dup
18: iconst_0
19: ldc #96 // String out
21: aastore
22: dup
23: iconst_1
24: ldc #98 // String err
26: aastore
27: checkcast #100 // class "[Ljava/lang/Object;"
30: invokevirtual #104 // Method scala/Predef$.wrapRe
fArray:([Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
33: invokevirtual #108 // Method scala/collection/imm
utable/List$.apply:(Lscala/collection/Seq;)Lscala/collection/immutable/List;
36: new #110 // class me/leo/dver/Shell$$an
onfun$1
39: dup
40: aload_0
41: invokespecial #113 // Method me/leo/dver/Shell$$a
nonfun$1."<init>":(Lme/leo/dver/Shell;)V
44: getstatic #87 // Field scala/collection/immu
table/List$.MODULE$:Lscala/collection/immutable/List$;
47: invokevirtual #117 // Method scala/collection/imm
utable/List$.canBuildFrom:()Lscala/collection/generic/CanBuildFrom;
50: invokevirtual #123 // Method scala/collection/imm
utable/List.map:(Lscala/Function1;Lscala/collection/generic/CanBuildFrom;)Lj
ava/lang/Object;
53: checkcast #125 // class scala/collection/Line
arSeqOptimized
56: new #127 // class me/leo/dver/Js
59: dup
60: invokespecial #128 // Method me/leo/dver/Js."<ini
t>":()V
63: new #130 // class me/leo/dver/Shell$$an
onfun$2
66: dup
67: aload_0
68: invokespecial #131 // Method me/leo/dver/Shell$$a
nonfun$2."<init>":(Lme/leo/dver/Shell;)V
71: invokeinterface #135, 3 // InterfaceMethod scala/colle
ction/LinearSeqOptimized.foldLeft:(Ljava/lang/Object;Lscala/Function2;)Ljava
/lang/Object;
76: checkcast #127 // class me/leo/dver/Js
79: astore_1
80: ldc #137 // String refresh_output
82: astore_2
83: aload_1
84: invokevirtual #140 // Method me/leo/dver/Js.asFun
$default$2:()Ljava/lang/String;
87: astore_3
88: aload_1
89: aload_2
90: aload_3
91: invokevirtual #143 // Method me/leo/dver/Js.asFun
:(Ljava/lang/String;Ljava/lang/String;)Lme/leo/dver/Js;
94: invokevirtual #146 // Method me/leo/dver/Js.code:
()Ljava/lang/String;
97: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
100: new #127 // class me/leo/dver/Js
103: dup
104: invokespecial #128 // Method me/leo/dver/Js."<ini
t>":()V
107: new #127 // class me/leo/dver/Js
110: dup
111: ldc #152 // String ev.keyCode == 13
113: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
116: new #157 // class me/leo/dver/JsVar
119: dup
120: ldc #159 // String cmd
122: invokespecial #160 // Method me/leo/dver/JsVar."<
init>":(Ljava/lang/String;)V
125: new #162 // class me/leo/dver/JsId
128: dup
129: ldc #164 // String sh_in
131: invokespecial #165 // Method me/leo/dver/JsId."<i
nit>":(Ljava/lang/String;)V
134: invokevirtual #169 // Method me/leo/dver/JsVar.se
t:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
137: new #157 // class me/leo/dver/JsVar
140: dup
141: ldc #96 // String out
143: invokespecial #160 // Method me/leo/dver/JsVar."<
init>":(Ljava/lang/String;)V
146: new #162 // class me/leo/dver/JsId
149: dup
150: ldc #171 // String sh_out
152: invokespecial #165 // Method me/leo/dver/JsId."<i
nit>":(Ljava/lang/String;)V
155: invokevirtual #169 // Method me/leo/dver/JsVar.se
t:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
158: invokevirtual #174 // Method me/leo/dver/Js.$plus
:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
161: new #127 // class me/leo/dver/Js
164: dup
165: ldc #176 // String refresh_output();
167: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
170: invokevirtual #174 // Method me/leo/dver/Js.$plus
:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
173: new #178 // class me/leo/dver/JsHttp
176: dup
177: ldc #180 // String POST
179: new #182 // class me/leo/dver/JsLiteral
182: dup
183: new #77 // class scala/collection/muta
ble/StringBuilder
186: dup
187: invokespecial #81 // Method scala/collection/mut
able/StringBuilder."<init>":()V
190: ldc #184 // String /sh/
192: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
195: aload_0
196: getfield #186 // Field me$leo$dver$Shell$$pa
th:Ljava/lang/String;
199: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
202: invokevirtual #189 // Method scala/collection/mut
able/StringBuilder.toString:()Ljava/lang/String;
205: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
208: new #127 // class me/leo/dver/Js
211: dup
212: invokespecial #128 // Method me/leo/dver/Js."<ini
t>":()V
215: ldc #192 // String cmd.value
217: invokevirtual #195 // Method me/leo/dver/Js.jsVar
:(Ljava/lang/String;)Lme/leo/dver/Js;
220: new #127 // class me/leo/dver/Js
223: dup
224: ldc #197 // String setInterval(refresh_
output, 2000);
226: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
229: new #127 // class me/leo/dver/Js
232: dup
233: ldc #192 // String cmd.value
235: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
238: new #182 // class me/leo/dver/JsLiteral
241: dup
242: ldc #199 // String
244: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
247: invokevirtual #200 // Method me/leo/dver/Js.set:(
Lme/leo/dver/Js;)Lme/leo/dver/Js;
250: invokevirtual #174 // Method me/leo/dver/Js.$plus
:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
253: invokespecial #203 // Method me/leo/dver/JsHttp."
<init>":(Ljava/lang/String;Lme/leo/dver/Js;Lme/leo/dver/Js;Lme/leo/dver/Js;)
V
256: invokevirtual #174 // Method me/leo/dver/Js.$plus
:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
259: invokevirtual #207 // Method me/leo/dver/Js.cond:
(Lme/leo/dver/Js;Lme/leo/dver/Js;)Lme/leo/dver/Js;
262: aload_0
263: ldc #164 // String sh_in
265: invokevirtual #209 // Method literalTab:(Ljava/la
ng/String;)Lme/leo/dver/Js;
268: invokevirtual #174 // Method me/leo/dver/Js.$plus
:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
271: ldc #211 // String sh_cmd
273: ldc #213 // String ev
275: invokevirtual #143 // Method me/leo/dver/Js.asFun
:(Ljava/lang/String;Ljava/lang/String;)Lme/leo/dver/Js;
278: invokevirtual #146 // Method me/leo/dver/Js.code:
()Ljava/lang/String;
281: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
284: new #178 // class me/leo/dver/JsHttp
287: dup
288: ldc #180 // String POST
290: new #182 // class me/leo/dver/JsLiteral
293: dup
294: new #77 // class scala/collection/muta
ble/StringBuilder
297: dup
298: invokespecial #81 // Method scala/collection/mut
able/StringBuilder."<init>":()V
301: ldc #215 // String /w/
303: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
306: aload_0
307: getfield #186 // Field me$leo$dver$Shell$$pa
th:Ljava/lang/String;
310: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
313: ldc #217 // String /box.ctl
315: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
318: invokevirtual #189 // Method scala/collection/mut
able/StringBuilder.toString:()Ljava/lang/String;
321: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
324: new #182 // class me/leo/dver/JsLiteral
327: dup
328: ldc #219 // String k
330: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
333: new #127 // class me/leo/dver/Js
336: dup
337: ldc #221 // String window.location.relo
ad(false);
339: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
342: invokespecial #203 // Method me/leo/dver/JsHttp."
<init>":(Ljava/lang/String;Lme/leo/dver/Js;Lme/leo/dver/Js;Lme/leo/dver/Js;)
V
345: astore 4
347: ldc #223 // String sh_term
349: astore 5
351: aload 4
353: invokevirtual #224 // Method me/leo/dver/JsHttp.a
sFun$default$2:()Ljava/lang/String;
356: astore 6
358: aload 4
360: aload 5
362: aload 6
364: invokevirtual #225 // Method me/leo/dver/JsHttp.a
sFun:(Ljava/lang/String;Ljava/lang/String;)Lme/leo/dver/Js;
367: invokevirtual #146 // Method me/leo/dver/Js.code:
()Ljava/lang/String;
370: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
373: new #178 // class me/leo/dver/JsHttp
376: dup
377: ldc #180 // String POST
379: new #182 // class me/leo/dver/JsLiteral
382: dup
383: new #77 // class scala/collection/muta
ble/StringBuilder
386: dup
387: invokespecial #81 // Method scala/collection/mut
able/StringBuilder."<init>":()V
390: ldc #215 // String /w/
392: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
395: aload_0
396: getfield #186 // Field me$leo$dver$Shell$$pa
th:Ljava/lang/String;
399: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
402: ldc #217 // String /box.ctl
404: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
407: invokevirtual #189 // Method scala/collection/mut
able/StringBuilder.toString:()Ljava/lang/String;
410: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
413: new #182 // class me/leo/dver/JsLiteral
416: dup
417: ldc #227 // String c
419: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
422: new #127 // class me/leo/dver/Js
425: dup
426: invokespecial #128 // Method me/leo/dver/Js."<ini
t>":()V
429: invokespecial #203 // Method me/leo/dver/JsHttp."
<init>":(Ljava/lang/String;Lme/leo/dver/Js;Lme/leo/dver/Js;Lme/leo/dver/Js;)
V
432: astore 7
434: ldc #229 // String sh_cls
436: astore 8
438: aload 7
440: invokevirtual #224 // Method me/leo/dver/JsHttp.a
sFun$default$2:()Ljava/lang/String;
443: astore 9
445: aload 7
447: aload 8
449: aload 9
451: invokevirtual #225 // Method me/leo/dver/JsHttp.a
sFun:(Ljava/lang/String;Ljava/lang/String;)Lme/leo/dver/Js;
454: invokevirtual #146 // Method me/leo/dver/Js.code:
()Ljava/lang/String;
457: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
460: new #157 // class me/leo/dver/JsVar
463: dup
464: ldc #231 // String day
466: invokespecial #160 // Method me/leo/dver/JsVar."<
init>":(Ljava/lang/String;)V
469: new #127 // class me/leo/dver/Js
472: dup
473: ldc #233 // String new Date()
475: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
478: invokevirtual #169 // Method me/leo/dver/JsVar.se
t:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
481: new #178 // class me/leo/dver/JsHttp
484: dup
485: ldc #180 // String POST
487: new #182 // class me/leo/dver/JsLiteral
490: dup
491: ldc #184 // String /sh/
493: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
496: new #182 // class me/leo/dver/JsLiteral
499: dup
500: ldc #235 // String sudo date -s
502: invokespecial #190 // Method me/leo/dver/JsLitera
l."<init>":(Ljava/lang/String;)V
505: ldc #237 // String day.getFullYear()
507: invokevirtual #239 // Method me/leo/dver/JsLitera
l.append:(Ljava/lang/String;)Lme/leo/dver/Js;
510: ldc #241 // String -
512: invokevirtual #244 // Method me/leo/dver/Js.liter
al:(Ljava/lang/String;)Lme/leo/dver/Js;
515: ldc #246 // String (day.getMonth()+1)
517: invokevirtual #247 // Method me/leo/dver/Js.appen
d:(Ljava/lang/String;)Lme/leo/dver/Js;
520: ldc #241 // String -
522: invokevirtual #244 // Method me/leo/dver/Js.liter
al:(Ljava/lang/String;)Lme/leo/dver/Js;
525: ldc #249 // String day.getDate()
527: invokevirtual #247 // Method me/leo/dver/Js.appen
d:(Ljava/lang/String;)Lme/leo/dver/Js;
530: ldc #251 // String \\
532: invokevirtual #244 // Method me/leo/dver/Js.liter
al:(Ljava/lang/String;)Lme/leo/dver/Js;
535: ldc #253 // String day.getHours()
537: invokevirtual #247 // Method me/leo/dver/Js.appen
d:(Ljava/lang/String;)Lme/leo/dver/Js;
540: ldc #255 // String :
542: invokevirtual #244 // Method me/leo/dver/Js.liter
al:(Ljava/lang/String;)Lme/leo/dver/Js;
545: ldc_w #257 // String day.getMinutes()
548: invokevirtual #247 // Method me/leo/dver/Js.appen
d:(Ljava/lang/String;)Lme/leo/dver/Js;
551: new #127 // class me/leo/dver/Js
554: dup
555: ldc_w #259 // String alert(\"ok\");
558: invokespecial #155 // Method me/leo/dver/Js."<ini
t>":(Ljava/lang/String;)V
561: invokespecial #203 // Method me/leo/dver/JsHttp."
<init>":(Ljava/lang/String;Lme/leo/dver/Js;Lme/leo/dver/Js;Lme/leo/dver/Js;)
V
564: invokevirtual #174 // Method me/leo/dver/Js.$plus
:(Lme/leo/dver/Js;)Lme/leo/dver/Js;
567: astore 10
569: ldc_w #261 // String sh_date
572: astore 11
574: aload 10
576: invokevirtual #140 // Method me/leo/dver/Js.asFun
$default$2:()Ljava/lang/String;
579: astore 12
581: aload 10
583: aload 11
585: aload 12
587: invokevirtual #143 // Method me/leo/dver/Js.asFun
:(Ljava/lang/String;Ljava/lang/String;)Lme/leo/dver/Js;
590: invokevirtual #146 // Method me/leo/dver/Js.code:
()Ljava/lang/String;
593: invokevirtual #150 // Method scala/collection/mut
able/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/Stri
ngBuilder;
596: invokevirtual #189 // Method scala/collection/mut
able/StringBuilder.toString:()Ljava/lang/String;
599: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 600 0 this Lme/leo/dver/Shell;
80 14 1 qual$5 Lme/leo/dver/Js;
83 11 2 x$9 Ljava/lang/String;
88 6 3 x$10 Ljava/lang/String;
347 20 4 qual$6 Lme/leo/dver/JsHttp;
351 16 5 x$11 Ljava/lang/String;
358 9 6 x$12 Ljava/lang/String;
434 20 7 qual$7 Lme/leo/dver/JsHttp;
438 16 8 x$13 Ljava/lang/String;
445 9 9 x$14 Ljava/lang/String;
569 21 10 qual$8 Lme/leo/dver/Js;
574 16 11 x$15 Ljava/lang/String;
581 9 12 x$16 Ljava/lang/String;
LineNumberTable:
line 326: 0
line 303: 7
line 311: 56
line 312: 100
line 313: 116
line 314: 137
line 313: 158
line 315: 161
line 314: 170
line 316: 173
line 317: 208
line 318: 220
line 319: 229
line 318: 250
line 316: 253
line 315: 256
line 312: 259
line 320: 262
line 321: 284
line 322: 324
line 321: 342
line 323: 347
line 321: 351
line 323: 353
line 321: 358
line 323: 360
line 324: 373
line 325: 413
line 324: 429
line 326: 434
line 324: 438
line 326: 440
line 324: 445
line 326: 447
line 327: 460
line 328: 481
line 329: 496
line 330: 505
line 331: 510
line 332: 515
line 333: 520
line 334: 525
line 335: 530
line 336: 535
line 337: 540
line 338: 545
line 339: 551
line 328: 561
line 327: 564
line 340: 569
line 327: 574
line 340: 576
line 327: 581
line 340: 583
line 326: 596
// [...]
}
SourceFile: "html.scala"
InnerClasses:
public #110; //class me/leo/dver/Shell$$anonfun$1
public #130; //class me/leo/dver/Shell$$anonfun$2
public static #375= #312 of #374; //ArrowAssoc$=class scala/Predef$Arro
wAssoc$ of class scala/Predef
public #356; //class me/leo/dver/Shell$$anonfun$tags$3
public #359; //class me/leo/dver/Shell$$anonfun$tags$4
RuntimeVisibleAnnotations:
0: #8(#9=s#10)
Error: unknown attribute
ScalaSig: length = 0x3
05 00 00
Kto kiedykolwiek bawił się w reverse engineering ten wie ja to wygląda dla natywnych binarek (tu ARM/Linux):
$ objdump -d box
box: file format elf32-littlearm
Disassembly of section .init:
0000079c <_init>:
79c: e92d4008 push {r3, lr}
7a0: eb00006c bl 958 <call_weak_fn>
7a4: e8bd8008 pop {r3, pc}
Disassembly of section .plt:
000007a8 <.plt>:
7a8: e52de004 push {lr} ; (str lr, [sp, #-4]!)
7ac: e59fe004 ldr lr, [pc, #4] ; 7b8 <.plt+0x10>
7b0: e08fe00e add lr, pc, lr
7b4: e5bef008 ldr pc, [lr, #8]!
7b8: 00011848 .word 0x00011848
000007bc <__cxa_finalize@plt>:
7bc: e28fc600 add ip, pc, #0, 12
7c0: e28cca11 add ip, ip, #69632 ; 0x11000
7c4: e5bcf848 ldr pc, [ip, #2120]! ; 0x848
000007c8 <read@plt>:
7c8: e28fc600 add ip, pc, #0, 12
7cc: e28cca11 add ip, ip, #69632 ; 0x11000
7d0: e5bcf840 ldr pc, [ip, #2112]! ; 0x840
000007d4 <execvp@plt>:
7d4: e28fc600 add ip, pc, #0, 12
7d8: e28cca11 add ip, ip, #69632 ; 0x11000
7dc: e5bcf838 ldr pc, [ip, #2104]! ; 0x838
000007e0 <lseek@plt>:
7e0: e28fc600 add ip, pc, #0, 12
7e4: e28cca11 add ip, ip, #69632 ; 0x11000
7e8: e5bcf830 ldr pc, [ip, #2096]! ; 0x830
000007ec <__stack_chk_fail@plt>:
7ec: e28fc600 add ip, pc, #0, 12
7f0: e28cca11 add ip, ip, #69632 ; 0x11000
7f4: e5bcf828 ldr pc, [ip, #2088]! ; 0x828
// [...]
Disassembly of section .text:
00000900 <_start>:
900: e3a0b000 mov fp, #0
904: e3a0e000 mov lr, #0
908: e49d1004 pop {r1} ; (ldr r1, [sp], #4)
90c: e1a0200d mov r2, sp
910: e52d2004 push {r2} ; (str r2, [sp, #-4]
!)
914: e52d0004 push {r0} ; (str r0, [sp, #-4]
!)
918: e59fa028 ldr sl, [pc, #40] ; 948 <_start+0x48>
91c: e28f3024 add r3, pc, #36 ; 0x24
920: e08aa003 add sl, sl, r3
924: e59fc020 ldr ip, [pc, #32] ; 94c <_start+0x4c>
928: e79ac00c ldr ip, [sl, ip]
92c: e52dc004 push {ip} ; (str ip, [sp, #-4]
!)
930: e59f3018 ldr r3, [pc, #24] ; 950 <_start+0x50>
934: e79a3003 ldr r3, [sl, r3]
938: e59f0014 ldr r0, [pc, #20] ; 954 <_start+0x54>
93c: e79a0000 ldr r0, [sl, r0]
940: ebffffbe bl 840 <__libc_start_main@plt>
944: ebffffe4 bl 8dc <abort@plt>
948: 000116b8 .word 0x000116b8
94c: 00000078 .word 0x00000078
950: 00000090 .word 0x00000090
954: 00000094 .word 0x00000094
// [...]
e04: e50b3010 str r3, [fp, #-16]
e08: ebfffeaa bl 8b8 <fork@plt>
e0c: e1a03000 mov r3, r0
e10: e3530000 cmp r3, #0
e14: 1a000105 bne 1230 <handle_streams+0x454>
e18: e24b3048 sub r3, fp, #72 ; 0x48
e1c: e3a02038 mov r2, #56 ; 0x38
e20: e3a01000 mov r1, #0
e24: e1a00003 mov r0, r3
e28: ebfffe96 bl 888 <memset@plt>
e2c: e59b3010 ldr r3, [fp, #16]
e30: e3a02000 mov r2, #0
e34: e3a01002 mov r1, #2
e38: e1a00003 mov r0, r3
e3c: ebfffe85 bl 858 <open@plt>
e40: e1a03000 mov r3, r0
e44: e50b3048 str r3, [fp, #-72] ; 0xffffffb8
e48: e3a03001 mov r3, #1
e4c: e14b34b4 strh r3, [fp, #-68] ; 0xffffffbc
e50: e59b3004 ldr r3, [fp, #4]
e54: e3a02000 mov r2, #0
e58: e3a01002 mov r1, #2
e5c: e1a00003 mov r0, r3
e60: ebfffe7c bl 858 <open@plt>
e64: e1a03000 mov r3, r0
e68: e50b3040 str r3, [fp, #-64] ; 0xffffffc0
e6c: e3a03001 mov r3, #1
e70: e14b33bc strh r3, [fp, #-60] ; 0xffffffc4
e74: e51b3078 ldr r3, [fp, #-120] ; 0xffffff88
e78: e50b3038 str r3, [fp, #-56] ; 0xffffffc8
e7c: e3a03001 mov r3, #1
// [...]
126c: 00011500 .word 0x00011500
1270: 000114ac .word 0x000114ac
1274: 000114b4 .word 0x000114b4
1278: 00011340 .word 0x00011340
127c: 00011310 .word 0x00011310
1280: 00000660 .word 0x00000660
1284: 00000648 .word 0x00000648
00001288 <execute>:
1288: e92d4810 push {r4, fp, lr}
128c: e28db008 add fp, sp, #8
1290: e24dd014 sub sp, sp, #20
1294: e50b0018 str r0, [fp, #-24] ; 0xffffffe8
1298: e59f404c ldr r4, [pc, #76] ; 12ec <execute+0x64
>
129c: e08f4004 add r4, pc, r4
12a0: e51b3018 ldr r3, [fp, #-24] ; 0xffffffe8
12a4: e50b3010 str r3, [fp, #-16]
12a8: e51b3010 ldr r3, [fp, #-16]
12ac: e5933000 ldr r3, [r3]
// [...] hen hen...
I to był kod z symbolami dla debuggera, a w x86 jest jeszcze ciężej. Myślę, że tego nie trzeba za bardzo tłumaczyć. Chyba każdy, kto miał kiedyś aspiracje w kierunku inżynierii wstecznej i pracy z kodem maszynowym doceni.
JVM daje jednolity interfejs do komunikacji między językami. Dzięki stosunkowo wysokopoziomowemu zestawowi rozkazów, różnice w niskopoziomowej implementacji poszczególnych języków nie gra dużej roli. Co więcej, tworzy świetną podstawę do tworzenia nowych języków. Oczywiście nie jest to siła lispowych makr, ale jest całkiem dobrze. Szczęśliwie, JVM pozwala na znacznie więcej niż toporna Java. Dzięki temu możliwe jest zaimplementowanie nawet wysoce ekspresywnych języków jak Scala, Haskell czy Common Lisp.
To zasadniczo tyczy każdego kodu bajtowego, ale warto wspomnieć. Kod bajtowy może służyć jako półprodukt w procesie kompilacji. Dzięki temu łatwiej zaimplementować nowy język. LLVM stosuje takie rozwiązanie, choć z tego co wiem, jego kod pośredni jest znacznie bardziej niskopoziomowy. Istnieją również konwertery kodu JVM na kod maszynowy danej architektury.
Jednak kompilacja to nie jedyne możliwe zastosowanie. Na poziomie kodu bajtowego można spokojnie przeprowadzić całą masę zadań wykonywanych na kodzie źródłowym, choćby statyczną analizę. Dzięki takiemu kodowi pośredniemu, możemy napisać jedno narzędzie, które będzie współpracować ze wszystkimi językami kompilowanymi do tego kodu bajtowego. W tym względzie JVM jest o tyle przyjemny, że ma wysokopoziomowe rozkazy, dzięki czemu pisanie takich narzędzi powinno być stosunkowo łatwe i przyjemne. Mogłoby również obejmować takie zagadnienia jak właściwy podział kodu na klasy. Właściwie, możliwe że napisanie takiego narzędzia byłoby łatwiejsze niż dla zwykłego języka programowania wysokiego poziomu, bo nie wymagałoby obsługi lukru składniowego.
Pewnie słyszeliście ten kawał?
Oczywiście dużo się zmieniło i tak dalej. Jest JIT, który sprawia, że większość użytkowego oprogramowania działa zasadniczo tak dobrze jak w językach kompilowanych (choć uruchomienie maszyny wirtualnej zwykle trwa dłużej). Z drugiej strony w JVMowym ekosystemie narzędzi takich jak SBT, Gradle czy Maven oraz typowych frameworków jak Spring wszystko się strasznie długo uruchamia i nawet na dobrym sprzęcie może irytować, ile to czekać trzeba. Ze słabym sprzętem jest ciężej. Chociaż, wygenerowanie prostego kodu w Javie (Common Lisp), kompilacja i uruchomienie, tylko 2,5s na Celeronie M 1,73 GHz (1 rdzeń) z 512MB RAMu. Podobny wynik na Raspberry Pi. Całkiem nieźle. :) Zbudowanie prostej aplikacji na Androida też się zamyka w niewiele dłuższym czasie, pod warunkiem, że nie użyjemy żadnej krowy pokroju Maven lub Gradle, ani ciężkich paczek z bajerami od Google (ale to już na i3).
Odnoszę czasem wrażenie, że powolne działanie w ekosystemie Javy są cechą pożądaną, więcej czasu na kawę, dłuższe projekty. Osobiście tego nie lubię.
JVM to popularne środowisko uruchomieniowe, które jest dostępne na bardzo wielu architekturach (niektóre oferują nawet wsparcie sprzętowe) i jest domyślnym środowiskiem na Androidzie. Poza językami stworzonymi specjalnie pod nie, istnieje również szereg implementacji innych popularnych języków posiadające implementację dla JVM.
Kod bajtowy, którego używa jest prosty i wysokopoziomowy, dzięki czemu jest łatwiejszy w ewentualnej analizie, ale też może być wartościowy również jako uniwersalna notacja programów komputerowych, jako kod pośredni dla kompilatora (jak w LLVM) lub narzędzi programistycznych, takich jak statyczne analizatory kodu. Jest podobny do CIL Microsoftu, assemblera dla .NET, który też jest obiektowy.