JVM bez ściemy

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.

tematy:

  1. Ważniejsze formaty plików JVM: .class, .jar, .dex
  2. Format pliku klasy i assembler
  3. Łączenie języków i tworzenie nowych znacznie łatwiejsze
  4. Kod bajtowy jako rodzaj kodu pośredniego
  5. Wydajność
  6. Podsumowując

Ważniejsze formaty plików JVM: .class, .jar, .dex

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.

Format pliku klasy i assembler

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.

Łączenie języków i tworzenie nowych znacznie łatwiejsze

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.

Kod bajtowy jako rodzaj kodu pośredniego

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.

Wydajność

Pewnie słyszeliście ten kawał?

- Puk puk
- Kto tam?

Po 5 minutach:

- Java....

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ę.

Podsumowując

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.

Komentarze? Pytania? Sugestie? Współpraca? Napisz.