Rust製JVMを

作成した話

ついでにWasmでブラウザで動かす

誰?

  • 仕様書を元にした開発を一回やってみたい

  • Javaの仕様書は結構良いらしい(真偽不明)

  • Stack Machineの学習ついでにやろう

  • インスタンス、配列など色々参考にしよう

背景

  というわけで

なんか流行ってるし、やってみよう

ついでにブラウザで動かそう

成果物

rust-jvm

Rust製JVM。Web Assembly(wasm)としてブラウザで動かすこともできる

url:    https://github.com/rchaser53/rust-jvm

  1. ローカルでのRust製JVMの作成(※1)

  2. ブラウザ上でWasmとして実行

今回やったこと

  • 実装すること

  • 実際に覗いてみる

  • ビルトインのクラスについて

1. ローカルでのRust製JVMの作成

何をすれば良いの?

実装すること

  • ​classファイルを引数にした実行可能ファイルを作成する
  • classファイルは本当に仕様書の通りのフォーマット
  • シンプルにそれを解釈して実行できるようにすれば良い

本当にそれだけで良いの?

実際に覗いてみる

  • 0xEDというバイナリ編集ソフトを使う
  • シンプルなHelloWorldを出力するClassファイルを作成
  • ちょっと先端だけ本当にmagicなのか確認してみる
  • magicは「0xCAFEBABE」という数値
// これをjavacで実行してTest.classを作る
public class Test {
  public static void main(String[] args) {
    System.out.println("HelloWorld");
  }
}

HelloWorld出力するコード

本当にそれだけで良いの?②

  • 先端はmagic、0xCAFEBABE」のはず…

実際に覗いてみる

本当にそれだけで良いの?③

  • 先端はmagic、0xCAFEBABE」のはず…

本当にフォーマット通り

実際に覗いてみる

話はそんなに簡単に進まない

ビルトインのクラスについて

  • 普段使っているビルトインのクラスは仕様書には存在しない
  • System.outも当然なくprintlnが実行できない
  • HelloWorldができない!!

話はそんなに簡単に進まない②

ビルトインのクラスについて

  • JDKの方にソースコードは存在する
  • またjavapを用いても確認できる
  • javapはJava クラスファイル逆アセンブラの略。便利
  • よし手段は見つかった

HelloWorldが遠ざかる…

ビルトインのクラスについて

  • 実際にはSystem.outではなくjava/io/PrintStreamになる
  • 実装量が非常に多い。必要箇所だけでも厳しい
  • 本気でリビルドしない限り代替手段を取った方が無難
rchaser53noMacBook-Pro:rj rchaser53$ javap -v java/io/PrintStream 
Classfile jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/rt.jar!/java/io/PrintStream.class
  Last modified 2017/07/21; size 9048 bytes
  MD5 checksum f62b2b102171bb9cd2cefa5efcf0e487
  Compiled from "PrintStream.java"
public class java.io.PrintStream extends java.io.FilterOutputStream implements java.lang.Appendable,java.io.Closeable
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
    #1 = Class              #165          // java/lang/NullPointerException
    #2 = Methodref          #1.#166       // java/lang/NullPointerException."<init>":(Ljava/lang/String;)V
    #3 = String             #167          // charsetName
    #4 = Methodref          #40.#168      // java/io/PrintStream.requireNonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
    #5 = Methodref          #169.#170     // java/nio/charset/Charset.forName:(Ljava/lang/String;)Ljava/nio/charset/Charset;
    #6 = Class              #171          // java/nio/charset/IllegalCharsetNameException
    #7 = Class              #172          // java/nio/charset/UnsupportedCharsetException
    #8 = Class              #173          // java/io/UnsupportedEncodingException
    #9 = Methodref          #8.#166       // java/io/UnsupportedEncodingException."<init>":(Ljava/lang/String;)V
   #10 = Methodref          #87.#174      // java/io/FilterOutputStream."<init>":(Ljava/io/OutputStream;)V
   #11 = Fieldref           #40.#175      // java/io/PrintStream.trouble:Z
   #12 = Fieldref           #40.#176      // java/io/PrintStream.closing:Z
   #13 = Fieldref           #40.#177      // java/io/PrintStream.autoFlush:Z
   #14 = Class              #178          // java/io/OutputStreamWriter
   #15 = Methodref          #14.#174      // java/io/OutputStreamWriter."<init>":(Ljava/io/OutputStream;)V
   #16 = Fieldref           #40.#179      // java/io/PrintStream.charOut:Ljava/io/OutputStreamWriter;
   #17 = Class              #180          // java/io/BufferedWriter
   #18 = Methodref          #17.#181      // java/io/BufferedWriter."<init>":(Ljava/io/Writer;)V
   #19 = Fieldref           #40.#182      // java/io/PrintStream.textOut:Ljava/io/BufferedWriter;
   #20 = Methodref          #14.#183      // java/io/OutputStreamWriter."<init>":(Ljava/io/OutputStream;Ljava/nio/charset/Charset;)V
   #21 = Methodref          #40.#184      // java/io/PrintStream."<init>":(ZLjava/io/OutputStream;Ljava/nio/charset/Charset;)V
   #22 = Methodref          #40.#185      // java/io/PrintStream."<init>":(Ljava/io/OutputStream;Z)V
   #23 = String             #186          // Null output stream
   #24 = Class              #187          // java/io/OutputStream
   #25 = Methodref          #40.#188      // java/io/PrintStream."<init>":(ZLjava/io/OutputStream;)V
   #26 = Methodref          #40.#189      // java/io/PrintStream.toCharset:(Ljava/lang/String;)Ljava/nio/charset/Charset;
   #27 = Class              #190          // java/io/FileOutputStream
   #28 = Methodref          #27.#166      // java/io/FileOutputStream."<init>":(Ljava/lang/String;)V
   #29 = Methodref          #40.#191      // java/io/PrintStream."<init>":(ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V
   #30 = Methodref          #27.#192      // java/io/FileOutputStream."<init>":(Ljava/io/File;)V
   #31 = Fieldref           #40.#193      // java/io/PrintStream.out:Ljava/io/OutputStream;
   #32 = Class              #194          // java/io/IOException
   #33 = String             #195          // Stream closed
   #34 = Methodref          #32.#166      // java/io/IOException."<init>":(Ljava/lang/String;)V
   #35 = Methodref          #40.#196      // java/io/PrintStream.ensureOpen:()V
   #36 = Methodref          #24.#197      // java/io/OutputStream.flush:()V
   #37 = Methodref          #17.#198      // java/io/BufferedWriter.close:()V
   #38 = Methodref          #24.#198      // java/io/OutputStream.close:()V
   #39 = Methodref          #40.#197      // java/io/PrintStream.flush:()V
   #40 = Class              #199          // java/io/PrintStream
   #41 = Methodref          #40.#200      // java/io/PrintStream.checkError:()Z
   #42 = Methodref          #24.#201      // java/io/OutputStream.write:(I)V
   #43 = Class              #202          // java/io/InterruptedIOException
   #44 = Methodref          #203.#204     // java/lang/Thread.currentThread:()Ljava/lang/Thread;
   #45 = Methodref          #203.#205     // java/lang/Thread.interrupt:()V
   #46 = Methodref          #24.#206      // java/io/OutputStream.write:([BII)V
   #47 = Methodref          #17.#207      // java/io/BufferedWriter.write:([C)V
   #48 = Methodref          #17.#208      // java/io/BufferedWriter.flushBuffer:()V
   #49 = Methodref          #14.#208      // java/io/OutputStreamWriter.flushBuffer:()V
   #50 = Methodref          #17.#209      // java/io/BufferedWriter.write:(Ljava/lang/String;)V
   #51 = Methodref          #210.#211     // java/lang/String.indexOf:(I)I
   #52 = Methodref          #17.#212      // java/io/BufferedWriter.newLine:()V
   #53 = String             #213          // true
   #54 = String             #214          // false
   #55 = Methodref          #40.#209      // java/io/PrintStream.write:(Ljava/lang/String;)V
   #56 = Methodref          #210.#215     // java/lang/String.valueOf:(C)Ljava/lang/String;
   #57 = Methodref          #210.#216     // java/lang/String.valueOf:(I)Ljava/lang/String;
   #58 = Methodref          #210.#217     // java/lang/String.valueOf:(J)Ljava/lang/String;
   #59 = Methodref          #210.#218     // java/lang/String.valueOf:(F)Ljava/lang/String;
   #60 = Methodref          #210.#219     // java/lang/String.valueOf:(D)Ljava/lang/String;
   #61 = Methodref          #40.#207      // java/io/PrintStream.write:([C)V
   #62 = String             #220          // null
   #63 = Methodref          #210.#221     // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   #64 = Methodref          #40.#212      // java/io/PrintStream.newLine:()V
   #65 = Methodref          #40.#222      // java/io/PrintStream.print:(Z)V
   #66 = Methodref          #40.#223      // java/io/PrintStream.print:(C)V
   #67 = Methodref          #40.#224      // java/io/PrintStream.print:(I)V
   #68 = Methodref          #40.#225      // java/io/PrintStream.print:(J)V
   #69 = Methodref          #40.#226      // java/io/PrintStream.print:(F)V
   #70 = Methodref          #40.#227      // java/io/PrintStream.print:(D)V
   #71 = Methodref          #40.#228      // java/io/PrintStream.print:([C)V
   #72 = Methodref          #40.#229      // java/io/PrintStream.print:(Ljava/lang/String;)V
   #73 = Methodref          #40.#230      // java/io/PrintStream.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
   #74 = Methodref          #40.#231      // java/io/PrintStream.format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
   #75 = Fieldref           #40.#232      // java/io/PrintStream.formatter:Ljava/util/Formatter;
   #76 = Methodref          #78.#233      // java/util/Formatter.locale:()Ljava/util/Locale;
   #77 = Methodref          #234.#235     // java/util/Locale.getDefault:()Ljava/util/Locale;
   #78 = Class              #236          // java/util/Formatter
   #79 = Methodref          #78.#237      // java/util/Formatter."<init>":(Ljava/lang/Appendable;)V
   #80 = Methodref          #78.#238      // java/util/Formatter.format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
   #81 = Methodref          #78.#239      // java/util/Formatter."<init>":(Ljava/lang/Appendable;Ljava/util/Locale;)V
   #82 = InterfaceMethodref #240.#241     // java/lang/CharSequence.toString:()Ljava/lang/String;
   #83 = InterfaceMethodref #240.#242     // java/lang/CharSequence.subSequence:(II)Ljava/lang/CharSequence;
   #84 = Methodref          #40.#243      // java/io/PrintStream.append:(C)Ljava/io/PrintStream;
   #85 = Methodref          #40.#244      // java/io/PrintStream.append:(Ljava/lang/CharSequence;II)Ljava/io/PrintStream;
   #86 = Methodref          #40.#245      // java/io/PrintStream.append:(Ljava/lang/CharSequence;)Ljava/io/PrintStream;
   #87 = Class              #246          // java/io/FilterOutputStream
   #88 = Class              #247          // java/lang/Appendable
   #89 = Class              #248          // java/io/Closeable
   #90 = Utf8               autoFlush
   #91 = Utf8               Z
   #92 = Utf8               trouble
   #93 = Utf8               formatter
   #94 = Utf8               Ljava/util/Formatter;
   #95 = Utf8               textOut
   #96 = Utf8               Ljava/io/BufferedWriter;
   #97 = Utf8               charOut
   #98 = Utf8               Ljava/io/OutputStreamWriter;
   #99 = Utf8               closing
  #100 = Utf8               requireNonNull
  #101 = Utf8               (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
  #102 = Utf8               Code
  #103 = Utf8               LineNumberTable
  #104 = Utf8               StackMapTable
  #105 = Utf8               Signature
  #106 = Utf8               <T:Ljava/lang/Object;>(TT;Ljava/lang/String;)TT;
  #107 = Utf8               toCharset
  #108 = Utf8               (Ljava/lang/String;)Ljava/nio/charset/Charset;
  #109 = Class              #249          // java/lang/IllegalArgumentException
  #110 = Utf8               Exceptions
  #111 = Utf8               <init>
  #112 = Utf8               (ZLjava/io/OutputStream;)V
  #113 = Utf8               (ZLjava/io/OutputStream;Ljava/nio/charset/Charset;)V
  #114 = Utf8               (ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V
  #115 = Utf8               (Ljava/io/OutputStream;)V
  #116 = Utf8               (Ljava/io/OutputStream;Z)V
  #117 = Utf8               (Ljava/io/OutputStream;ZLjava/lang/String;)V
  #118 = Utf8               (Ljava/lang/String;)V
  #119 = Class              #250          // java/io/FileNotFoundException
  #120 = Utf8               (Ljava/lang/String;Ljava/lang/String;)V
  #121 = Utf8               (Ljava/io/File;)V
  #122 = Utf8               (Ljava/io/File;Ljava/lang/String;)V
  #123 = Utf8               ensureOpen
  #124 = Utf8               ()V
  #125 = Utf8               flush
  #126 = Class              #199          // java/io/PrintStream
  #127 = Class              #251          // java/lang/Object
  #128 = Class              #194          // java/io/IOException
  #129 = Class              #252          // java/lang/Throwable
  #130 = Utf8               close
  #131 = Utf8               checkError
  #132 = Utf8               ()Z
  #133 = Utf8               setError
  #134 = Utf8               clearError
  #135 = Utf8               write
  #136 = Utf8               (I)V
  #137 = Class              #202          // java/io/InterruptedIOException
  #138 = Utf8               ([BII)V
  #139 = Utf8               ([C)V
  #140 = Utf8               newLine
  #141 = Utf8               print
  #142 = Utf8               (Z)V
  #143 = Class              #253          // java/lang/String
  #144 = Utf8               (C)V
  #145 = Utf8               (J)V
  #146 = Utf8               (F)V
  #147 = Utf8               (D)V
  #148 = Utf8               (Ljava/lang/Object;)V
  #149 = Utf8               println
  #150 = Class              #254          // "[C"
  #151 = Utf8               printf
  #152 = Utf8               (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
  #153 = Utf8               (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
  #154 = Utf8               format
  #155 = Utf8               append
  #156 = Utf8               (Ljava/lang/CharSequence;)Ljava/io/PrintStream;
  #157 = Utf8               (Ljava/lang/CharSequence;II)Ljava/io/PrintStream;
  #158 = Class              #255          // java/lang/CharSequence
  #159 = Utf8               (C)Ljava/io/PrintStream;
  #160 = Utf8               (C)Ljava/lang/Appendable;
  #161 = Utf8               (Ljava/lang/CharSequence;II)Ljava/lang/Appendable;
  #162 = Utf8               (Ljava/lang/CharSequence;)Ljava/lang/Appendable;
  #163 = Utf8               SourceFile
  #164 = Utf8               PrintStream.java
  #165 = Utf8               java/lang/NullPointerException
  #166 = NameAndType        #111:#118     // "<init>":(Ljava/lang/String;)V
  #167 = Utf8               charsetName
  #168 = NameAndType        #100:#101     // requireNonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
  #169 = Class              #256          // java/nio/charset/Charset
  #170 = NameAndType        #257:#108     // forName:(Ljava/lang/String;)Ljava/nio/charset/Charset;
  #171 = Utf8               java/nio/charset/IllegalCharsetNameException
  #172 = Utf8               java/nio/charset/UnsupportedCharsetException
  #173 = Utf8               java/io/UnsupportedEncodingException
  #174 = NameAndType        #111:#115     // "<init>":(Ljava/io/OutputStream;)V
  #175 = NameAndType        #92:#91       // trouble:Z
  #176 = NameAndType        #99:#91       // closing:Z
  #177 = NameAndType        #90:#91       // autoFlush:Z
  #178 = Utf8               java/io/OutputStreamWriter
  #179 = NameAndType        #97:#98       // charOut:Ljava/io/OutputStreamWriter;
  #180 = Utf8               java/io/BufferedWriter
  #181 = NameAndType        #111:#258     // "<init>":(Ljava/io/Writer;)V
  #182 = NameAndType        #95:#96       // textOut:Ljava/io/BufferedWriter;
  #183 = NameAndType        #111:#259     // "<init>":(Ljava/io/OutputStream;Ljava/nio/charset/Charset;)V
  #184 = NameAndType        #111:#113     // "<init>":(ZLjava/io/OutputStream;Ljava/nio/charset/Charset;)V
  #185 = NameAndType        #111:#116     // "<init>":(Ljava/io/OutputStream;Z)V
  #186 = Utf8               Null output stream
  #187 = Utf8               java/io/OutputStream
  #188 = NameAndType        #111:#112     // "<init>":(ZLjava/io/OutputStream;)V
  #189 = NameAndType        #107:#108     // toCharset:(Ljava/lang/String;)Ljava/nio/charset/Charset;
  #190 = Utf8               java/io/FileOutputStream
  #191 = NameAndType        #111:#114     // "<init>":(ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V
  #192 = NameAndType        #111:#121     // "<init>":(Ljava/io/File;)V
  #193 = NameAndType        #260:#261     // out:Ljava/io/OutputStream;
  #194 = Utf8               java/io/IOException
  #195 = Utf8               Stream closed
  #196 = NameAndType        #123:#124     // ensureOpen:()V
  #197 = NameAndType        #125:#124     // flush:()V
  #198 = NameAndType        #130:#124     // close:()V
  #199 = Utf8               java/io/PrintStream
  #200 = NameAndType        #131:#132     // checkError:()Z
  #201 = NameAndType        #135:#136     // write:(I)V
  #202 = Utf8               java/io/InterruptedIOException
  #203 = Class              #262          // java/lang/Thread
  #204 = NameAndType        #263:#264     // currentThread:()Ljava/lang/Thread;
  #205 = NameAndType        #265:#124     // interrupt:()V
  #206 = NameAndType        #135:#138     // write:([BII)V
  #207 = NameAndType        #135:#139     // write:([C)V
  #208 = NameAndType        #266:#124     // flushBuffer:()V
  #209 = NameAndType        #135:#118     // write:(Ljava/lang/String;)V
  #210 = Class              #253          // java/lang/String
  #211 = NameAndType        #267:#268     // indexOf:(I)I
  #212 = NameAndType        #140:#124     // newLine:()V
  #213 = Utf8               true
  #214 = Utf8               false
  #215 = NameAndType        #269:#270     // valueOf:(C)Ljava/lang/String;
  #216 = NameAndType        #269:#271     // valueOf:(I)Ljava/lang/String;
  #217 = NameAndType        #269:#272     // valueOf:(J)Ljava/lang/String;
  #218 = NameAndType        #269:#273     // valueOf:(F)Ljava/lang/String;
  #219 = NameAndType        #269:#274     // valueOf:(D)Ljava/lang/String;
  #220 = Utf8               null
  #221 = NameAndType        #269:#275     // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #222 = NameAndType        #141:#142     // print:(Z)V
  #223 = NameAndType        #141:#144     // print:(C)V
  #224 = NameAndType        #141:#136     // print:(I)V
  #225 = NameAndType        #141:#145     // print:(J)V
  #226 = NameAndType        #141:#146     // print:(F)V
  #227 = NameAndType        #141:#147     // print:(D)V
  #228 = NameAndType        #141:#139     // print:([C)V
  #229 = NameAndType        #141:#118     // print:(Ljava/lang/String;)V
  #230 = NameAndType        #154:#152     // format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
  #231 = NameAndType        #154:#153     // format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
  #232 = NameAndType        #93:#94       // formatter:Ljava/util/Formatter;
  #233 = NameAndType        #276:#277     // locale:()Ljava/util/Locale;
  #234 = Class              #278          // java/util/Locale
  #235 = NameAndType        #279:#277     // getDefault:()Ljava/util/Locale;
  #236 = Utf8               java/util/Formatter
  #237 = NameAndType        #111:#280     // "<init>":(Ljava/lang/Appendable;)V
  #238 = NameAndType        #154:#281     // format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
  #239 = NameAndType        #111:#282     // "<init>":(Ljava/lang/Appendable;Ljava/util/Locale;)V
  #240 = Class              #255          // java/lang/CharSequence
  #241 = NameAndType        #283:#284     // toString:()Ljava/lang/String;
  #242 = NameAndType        #285:#286     // subSequence:(II)Ljava/lang/CharSequence;
  #243 = NameAndType        #155:#159     // append:(C)Ljava/io/PrintStream;
  #244 = NameAndType        #155:#157     // append:(Ljava/lang/CharSequence;II)Ljava/io/PrintStream;
  #245 = NameAndType        #155:#156     // append:(Ljava/lang/CharSequence;)Ljava/io/PrintStream;
  #246 = Utf8               java/io/FilterOutputStream
  #247 = Utf8               java/lang/Appendable
  #248 = Utf8               java/io/Closeable
  #249 = Utf8               java/lang/IllegalArgumentException
  #250 = Utf8               java/io/FileNotFoundException
  #251 = Utf8               java/lang/Object
  #252 = Utf8               java/lang/Throwable
  #253 = Utf8               java/lang/String
  #254 = Utf8               [C
  #255 = Utf8               java/lang/CharSequence
  #256 = Utf8               java/nio/charset/Charset
  #257 = Utf8               forName
  #258 = Utf8               (Ljava/io/Writer;)V
  #259 = Utf8               (Ljava/io/OutputStream;Ljava/nio/charset/Charset;)V
  #260 = Utf8               out
  #261 = Utf8               Ljava/io/OutputStream;
  #262 = Utf8               java/lang/Thread
  #263 = Utf8               currentThread
  #264 = Utf8               ()Ljava/lang/Thread;
  #265 = Utf8               interrupt
  #266 = Utf8               flushBuffer
  #267 = Utf8               indexOf
  #268 = Utf8               (I)I
  #269 = Utf8               valueOf
  #270 = Utf8               (C)Ljava/lang/String;
  #271 = Utf8               (I)Ljava/lang/String;
  #272 = Utf8               (J)Ljava/lang/String;
  #273 = Utf8               (F)Ljava/lang/String;
  #274 = Utf8               (D)Ljava/lang/String;
  #275 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
  #276 = Utf8               locale
  #277 = Utf8               ()Ljava/util/Locale;
  #278 = Utf8               java/util/Locale
  #279 = Utf8               getDefault
  #280 = Utf8               (Ljava/lang/Appendable;)V
  #281 = Utf8               (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
  #282 = Utf8               (Ljava/lang/Appendable;Ljava/util/Locale;)V
  #283 = Utf8               toString
  #284 = Utf8               ()Ljava/lang/String;
  #285 = Utf8               subSequence
  #286 = Utf8               (II)Ljava/lang/CharSequence;
{
  public java.io.PrintStream(java.io.OutputStream);
    descriptor: (Ljava/io/OutputStream;)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: iconst_0
         3: invokespecial #22                 // Method "<init>":(Ljava/io/OutputStream;Z)V
         6: return
      LineNumberTable:
        line 135: 0
        line 136: 6

  public java.io.PrintStream(java.io.OutputStream, boolean);
    descriptor: (Ljava/io/OutputStream;Z)V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=3, args_size=3
         0: aload_0
         1: iload_2
         2: aload_1
         3: ldc           #23                 // String Null output stream
         5: invokestatic  #4                  // Method requireNonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
         8: checkcast     #24                 // class java/io/OutputStream
        11: invokespecial #25                 // Method "<init>":(ZLjava/io/OutputStream;)V
        14: return
      LineNumberTable:
        line 151: 0
        line 152: 14

  public java.io.PrintStream(java.io.OutputStream, boolean, java.lang.String) throws java.io.UnsupportedEncodingException;
    descriptor: (Ljava/io/OutputStream;ZLjava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=4, args_size=4
         0: aload_0
         1: iload_2
         2: aload_1
         3: ldc           #23                 // String Null output stream
         5: invokestatic  #4                  // Method requireNonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
         8: checkcast     #24                 // class java/io/OutputStream
        11: aload_3
        12: invokestatic  #26                 // Method toCharset:(Ljava/lang/String;)Ljava/nio/charset/Charset;
        15: invokespecial #21                 // Method "<init>":(ZLjava/io/OutputStream;Ljava/nio/charset/Charset;)V
        18: return
      LineNumberTable:
        line 175: 0
        line 176: 5
        line 177: 12
        line 175: 15
        line 178: 18
    Exceptions:
      throws java.io.UnsupportedEncodingException

  public java.io.PrintStream(java.lang.String) throws java.io.FileNotFoundException;
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=5, locals=2, args_size=2
         0: aload_0
         1: iconst_0
         2: new           #27                 // class java/io/FileOutputStream
         5: dup
         6: aload_1
         7: invokespecial #28                 // Method java/io/FileOutputStream."<init>":(Ljava/lang/String;)V
        10: invokespecial #25                 // Method "<init>":(ZLjava/io/OutputStream;)V
        13: return
      LineNumberTable:
        line 208: 0
        line 209: 13
    Exceptions:
      throws java.io.FileNotFoundException

  public java.io.PrintStream(java.lang.String, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
    descriptor: (Ljava/lang/String;Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=6, locals=3, args_size=3
         0: aload_0
         1: iconst_0
         2: aload_2
         3: invokestatic  #26                 // Method toCharset:(Ljava/lang/String;)Ljava/nio/charset/Charset;
         6: new           #27                 // class java/io/FileOutputStream
         9: dup
        10: aload_1
        11: invokespecial #28                 // Method java/io/FileOutputStream."<init>":(Ljava/lang/String;)V
        14: invokespecial #29                 // Method "<init>":(ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V
        17: return
      LineNumberTable:
        line 248: 0
        line 249: 17
    Exceptions:
      throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException

  public java.io.PrintStream(java.io.File) throws java.io.FileNotFoundException;
    descriptor: (Ljava/io/File;)V
    flags: ACC_PUBLIC
    Code:
      stack=5, locals=2, args_size=2
         0: aload_0
         1: iconst_0
         2: new           #27                 // class java/io/FileOutputStream
         5: dup
         6: aload_1
         7: invokespecial #30                 // Method java/io/FileOutputStream."<init>":(Ljava/io/File;)V
        10: invokespecial #25                 // Method "<init>":(ZLjava/io/OutputStream;)V
        13: return
      LineNumberTable:
        line 279: 0
        line 280: 13
    Exceptions:
      throws java.io.FileNotFoundException

  public java.io.PrintStream(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
    descriptor: (Ljava/io/File;Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=6, locals=3, args_size=3
         0: aload_0
         1: iconst_0
         2: aload_2
         3: invokestatic  #26                 // Method toCharset:(Ljava/lang/String;)Ljava/nio/charset/Charset;
         6: new           #27                 // class java/io/FileOutputStream
         9: dup
        10: aload_1
        11: invokespecial #30                 // Method java/io/FileOutputStream."<init>":(Ljava/io/File;)V
        14: invokespecial #29                 // Method "<init>":(ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V
        17: return
      LineNumberTable:
        line 319: 0
        line 320: 17
    Exceptions:
      throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException

  public void flush();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_0
         5: invokespecial #35                 // Method ensureOpen:()V
         8: aload_0
         9: getfield      #31                 // Field out:Ljava/io/OutputStream;
        12: invokevirtual #36                 // Method java/io/OutputStream.flush:()V
        15: goto          24
        18: astore_2
        19: aload_0
        20: iconst_1
        21: putfield      #11                 // Field trouble:Z
        24: aload_1
        25: monitorexit
        26: goto          34
        29: astore_3
        30: aload_1
        31: monitorexit
        32: aload_3
        33: athrow
        34: return
      Exception table:
         from    to  target type
             4    15    18   Class java/io/IOException
             4    26    29   any
            29    32    29   any
      LineNumberTable:
        line 335: 0
        line 337: 4
        line 338: 8
        line 342: 15
        line 340: 18
        line 341: 19
        line 343: 24
        line 344: 34
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, class java/lang/Object ]
          stack = [ class java/io/IOException ]
        frame_type = 5 /* same */
        frame_type = 68 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void close();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_0
         5: getfield      #12                 // Field closing:Z
         8: ifne          54
        11: aload_0
        12: iconst_1
        13: putfield      #12                 // Field closing:Z
        16: aload_0
        17: getfield      #19                 // Field textOut:Ljava/io/BufferedWriter;
        20: invokevirtual #37                 // Method java/io/BufferedWriter.close:()V
        23: aload_0
        24: getfield      #31                 // Field out:Ljava/io/OutputStream;
        27: invokevirtual #38                 // Method java/io/OutputStream.close:()V
        30: goto          39
        33: astore_2
        34: aload_0
        35: iconst_1
        36: putfield      #11                 // Field trouble:Z
        39: aload_0
        40: aconst_null
        41: putfield      #19                 // Field textOut:Ljava/io/BufferedWriter;
        44: aload_0
        45: aconst_null
        46: putfield      #16                 // Field charOut:Ljava/io/OutputStreamWriter;
        49: aload_0
        50: aconst_null
        51: putfield      #31                 // Field out:Ljava/io/OutputStream;
        54: aload_1
        55: monitorexit
        56: goto          64
        59: astore_3
        60: aload_1
        61: monitorexit
        62: aload_3
        63: athrow
        64: return
      Exception table:
         from    to  target type
            16    30    33   Class java/io/IOException
             4    56    59   any
            59    62    59   any
      LineNumberTable:
        line 355: 0
        line 356: 4
        line 357: 11
        line 359: 16
        line 360: 23
        line 364: 30
        line 362: 33
        line 363: 34
        line 365: 39
        line 366: 44
        line 367: 49
        line 369: 54
        line 370: 64
      StackMapTable: number_of_entries = 5
        frame_type = 255 /* full_frame */
          offset_delta = 33
          locals = [ class java/io/PrintStream, class java/lang/Object ]
          stack = [ class java/io/IOException ]
        frame_type = 5 /* same */
        frame_type = 14 /* same */
        frame_type = 68 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public boolean checkError();
    descriptor: ()Z
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: aload_0
         1: getfield      #31                 // Field out:Ljava/io/OutputStream;
         4: ifnull        11
         7: aload_0
         8: invokevirtual #39                 // Method flush:()V
        11: aload_0
        12: getfield      #31                 // Field out:Ljava/io/OutputStream;
        15: instanceof    #40                 // class java/io/PrintStream
        18: ifeq          34
        21: aload_0
        22: getfield      #31                 // Field out:Ljava/io/OutputStream;
        25: checkcast     #40                 // class java/io/PrintStream
        28: astore_1
        29: aload_1
        30: invokevirtual #41                 // Method checkError:()Z
        33: ireturn
        34: aload_0
        35: getfield      #11                 // Field trouble:Z
        38: ireturn
      LineNumberTable:
        line 391: 0
        line 392: 7
        line 393: 11
        line 394: 21
        line 395: 29
        line 397: 34
      StackMapTable: number_of_entries = 2
        frame_type = 11 /* same */
        frame_type = 22 /* same */

  protected void setError();
    descriptor: ()V
    flags: ACC_PROTECTED
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: iconst_1
         2: putfield      #11                 // Field trouble:Z
         5: return
      LineNumberTable:
        line 410: 0
        line 411: 5

  protected void clearError();
    descriptor: ()V
    flags: ACC_PROTECTED
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: iconst_0
         2: putfield      #11                 // Field trouble:Z
         5: return
      LineNumberTable:
        line 423: 0
        line 424: 5

  public void write(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: invokespecial #35                 // Method ensureOpen:()V
         8: aload_0
         9: getfield      #31                 // Field out:Ljava/io/OutputStream;
        12: iload_1
        13: invokevirtual #42                 // Method java/io/OutputStream.write:(I)V
        16: iload_1
        17: bipush        10
        19: if_icmpne     36
        22: aload_0
        23: getfield      #13                 // Field autoFlush:Z
        26: ifeq          36
        29: aload_0
        30: getfield      #31                 // Field out:Ljava/io/OutputStream;
        33: invokevirtual #36                 // Method java/io/OutputStream.flush:()V
        36: aload_2
        37: monitorexit
        38: goto          46
        41: astore_3
        42: aload_2
        43: monitorexit
        44: aload_3
        45: athrow
        46: goto          65
        49: astore_2
        50: invokestatic  #44                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        53: invokevirtual #45                 // Method java/lang/Thread.interrupt:()V
        56: goto          65
        59: astore_2
        60: aload_0
        61: iconst_1
        62: putfield      #11                 // Field trouble:Z
        65: return
      Exception table:
         from    to  target type
             4    38    41   any
            41    44    41   any
             0    46    49   Class java/io/InterruptedIOException
             0    46    59   Class java/io/IOException
      LineNumberTable:
        line 447: 0
        line 448: 4
        line 449: 8
        line 450: 16
        line 451: 29
        line 452: 36
        line 459: 46
        line 454: 49
        line 455: 50
        line 459: 56
        line 457: 59
        line 458: 60
        line 460: 65
      StackMapTable: number_of_entries = 6
        frame_type = 252 /* append */
          offset_delta = 36
          locals = [ class java/lang/Object ]
        frame_type = 68 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4
        frame_type = 66 /* same_locals_1_stack_item */
          stack = [ class java/io/InterruptedIOException ]
        frame_type = 73 /* same_locals_1_stack_item */
          stack = [ class java/io/IOException ]
        frame_type = 5 /* same */

  public void write(byte[], int, int);
    descriptor: ([BII)V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=6, args_size=4
         0: aload_0
         1: dup
         2: astore        4
         4: monitorenter
         5: aload_0
         6: invokespecial #35                 // Method ensureOpen:()V
         9: aload_0
        10: getfield      #31                 // Field out:Ljava/io/OutputStream;
        13: aload_1
        14: iload_2
        15: iload_3
        16: invokevirtual #46                 // Method java/io/OutputStream.write:([BII)V
        19: aload_0
        20: getfield      #13                 // Field autoFlush:Z
        23: ifeq          33
        26: aload_0
        27: getfield      #31                 // Field out:Ljava/io/OutputStream;
        30: invokevirtual #36                 // Method java/io/OutputStream.flush:()V
        33: aload         4
        35: monitorexit
        36: goto          47
        39: astore        5
        41: aload         4
        43: monitorexit
        44: aload         5
        46: athrow
        47: goto          68
        50: astore        4
        52: invokestatic  #44                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        55: invokevirtual #45                 // Method java/lang/Thread.interrupt:()V
        58: goto          68
        61: astore        4
        63: aload_0
        64: iconst_1
        65: putfield      #11                 // Field trouble:Z
        68: return
      Exception table:
         from    to  target type
             5    36    39   any
            39    44    39   any
             0    47    50   Class java/io/InterruptedIOException
             0    47    61   Class java/io/IOException
      LineNumberTable:
        line 478: 0
        line 479: 5
        line 480: 9
        line 481: 19
        line 482: 26
        line 483: 33
        line 490: 47
        line 485: 50
        line 486: 52
        line 490: 58
        line 488: 61
        line 489: 63
        line 491: 68
      StackMapTable: number_of_entries = 6
        frame_type = 252 /* append */
          offset_delta = 33
          locals = [ class java/lang/Object ]
        frame_type = 69 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 7
        frame_type = 66 /* same_locals_1_stack_item */
          stack = [ class java/io/InterruptedIOException ]
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/io/IOException ]
        frame_type = 6 /* same */

  public void print(boolean);
    descriptor: (Z)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: ifeq          10
         5: ldc           #53                 // String true
         7: goto          12
        10: ldc           #54                 // String false
        12: invokespecial #55                 // Method write:(Ljava/lang/String;)V
        15: return
      LineNumberTable:
        line 571: 0
        line 572: 15
      StackMapTable: number_of_entries = 2
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 1
          locals = [ class java/io/PrintStream, int ]
          stack = [ class java/io/PrintStream, class java/lang/String ]

  public void print(char);
    descriptor: (C)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: invokestatic  #56                 // Method java/lang/String.valueOf:(C)Ljava/lang/String;
         5: invokespecial #55                 // Method write:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 583: 0
        line 584: 8

  public void print(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: invokestatic  #57                 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
         5: invokespecial #55                 // Method write:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 597: 0
        line 598: 8

  public void print(long);
    descriptor: (J)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=2
         0: aload_0
         1: lload_1
         2: invokestatic  #58                 // Method java/lang/String.valueOf:(J)Ljava/lang/String;
         5: invokespecial #55                 // Method write:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 611: 0
        line 612: 8

  public void print(float);
    descriptor: (F)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: fload_1
         2: invokestatic  #59                 // Method java/lang/String.valueOf:(F)Ljava/lang/String;
         5: invokespecial #55                 // Method write:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 625: 0
        line 626: 8

  public void print(double);
    descriptor: (D)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=2
         0: aload_0
         1: dload_1
         2: invokestatic  #60                 // Method java/lang/String.valueOf:(D)Ljava/lang/String;
         5: invokespecial #55                 // Method write:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 639: 0
        line 640: 8

  public void print(char[]);
    descriptor: ([C)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokespecial #61                 // Method write:([C)V
         5: return
      LineNumberTable:
        line 653: 0
        line 654: 5

  public void print(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_1
         1: ifnonnull     7
         4: ldc           #62                 // String null
         6: astore_1
         7: aload_0
         8: aload_1
         9: invokespecial #55                 // Method write:(Ljava/lang/String;)V
        12: return
      LineNumberTable:
        line 666: 0
        line 667: 4
        line 669: 7
        line 670: 12
      StackMapTable: number_of_entries = 1
        frame_type = 7 /* same */

  public void print(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokestatic  #63                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
         5: invokespecial #55                 // Method write:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 683: 0
        line 684: 8

  public void println();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #64                 // Method newLine:()V
         4: return
      LineNumberTable:
        line 696: 0
        line 697: 4

  public void println(boolean);
    descriptor: (Z)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: iload_1
         6: invokevirtual #65                 // Method print:(Z)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_2
        14: monitorexit
        15: goto          23
        18: astore_3
        19: aload_2
        20: monitorexit
        21: aload_3
        22: athrow
        23: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    21    18   any
      LineNumberTable:
        line 707: 0
        line 708: 4
        line 709: 9
        line 710: 13
        line 711: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, int, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void println(char);
    descriptor: (C)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: iload_1
         6: invokevirtual #66                 // Method print:(C)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_2
        14: monitorexit
        15: goto          23
        18: astore_3
        19: aload_2
        20: monitorexit
        21: aload_3
        22: athrow
        23: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    21    18   any
      LineNumberTable:
        line 721: 0
        line 722: 4
        line 723: 9
        line 724: 13
        line 725: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, int, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void println(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: iload_1
         6: invokevirtual #67                 // Method print:(I)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_2
        14: monitorexit
        15: goto          23
        18: astore_3
        19: aload_2
        20: monitorexit
        21: aload_3
        22: athrow
        23: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    21    18   any
      LineNumberTable:
        line 735: 0
        line 736: 4
        line 737: 9
        line 738: 13
        line 739: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, int, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void println(long);
    descriptor: (J)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=5, args_size=2
         0: aload_0
         1: dup
         2: astore_3
         3: monitorenter
         4: aload_0
         5: lload_1
         6: invokevirtual #68                 // Method print:(J)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_3
        14: monitorexit
        15: goto          25
        18: astore        4
        20: aload_3
        21: monitorexit
        22: aload         4
        24: athrow
        25: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    22    18   any
      LineNumberTable:
        line 749: 0
        line 750: 4
        line 751: 9
        line 752: 13
        line 753: 25
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, long, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 6

  public void println(float);
    descriptor: (F)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: fload_1
         6: invokevirtual #69                 // Method print:(F)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_2
        14: monitorexit
        15: goto          23
        18: astore_3
        19: aload_2
        20: monitorexit
        21: aload_3
        22: athrow
        23: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    21    18   any
      LineNumberTable:
        line 763: 0
        line 764: 4
        line 765: 9
        line 766: 13
        line 767: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, float, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void println(double);
    descriptor: (D)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=5, args_size=2
         0: aload_0
         1: dup
         2: astore_3
         3: monitorenter
         4: aload_0
         5: dload_1
         6: invokevirtual #70                 // Method print:(D)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_3
        14: monitorexit
        15: goto          25
        18: astore        4
        20: aload_3
        21: monitorexit
        22: aload         4
        24: athrow
        25: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    22    18   any
      LineNumberTable:
        line 777: 0
        line 778: 4
        line 779: 9
        line 780: 13
        line 781: 25
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, double, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 6

  public void println(char[]);
    descriptor: ([C)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: aload_1
         6: invokevirtual #71                 // Method print:([C)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_2
        14: monitorexit
        15: goto          23
        18: astore_3
        19: aload_2
        20: monitorexit
        21: aload_3
        22: athrow
        23: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    21    18   any
      LineNumberTable:
        line 791: 0
        line 792: 4
        line 793: 9
        line 794: 13
        line 795: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, class "[C", class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void println(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: aload_1
         6: invokevirtual #72                 // Method print:(Ljava/lang/String;)V
         9: aload_0
        10: invokespecial #64                 // Method newLine:()V
        13: aload_2
        14: monitorexit
        15: goto          23
        18: astore_3
        19: aload_2
        20: monitorexit
        21: aload_3
        22: athrow
        23: return
      Exception table:
         from    to  target type
             4    15    18   any
            18    21    18   any
      LineNumberTable:
        line 805: 0
        line 806: 4
        line 807: 9
        line 808: 13
        line 809: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class java/io/PrintStream, class java/lang/String, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public void println(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=5, args_size=2
         0: aload_1
         1: invokestatic  #63                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
         4: astore_2
         5: aload_0
         6: dup
         7: astore_3
         8: monitorenter
         9: aload_0
        10: aload_2
        11: invokevirtual #72                 // Method print:(Ljava/lang/String;)V
        14: aload_0
        15: invokespecial #64                 // Method newLine:()V
        18: aload_3
        19: monitorexit
        20: goto          30
        23: astore        4
        25: aload_3
        26: monitorexit
        27: aload         4
        29: athrow
        30: return
      Exception table:
         from    to  target type
             9    20    23   any
            23    27    23   any
      LineNumberTable:
        line 821: 0
        line 822: 5
        line 823: 9
        line 824: 14
        line 825: 18
        line 826: 30
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 23
          locals = [ class java/io/PrintStream, class java/lang/Object, class java/lang/String, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 6

  public java.io.PrintStream printf(java.lang.String, java.lang.Object...);
    descriptor: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
    flags: ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: aload_1
         2: aload_2
         3: invokevirtual #73                 // Method format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
         6: areturn
      LineNumberTable:
        line 871: 0

  public java.io.PrintStream printf(java.util.Locale, java.lang.String, java.lang.Object...);
    descriptor: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
    flags: ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=4, locals=4, args_size=4
         0: aload_0
         1: aload_1
         2: aload_2
         3: aload_3
         4: invokevirtual #74                 // Method format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
         7: areturn
      LineNumberTable:
        line 921: 0

  public java.io.PrintStream format(java.lang.String, java.lang.Object...);
    descriptor: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
    flags: ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=4, locals=5, args_size=3
         0: aload_0
         1: dup
         2: astore_3
         3: monitorenter
         4: aload_0
         5: invokespecial #35                 // Method ensureOpen:()V
         8: aload_0
         9: getfield      #75                 // Field formatter:Ljava/util/Formatter;
        12: ifnull        28
        15: aload_0
        16: getfield      #75                 // Field formatter:Ljava/util/Formatter;
        19: invokevirtual #76                 // Method java/util/Formatter.locale:()Ljava/util/Locale;
        22: invokestatic  #77                 // Method java/util/Locale.getDefault:()Ljava/util/Locale;
        25: if_acmpeq     40
        28: aload_0
        29: new           #78                 // class java/util/Formatter
        32: dup
        33: aload_0
        34: invokespecial #79                 // Method java/util/Formatter."<init>":(Ljava/lang/Appendable;)V
        37: putfield      #75                 // Field formatter:Ljava/util/Formatter;
        40: aload_0
        41: getfield      #75                 // Field formatter:Ljava/util/Formatter;
        44: invokestatic  #77                 // Method java/util/Locale.getDefault:()Ljava/util/Locale;
        47: aload_1
        48: aload_2
        49: invokevirtual #80                 // Method java/util/Formatter.format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
        52: pop
        53: aload_3
        54: monitorexit
        55: goto          65
        58: astore        4
        60: aload_3
        61: monitorexit
        62: aload         4
        64: athrow
        65: goto          84
        68: astore_3
        69: invokestatic  #44                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        72: invokevirtual #45                 // Method java/lang/Thread.interrupt:()V
        75: goto          84
        78: astore_3
        79: aload_0
        80: iconst_1
        81: putfield      #11                 // Field trouble:Z
        84: aload_0
        85: areturn
      Exception table:
         from    to  target type
             4    55    58   any
            58    62    58   any
             0    65    68   Class java/io/InterruptedIOException
             0    65    78   Class java/io/IOException
      LineNumberTable:
        line 965: 0
        line 966: 4
        line 967: 8
        line 968: 19
        line 969: 28
        line 970: 40
        line 971: 53
        line 976: 65
        line 972: 68
        line 973: 69
        line 976: 75
        line 974: 78
        line 975: 79
        line 977: 84
      StackMapTable: number_of_entries = 7
        frame_type = 252 /* append */
          offset_delta = 28
          locals = [ class java/lang/Object ]
        frame_type = 11 /* same */
        frame_type = 81 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 6
        frame_type = 66 /* same_locals_1_stack_item */
          stack = [ class java/io/InterruptedIOException ]
        frame_type = 73 /* same_locals_1_stack_item */
          stack = [ class java/io/IOException ]
        frame_type = 5 /* same */

  public java.io.PrintStream format(java.util.Locale, java.lang.String, java.lang.Object...);
    descriptor: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
    flags: ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=5, locals=6, args_size=4
         0: aload_0
         1: dup
         2: astore        4
         4: monitorenter
         5: aload_0
         6: invokespecial #35                 // Method ensureOpen:()V
         9: aload_0
        10: getfield      #75                 // Field formatter:Ljava/util/Formatter;
        13: ifnull        27
        16: aload_0
        17: getfield      #75                 // Field formatter:Ljava/util/Formatter;
        20: invokevirtual #76                 // Method java/util/Formatter.locale:()Ljava/util/Locale;
        23: aload_1
        24: if_acmpeq     40
        27: aload_0
        28: new           #78                 // class java/util/Formatter
        31: dup
        32: aload_0
        33: aload_1
        34: invokespecial #81                 // Method java/util/Formatter."<init>":(Ljava/lang/Appendable;Ljava/util/Locale;)V
        37: putfield      #75                 // Field formatter:Ljava/util/Formatter;
        40: aload_0
        41: getfield      #75                 // Field formatter:Ljava/util/Formatter;
        44: aload_1
        45: aload_2
        46: aload_3
        47: invokevirtual #80                 // Method java/util/Formatter.format:(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
        50: pop
        51: aload         4
        53: monitorexit
        54: goto          65
        57: astore        5
        59: aload         4
        61: monitorexit
        62: aload         5
        64: athrow
        65: goto          86
        68: astore        4
        70: invokestatic  #44                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        73: invokevirtual #45                 // Method java/lang/Thread.interrupt:()V
        76: goto          86
        79: astore        4
        81: aload_0
        82: iconst_1
        83: putfield      #11                 // Field trouble:Z
        86: aload_0
        87: areturn
      Exception table:
         from    to  target type
             5    54    57   any
            57    62    57   any
             0    65    68   Class java/io/InterruptedIOException
             0    65    79   Class java/io/IOException
      LineNumberTable:
        line 1022: 0
        line 1023: 5
        line 1024: 9
        line 1025: 20
        line 1026: 27
        line 1027: 40
        line 1028: 51
        line 1033: 65
        line 1029: 68
        line 1030: 70
        line 1033: 76
        line 1031: 79
        line 1032: 81
        line 1034: 86
      StackMapTable: number_of_entries = 7
        frame_type = 252 /* append */
          offset_delta = 27
          locals = [ class java/lang/Object ]
        frame_type = 12 /* same */
        frame_type = 80 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 7
        frame_type = 66 /* same_locals_1_stack_item */
          stack = [ class java/io/InterruptedIOException ]
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/io/IOException ]
        frame_type = 6 /* same */

  public java.io.PrintStream append(java.lang.CharSequence);
    descriptor: (Ljava/lang/CharSequence;)Ljava/io/PrintStream;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_1
         1: ifnonnull     13
         4: aload_0
         5: ldc           #62                 // String null
         7: invokevirtual #72                 // Method print:(Ljava/lang/String;)V
        10: goto          23
        13: aload_0
        14: aload_1
        15: invokeinterface #82,  1           // InterfaceMethod java/lang/CharSequence.toString:()Ljava/lang/String;
        20: invokevirtual #72                 // Method print:(Ljava/lang/String;)V
        23: aload_0
        24: areturn
      LineNumberTable:
        line 1062: 0
        line 1063: 4
        line 1065: 13
        line 1066: 23
      StackMapTable: number_of_entries = 2
        frame_type = 13 /* same */
        frame_type = 9 /* same */

  public java.io.PrintStream append(java.lang.CharSequence, int, int);
    descriptor: (Ljava/lang/CharSequence;II)Ljava/io/PrintStream;
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=5, args_size=4
         0: aload_1
         1: ifnonnull     9
         4: ldc           #62                 // String null
         6: goto          10
         9: aload_1
        10: astore        4
        12: aload_0
        13: aload         4
        15: iload_2
        16: iload_3
        17: invokeinterface #83,  3           // InterfaceMethod java/lang/CharSequence.subSequence:(II)Ljava/lang/CharSequence;
        22: invokeinterface #82,  1           // InterfaceMethod java/lang/CharSequence.toString:()Ljava/lang/String;
        27: invokespecial #55                 // Method write:(Ljava/lang/String;)V
        30: aload_0
        31: areturn
      LineNumberTable:
        line 1103: 0
        line 1104: 12
        line 1105: 30
      StackMapTable: number_of_entries = 2
        frame_type = 9 /* same */
        frame_type = 64 /* same_locals_1_stack_item */
          stack = [ class java/lang/CharSequence ]

  public java.io.PrintStream append(char);
    descriptor: (C)Ljava/io/PrintStream;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: invokevirtual #66                 // Method print:(C)V
         5: aload_0
         6: areturn
      LineNumberTable:
        line 1125: 0
        line 1126: 5

  public java.lang.Appendable append(char) throws java.io.IOException;
    descriptor: (C)Ljava/lang/Appendable;
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: invokevirtual #84                 // Method append:(C)Ljava/io/PrintStream;
         5: areturn
      LineNumberTable:
        line 57: 0
    Exceptions:
      throws java.io.IOException

  public java.lang.Appendable append(java.lang.CharSequence, int, int) throws java.io.IOException;
    descriptor: (Ljava/lang/CharSequence;II)Ljava/lang/Appendable;
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=4, locals=4, args_size=4
         0: aload_0
         1: aload_1
         2: iload_2
         3: iload_3
         4: invokevirtual #85                 // Method append:(Ljava/lang/CharSequence;II)Ljava/io/PrintStream;
         7: areturn
      LineNumberTable:
        line 57: 0
    Exceptions:
      throws java.io.IOException

  public java.lang.Appendable append(java.lang.CharSequence) throws java.io.IOException;
    descriptor: (Ljava/lang/CharSequence;)Ljava/lang/Appendable;
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokevirtual #86                 // Method append:(Ljava/lang/CharSequence;)Ljava/io/PrintStream;
         5: areturn
      LineNumberTable:
        line 57: 0
    Exceptions:
      throws java.io.IOException
}
SourceFile: "PrintStream.java"
rchaser53noMacBook-Pro:rj rchaser53$ 

javapでjava/io/PrintStreamのクラスファイルの実装を表示

回避策

ビルトインのクラスについて

  • 今回はビルトインのクラスに関してはRustで代用
  • 同様のオペランド スタックの挙動をさせることで対応
  • 実行時に予め読み込ませる形を取った
  • オペランド スタックはJVMが計算や引数、戻り値に使用するスタック

2. ブラウザ上でWasmとして実行

  • wasm-bindgenを利用してWasmを生成
    
    ドキュメントもそれなりに充実している
  • RustとJS間の値の受け渡しをある程度やってくれる

RustとJS間の値の受け渡しをある程度やってくれる

2. ブラウザ上でWasmとして実行

// RustでJavaScriptの関数のネイティブの関数を呼び出したい
#[wasm_bindgen]
extern "C" {
    pub fn alert(s: &str);
}

// RustでJavaScriptの関数の自作の関数を呼び出したい
/* Rust側 */
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(module = "/web/map.js")]
extern "C" {
    pub fn get_file_content_from_js(key: &str) -> Vec<u8>;
}


/* JavaScript側 */
fn get_file_content_from_js(key) {
    // 実際にはclassの情報を持ったobjectがあり、引数のkeyで値を取得する
	const testData = new Uint8Array([
    	202, 254, 186, 190		// CA, FE, BA, BE ... Javaのclass fileの最初のやつ。magic
    ]);
	return testData;
}

現状はまだまだ制限もある

2. ブラウザ上でWasmとして実行

// referencesは返せない
/* error: cannot return references in #[wasm_bindgen] imports yet */
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(module = "/web/map.js")]
extern "C" {
    pub fn get_file_content_from_js(key: &str) -> &[u8];
}

// lifetimeや型パラメータは使えない
/* error: can't #[wasm_bindgen] functions with lifetime or type parameters */
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(module = "/web/map.js")]
extern "C" {
    pub fn get_file_content_from_js<'a>'(key: &'a str) -> Vec<u8>;
}
  • FizzBuzz

実際に動かしてみる

  • 多次元配列とインスタンスの生成

実際に動かしてみる

今回割と上手くいったこと

  • 言語を開発する度に毎度デバッグに苦しめられている
  • 以下の2点を導入したら、デバッグ効率が改善された
    • javapライクの機能の生成
    • 状態を1命令ごとに出力し続ける機能の作成
  • 次回以降の自作言語関連の開発に活かそうと思う

今回割と上手くいったこと

javapライクの機能の生成

  • JVMの処理としてバイナリから情報を抽出する必要がある
    • 今回は1クラスファイル、1structという形で抽出した
  • そもそも間違った形式で抽出していては話にならない
  • javapと似たフォーマットでDebug情報を出力する
  • スタート地点でこけていないか確認できるようにした

今回割と上手くいったこと

状態を1命令ごとに出力し続ける機能の作成

  • どこかの命令で状態の変更を失敗するのが大体の原因
  • 状態遷移を明示すれば失敗した箇所を見つけやすいのでは

今回できなかったこと、やらなかったこと

  • 楽しい

  • 仕様書からでも以外と書ける

  • 実装量が暴力的に多い。1人では無理では?

  • VMだけでは思ったより何もできない

  • デバッグしやすさは重要

まとめや感想

参考資料

ご静聴ありがとうございました

Rust製JVMを作成した話

By rchaser53

Rust製JVMを作成した話

ついでにWasmでブラウザで動かす

  • 14,707