├── .gitignore ├── .travis.yml ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── NestLoop.gif ├── README.md ├── fizzbuzz.gif ├── memo.md ├── src ├── array.rs ├── attribute.rs ├── attribute │ ├── code.rs │ ├── defs.rs │ └── instruction.rs ├── bin │ └── main.rs ├── constant.rs ├── context.rs ├── field.rs ├── java_class.rs ├── java_class │ ├── builtin.rs │ ├── custom.rs │ └── default.rs ├── lib.rs ├── method.rs ├── object.rs ├── operand.rs ├── option.rs ├── order.rs ├── stackframe.rs ├── string_pool.rs ├── utils.rs └── wasm.rs ├── tests ├── class │ ├── ArrayElementClass.class │ ├── CallInstanceMethod.class │ ├── CustomArray.class │ ├── CustomMultiDimentionArray.class │ ├── DconstN.class │ ├── Field.class │ ├── FizzBuzz.class │ ├── FizzBuzz2.class │ ├── FloatCulculate.class │ ├── HelloWorld.class │ ├── InitializeStatic.class │ ├── InstanceField.class │ ├── LongArray.class │ ├── LongCulculate.class │ ├── MinusInt30.class │ ├── NestFor.class │ ├── NestForElement.class │ ├── NewAndCallInstanceMethod.class │ ├── OtherClass.class │ ├── PrimitiveArray.class │ ├── PrintMessage.class │ ├── SimpleMultiDimentions.class │ └── Switch.class ├── original │ ├── CustomArray.java │ ├── CustomMultiDimentionArray.java │ ├── DconstN.java │ ├── FloatCulculate.java │ ├── InitializeStatic.java │ ├── InstanceField.java │ ├── LongArray.java │ ├── LongCulculate.java │ ├── MinusInt30.java │ ├── NestFor.java │ ├── NewAndCallInstanceMethod.java │ ├── PrimitiveArray.java │ ├── SimpleMultiDimentions.java │ └── Switch.java └── test.rs └── web ├── App.vue ├── Result.vue ├── Setting.vue ├── index.html ├── index.js ├── map.js ├── package-lock.json ├── package.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.java 4 | *.class 5 | !tests/class/*.class 6 | !tests/original/*.java 7 | 8 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | 4 | matrix: 5 | include: 6 | - os: linux 7 | rust: nightly 8 | 9 | - os: osx 10 | rust: nightly 11 | 12 | script: 13 | - cargo check 14 | - cargo test -- --test-threads=1 15 | 16 | branches: 17 | only: 18 | - master 19 | 20 | notifications: 21 | email: false -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "type": "java", 5 | "name": "CodeLens (Launch) - InitializeStatic", 6 | "request": "launch", 7 | "mainClass": "InitializeStatic", 8 | "projectName": "r-jvm_11c22782" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.11.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "anyhow" 13 | version = "1.0.23" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "atty" 18 | version = "0.2.13" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | dependencies = [ 21 | "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 23 | ] 24 | 25 | [[package]] 26 | name = "bitflags" 27 | version = "1.2.1" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | 30 | [[package]] 31 | name = "bumpalo" 32 | version = "2.6.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | 35 | [[package]] 36 | name = "cfg-if" 37 | version = "0.1.10" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | 40 | [[package]] 41 | name = "clap" 42 | version = "2.33.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 46 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 47 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 48 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 52 | ] 53 | 54 | [[package]] 55 | name = "console_error_panic_hook" 56 | version = "0.1.6" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 61 | ] 62 | 63 | [[package]] 64 | name = "heck" 65 | version = "0.3.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | dependencies = [ 68 | "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 69 | ] 70 | 71 | [[package]] 72 | name = "js-sys" 73 | version = "0.3.32" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | dependencies = [ 76 | "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 77 | ] 78 | 79 | [[package]] 80 | name = "lazy_static" 81 | version = "1.4.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | 84 | [[package]] 85 | name = "libc" 86 | version = "0.2.65" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | 89 | [[package]] 90 | name = "log" 91 | version = "0.4.8" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | dependencies = [ 94 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 95 | ] 96 | 97 | [[package]] 98 | name = "memchr" 99 | version = "2.2.1" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | 102 | [[package]] 103 | name = "nom" 104 | version = "4.2.3" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | dependencies = [ 107 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 108 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 109 | ] 110 | 111 | [[package]] 112 | name = "proc-macro2" 113 | version = "1.0.6" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | dependencies = [ 116 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 117 | ] 118 | 119 | [[package]] 120 | name = "quote" 121 | version = "1.0.2" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | dependencies = [ 124 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "r-jvm" 129 | version = "0.1.0" 130 | dependencies = [ 131 | "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", 137 | ] 138 | 139 | [[package]] 140 | name = "sourcefile" 141 | version = "0.1.4" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | 144 | [[package]] 145 | name = "strsim" 146 | version = "0.8.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | 149 | [[package]] 150 | name = "syn" 151 | version = "1.0.8" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | dependencies = [ 154 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 157 | ] 158 | 159 | [[package]] 160 | name = "textwrap" 161 | version = "0.11.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | dependencies = [ 164 | "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 165 | ] 166 | 167 | [[package]] 168 | name = "unicode-segmentation" 169 | version = "1.6.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | 172 | [[package]] 173 | name = "unicode-width" 174 | version = "0.1.6" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | 177 | [[package]] 178 | name = "unicode-xid" 179 | version = "0.2.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | 182 | [[package]] 183 | name = "vec_map" 184 | version = "0.8.1" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | 187 | [[package]] 188 | name = "version_check" 189 | version = "0.1.5" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | 192 | [[package]] 193 | name = "wasm-bindgen" 194 | version = "0.2.55" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "wasm-bindgen-macro 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 199 | ] 200 | 201 | [[package]] 202 | name = "wasm-bindgen-backend" 203 | version = "0.2.55" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | dependencies = [ 206 | "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 207 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 208 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 209 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 212 | "wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 213 | ] 214 | 215 | [[package]] 216 | name = "wasm-bindgen-macro" 217 | version = "0.2.55" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | dependencies = [ 220 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "wasm-bindgen-macro-support 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 222 | ] 223 | 224 | [[package]] 225 | name = "wasm-bindgen-macro-support" 226 | version = "0.2.55" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | dependencies = [ 229 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 230 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 233 | "wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 234 | ] 235 | 236 | [[package]] 237 | name = "wasm-bindgen-shared" 238 | version = "0.2.55" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | 241 | [[package]] 242 | name = "wasm-bindgen-webidl" 243 | version = "0.2.55" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | dependencies = [ 246 | "anyhow 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", 247 | "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 248 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 249 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 250 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 251 | "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 252 | "wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 253 | "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 254 | ] 255 | 256 | [[package]] 257 | name = "web-sys" 258 | version = "0.3.32" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | dependencies = [ 261 | "anyhow 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", 262 | "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", 263 | "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "wasm-bindgen-webidl 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 266 | ] 267 | 268 | [[package]] 269 | name = "weedle" 270 | version = "0.10.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | dependencies = [ 273 | "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 274 | ] 275 | 276 | [[package]] 277 | name = "winapi" 278 | version = "0.3.8" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | dependencies = [ 281 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 282 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 283 | ] 284 | 285 | [[package]] 286 | name = "winapi-i686-pc-windows-gnu" 287 | version = "0.4.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | 290 | [[package]] 291 | name = "winapi-x86_64-pc-windows-gnu" 292 | version = "0.4.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | 295 | [metadata] 296 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 297 | "checksum anyhow 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1072d8f55592084072d2d3cb23a4b680a8543c00f10d446118e85ad3718142" 298 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" 299 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 300 | "checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" 301 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 302 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 303 | "checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" 304 | "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 305 | "checksum js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "1c840fdb2167497b0bd0db43d6dfe61e91637fa72f9d061f8bd17ddc44ba6414" 306 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 307 | "checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" 308 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 309 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" 310 | "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" 311 | "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" 312 | "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" 313 | "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" 314 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 315 | "checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" 316 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 317 | "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 318 | "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" 319 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 320 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 321 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 322 | "checksum wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "29ae32af33bacd663a9a28241abecf01f2be64e6a185c6139b04f18b6385c5f2" 323 | "checksum wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "1845584bd3593442dc0de6e6d9f84454a59a057722f36f005e44665d6ab19d85" 324 | "checksum wasm-bindgen-macro 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "87fcc747e6b73c93d22c947a6334644d22cfec5abd8b66238484dc2b0aeb9fe4" 325 | "checksum wasm-bindgen-macro-support 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "3dc4b3f2c4078c8c4a5f363b92fcf62604c5913cbd16c6ff5aaf0f74ec03f570" 326 | "checksum wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ca0b78d6d3be8589b95d1d49cdc0794728ca734adf36d7c9f07e6459508bb53d" 327 | "checksum wasm-bindgen-webidl 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "3126356474ceb717c8fb5549ae387c9fbf4872818454f4d87708bee997214bb5" 328 | "checksum web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "98405c0a2e722ed3db341b4c5b70eb9fe0021621f7350bab76df93b09b649bbf" 329 | "checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" 330 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 331 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 332 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 333 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "r-jvm" 3 | version = "0.1.0" 4 | authors = ["rchaser53 "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | clap = "2.33.0" 9 | console_error_panic_hook = "0.1.6" 10 | lazy_static = "1.4.0" 11 | wasm-bindgen = "0.2.55" 12 | 13 | [dependencies.web-sys] 14 | version = "0.3.32" 15 | features = [ 16 | 'CssStyleDeclaration', 17 | 'Document', 18 | 'Element', 19 | 'HtmlElement', 20 | 'EventTarget', 21 | 'Node', 22 | 'Window', 23 | 'console' 24 | ] 25 | 26 | [dependencies.js-sys] 27 | version = "0.3.32" 28 | 29 | [[bin]] 30 | name = "rj" 31 | path = "src/bin/main.rs" 32 | 33 | [[test]] 34 | name = "test" 35 | harness = false 36 | 37 | [lib] 38 | crate-type = ["cdylib", "lib"] -------------------------------------------------------------------------------- /NestLoop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/NestLoop.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![build_status](https://travis-ci.org/rchaser53/rust-jvm.svg?branch=master) 2 | 3 | # How to play 4 | 5 | ```sh 6 | $ cargo run --bin rj -- ClassName 7 | ``` 8 | 9 | # Play on Browser 10 | 11 | rust-jvm can be used on browser using wasm. 12 | 13 | ```sh 14 | $ cd ./web 15 | $ npm i 16 | $ npm run serve 17 | ``` 18 | 19 | ## dependencies 20 | 21 | you need to install below to play rust-jvm using wasm. 22 | 23 | - [node](https://nodejs.org/ja/download/) 24 | - [rust](https://www.rust-lang.org/tools/install) 25 | - [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) 26 | 27 | ## Demo 28 | 29 | ## FizzBuzz on Wasm 30 | 31 | 32 | ## Creating Instance + Nested Array on Cli 33 | 34 | -------------------------------------------------------------------------------- /fizzbuzz.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/fizzbuzz.gif -------------------------------------------------------------------------------- /memo.md: -------------------------------------------------------------------------------- 1 | ``` 2 | javap -v x.class 3 | javac xxx.java 4 | ``` 5 | 6 | - 0xED to rewrite binary 7 | -------------------------------------------------------------------------------- /src/array.rs: -------------------------------------------------------------------------------- 1 | use crate::operand::Item; 2 | 3 | use std::cell::RefCell; 4 | use std::collections::HashMap; 5 | use std::fmt; 6 | 7 | #[derive(Debug)] 8 | pub struct ArrayMap { 9 | pub id: usize, 10 | pub map: HashMap, 11 | } 12 | impl ArrayMap { 13 | pub fn new() -> ArrayMap { 14 | ArrayMap { 15 | id: 0, 16 | map: HashMap::new(), 17 | } 18 | } 19 | 20 | pub fn add(&mut self, value: Array) -> usize { 21 | let id = self.id; 22 | self.id += 1; 23 | self.map.insert(id, value); 24 | id 25 | } 26 | 27 | pub fn get(&self, id: &usize) -> Option<&Array> { 28 | self.map.get(id) 29 | } 30 | 31 | pub fn get_mut(&mut self, id: &usize) -> Option<&mut Array> { 32 | self.map.get_mut(id) 33 | } 34 | } 35 | 36 | #[derive(Debug)] 37 | pub enum PrimitiveArrayType { 38 | TBoolean = 4, 39 | TChar = 5, 40 | TFloat = 6, 41 | TDouble = 7, 42 | TByte = 8, 43 | TShort = 9, 44 | TInt = 10, 45 | TLong = 11, 46 | } 47 | 48 | #[derive(Clone, Debug)] 49 | pub enum Array { 50 | Primitive(RefCell>), 51 | Array(RefCell>), 52 | Custom(RefCell>), 53 | } 54 | 55 | impl fmt::Display for Array { 56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 57 | write!(f, "") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/attribute.rs: -------------------------------------------------------------------------------- 1 | pub mod code; 2 | pub mod defs; 3 | pub mod instruction; 4 | -------------------------------------------------------------------------------- /src/attribute/code.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute::defs::Attribute; 2 | use crate::attribute::instruction::Instruction; 3 | use crate::constant::ConstantPool; 4 | use crate::string_pool::StringPool; 5 | use crate::utils::extract_x_byte_as_usize; 6 | use std::fmt; 7 | 8 | #[derive(Debug)] 9 | pub struct Code { 10 | pub attribute_name_index: u16, // u2 11 | pub attribute_length: u32, // u4 12 | pub max_stack: u16, // u2 13 | pub max_locals: u16, // u2 14 | pub code_length: usize, // u4 15 | pub code: Vec, 16 | pub exception_table_length: usize, // u2 17 | pub exception_table: Vec, 18 | pub attributes_count: usize, // u2 19 | pub attribute_info: Vec, 20 | } 21 | 22 | impl Code { 23 | pub fn new( 24 | string_pool: &mut StringPool, 25 | constant_pool: &ConstantPool, 26 | inputs: &[u8], 27 | index: usize, 28 | attribute_name_index: u16, 29 | ) -> (Code, usize) { 30 | let (attribute_length, index) = extract_x_byte_as_usize(inputs, index, 4); 31 | let attribute_length = attribute_length as u32; 32 | 33 | let (max_stack, index) = extract_x_byte_as_usize(inputs, index, 2); 34 | let max_stack = max_stack as u16; 35 | 36 | let (max_locals, index) = extract_x_byte_as_usize(inputs, index, 2); 37 | let max_locals = max_locals as u16; 38 | 39 | let (code_length, mut index) = extract_x_byte_as_usize(inputs, index, 4); 40 | let mut code = Vec::with_capacity(code_length); 41 | let mut code_loop_index = 0; 42 | 43 | while code_length - code_loop_index > 0 { 44 | let (tag, update_index) = extract_x_byte_as_usize(inputs, index, 1); 45 | let (update_index, consume_index) = 46 | Instruction::create_and_push(&mut code, inputs, update_index, tag); 47 | code_loop_index += consume_index; 48 | index = update_index; 49 | } 50 | 51 | let (exception_table_length, index) = extract_x_byte_as_usize(inputs, index, 2); 52 | let exception_table = Vec::with_capacity(exception_table_length); 53 | 54 | let (attributes_count, mut index) = extract_x_byte_as_usize(inputs, index, 2); 55 | let mut attribute_info = Vec::with_capacity(attributes_count); 56 | for _ in 0..attributes_count { 57 | let (attribute, update_index) = 58 | Attribute::new(string_pool, constant_pool, inputs, index); 59 | index = update_index; 60 | attribute_info.push(attribute); 61 | } 62 | 63 | ( 64 | Code { 65 | attribute_name_index, 66 | attribute_length, 67 | max_stack, 68 | max_locals, 69 | code_length, 70 | code, 71 | exception_table_length, 72 | exception_table, 73 | attributes_count, 74 | attribute_info, 75 | }, 76 | index, 77 | ) 78 | } 79 | } 80 | 81 | impl fmt::Display for Code { 82 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 83 | let mut code_strs = Vec::with_capacity(self.code_length); 84 | for (index, code) in self.code.iter().enumerate() { 85 | if let Instruction::Noope = *code { 86 | continue; 87 | } 88 | code_strs.push(format!("{}: {}", index, code)); 89 | } 90 | let mut attribute_strs = Vec::with_capacity(self.attributes_count); 91 | for item in self.attribute_info.iter() { 92 | attribute_strs.push(format!("{}", item)); 93 | } 94 | 95 | write!( 96 | f, 97 | "Code: 98 | stack:{}, locals={}, args_size=? 99 | {} 100 | {}", 101 | self.max_stack, 102 | self.max_locals, 103 | code_strs.join("\n "), 104 | attribute_strs.join("\n "), 105 | ) 106 | } 107 | } 108 | 109 | #[derive(Debug)] 110 | pub struct ExceptionTableItem { 111 | pub start_pc: u16, //u2 112 | pub end_pc: u16, //u2 113 | pub handler_pc: u16, //u2 114 | pub catch_type: u16, //u2 115 | } 116 | -------------------------------------------------------------------------------- /src/attribute/defs.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute::code::Code; 2 | use crate::constant::{ConstPoolItem, ConstantPool}; 3 | use crate::string_pool::StringPool; 4 | use crate::utils::extract_x_byte_as_usize; 5 | 6 | use std::fmt; 7 | 8 | #[derive(Debug)] 9 | pub enum Attribute { 10 | SourceFile(SourceFile), 11 | InnerClasses, 12 | EnclosingMethod, 13 | SourceDebugExtension, 14 | BootstrapMethods, 15 | ConstantValue, 16 | Code(Code), 17 | Exceptions, 18 | RuntimeVisibleParameterAnnotations, 19 | RuntimeInvisibleParameterAnnotations, 20 | AnnotationDefault, 21 | MethodParameters, 22 | Synthetic, 23 | Deprecated, 24 | Signature, 25 | RuntimeVisibleAnnotations, 26 | RuntimeInvisibleAnnotations, 27 | LineNumberTable(LineNumberTable), 28 | LocalVariableTable, 29 | LocalVariableTypeTable, 30 | StackMapTable(StackMapTable), 31 | RuntimeVisibleTypeAnnotations, 32 | RuntimeInvisibleTypeAnnotations, 33 | } 34 | 35 | impl Attribute { 36 | pub fn new( 37 | string_pool: &mut StringPool, 38 | constant_pool: &ConstantPool, 39 | inputs: &[u8], 40 | index: usize, 41 | ) -> (Attribute, usize) { 42 | let (attribute_name_index, index) = extract_x_byte_as_usize(inputs, index, 2); 43 | if let ConstPoolItem::ConstantUtf8(item) = &constant_pool.0[attribute_name_index] { 44 | let attribute_name_index = attribute_name_index as u16; 45 | 46 | match AttributeTag::from(string_pool.get_value(&item.id)) { 47 | AttributeTag::SourceFile => { 48 | let (item, index) = SourceFile::new(inputs, index, attribute_name_index); 49 | (Attribute::SourceFile(item), index) 50 | } 51 | AttributeTag::LineNumberTable => { 52 | let (item, index) = LineNumberTable::new(inputs, index, attribute_name_index); 53 | (Attribute::LineNumberTable(item), index) 54 | } 55 | AttributeTag::StackMapTable => { 56 | let (item, index) = StackMapTable::new(inputs, index, attribute_name_index); 57 | (Attribute::StackMapTable(item), index) 58 | } 59 | AttributeTag::Code => { 60 | let (item, index) = Code::new( 61 | string_pool, 62 | constant_pool, 63 | inputs, 64 | index, 65 | attribute_name_index, 66 | ); 67 | (Attribute::Code(item), index) 68 | } 69 | _ => unimplemented!(), 70 | } 71 | } else { 72 | panic!( 73 | "{:?} is not ConstantUtf8", 74 | constant_pool.0[attribute_name_index] 75 | ); 76 | } 77 | } 78 | } 79 | 80 | impl fmt::Display for Attribute { 81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 82 | match self { 83 | Attribute::SourceFile(val) => write!(f, "{}", val), 84 | Attribute::Code(val) => write!(f, "{}", val), 85 | Attribute::LineNumberTable(val) => write!(f, "{}", val), 86 | Attribute::StackMapTable(val) => write!(f, "{}", val), 87 | _ => unimplemented!(), 88 | } 89 | } 90 | } 91 | 92 | // this is a custom enum for handling 93 | #[derive(Debug)] 94 | pub enum AttributeTag { 95 | SourceFile, 96 | InnerClasses, 97 | EnclosingMethod, 98 | SourceDebugExtension, 99 | BootstrapMethods, 100 | ConstantValue, 101 | Code, 102 | Exceptions, 103 | RuntimeVisibleParameterAnnotations, 104 | RuntimeInvisibleParameterAnnotations, 105 | AnnotationDefault, 106 | MethodParameters, 107 | Synthetic, 108 | Deprecated, 109 | Signature, 110 | RuntimeVisibleAnnotations, 111 | RuntimeInvisibleAnnotations, 112 | LineNumberTable, 113 | LocalVariableTable, 114 | LocalVariableTypeTable, 115 | StackMapTable, 116 | RuntimeVisibleTypeAnnotations, 117 | RuntimeInvisibleTypeAnnotations, 118 | } 119 | 120 | impl From for AttributeTag { 121 | fn from(input: String) -> AttributeTag { 122 | match input.as_str() { 123 | "SourceFile" => AttributeTag::SourceFile, 124 | "InnerClasses" => AttributeTag::InnerClasses, 125 | "EnclosingMethod" => AttributeTag::EnclosingMethod, 126 | "SourceDebugExtension" => AttributeTag::SourceDebugExtension, 127 | "BootstrapMethods" => AttributeTag::BootstrapMethods, 128 | "ConstantValue" => AttributeTag::ConstantValue, 129 | "Code" => AttributeTag::Code, 130 | "Exceptions" => AttributeTag::Exceptions, 131 | "RuntimeVisibleParameterAnnotations" => { 132 | AttributeTag::RuntimeVisibleParameterAnnotations 133 | } 134 | "RuntimeInvisibleParameterAnnotations" => { 135 | AttributeTag::RuntimeInvisibleParameterAnnotations 136 | } 137 | "AnnotationDefault" => AttributeTag::AnnotationDefault, 138 | "MethodParameters" => AttributeTag::MethodParameters, 139 | "Synthetic" => AttributeTag::Synthetic, 140 | "Deprecated" => AttributeTag::Deprecated, 141 | "Signature" => AttributeTag::Signature, 142 | "RuntimeVisibleAnnotations" => AttributeTag::RuntimeVisibleAnnotations, 143 | "RuntimeInvisibleAnnotations" => AttributeTag::RuntimeInvisibleAnnotations, 144 | "LineNumberTable" => AttributeTag::LineNumberTable, 145 | "LocalVariableTable" => AttributeTag::LocalVariableTable, 146 | "LocalVariableTypeTable" => AttributeTag::LocalVariableTypeTable, 147 | "StackMapTable" => AttributeTag::StackMapTable, 148 | "RuntimeVisibleTypeAnnotations" => AttributeTag::RuntimeVisibleTypeAnnotations, 149 | "RuntimeInvisibleTypeAnnotations" => AttributeTag::RuntimeInvisibleTypeAnnotations, 150 | _ => unreachable!(), 151 | } 152 | } 153 | } 154 | 155 | #[derive(Debug)] 156 | pub struct SourceFile { 157 | pub attribute_name_index: u16, // u2 158 | pub attribute_length: u32, // u4 159 | pub sourcefile_index: u16, // u2 160 | } 161 | 162 | impl SourceFile { 163 | pub fn new(inputs: &[u8], index: usize, attribute_name_index: u16) -> (SourceFile, usize) { 164 | let (attribute_length, index) = extract_x_byte_as_usize(inputs, index, 4); 165 | let attribute_length = attribute_length as u32; 166 | 167 | let (sourcefile_index, index) = extract_x_byte_as_usize(inputs, index, 2); 168 | let sourcefile_index = sourcefile_index as u16; 169 | 170 | let source_file = SourceFile { 171 | attribute_name_index, 172 | attribute_length, 173 | sourcefile_index, 174 | }; 175 | (source_file, index) 176 | } 177 | } 178 | 179 | impl fmt::Display for SourceFile { 180 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 181 | write!(f, "SourceFile: #{}", self.sourcefile_index) 182 | } 183 | } 184 | 185 | #[derive(Debug)] 186 | pub struct LineNumberTable { 187 | pub attribute_name_index: u16, // u2 188 | pub attribute_length: u32, // u4 189 | pub line_number_table_length: usize, // u2 190 | pub line_number_tables: Vec, 191 | } 192 | 193 | impl LineNumberTable { 194 | pub fn new(inputs: &[u8], index: usize, attribute_name_index: u16) -> (LineNumberTable, usize) { 195 | let (attribute_length, index) = extract_x_byte_as_usize(inputs, index, 4); 196 | let attribute_length = attribute_length as u32; 197 | 198 | let (line_number_table_length, mut index) = extract_x_byte_as_usize(inputs, index, 2); 199 | let mut line_number_tables = Vec::with_capacity(line_number_table_length); 200 | 201 | for _ in 0..line_number_table_length { 202 | let (start_pc, update_index) = extract_x_byte_as_usize(inputs, index, 2); 203 | let start_pc = start_pc as u16; 204 | 205 | let (line_number, update_index) = extract_x_byte_as_usize(inputs, update_index, 2); 206 | let line_number = line_number as u16; 207 | 208 | line_number_tables.push(LineNumberTableItem { 209 | start_pc, 210 | line_number, 211 | }); 212 | index = update_index; 213 | } 214 | 215 | ( 216 | LineNumberTable { 217 | attribute_name_index, 218 | attribute_length, 219 | line_number_table_length, 220 | line_number_tables, 221 | }, 222 | index, 223 | ) 224 | } 225 | } 226 | 227 | impl fmt::Display for LineNumberTable { 228 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 229 | let mut table_strs = Vec::with_capacity(self.line_number_table_length); 230 | for item in self.line_number_tables.iter() { 231 | table_strs.push(format!("{}", item)); 232 | } 233 | write!( 234 | f, 235 | "LineNumberTable: 236 | {}", 237 | table_strs.join("\n ") 238 | ) 239 | } 240 | } 241 | 242 | #[derive(Debug)] 243 | pub struct LineNumberTableItem { 244 | pub start_pc: u16, // u2 245 | pub line_number: u16, // u2 246 | } 247 | 248 | impl fmt::Display for LineNumberTableItem { 249 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 250 | write!(f, " line {}: {}", self.line_number, self.start_pc) 251 | } 252 | } 253 | 254 | #[derive(Debug)] 255 | pub struct StackMapTable { 256 | attribute_name_index: u16, // u2 257 | attribute_length: u32, // u4 258 | number_of_entries: usize, // u2 259 | stack_map_frame: Vec, 260 | } 261 | 262 | impl StackMapTable { 263 | pub fn new(inputs: &[u8], index: usize, attribute_name_index: u16) -> (StackMapTable, usize) { 264 | let (attribute_length, index) = extract_x_byte_as_usize(inputs, index, 4); 265 | let attribute_length = attribute_length as u32; 266 | 267 | let (number_of_entries, mut index) = extract_x_byte_as_usize(inputs, index, 2); 268 | let mut stack_map_frame = Vec::with_capacity(number_of_entries); 269 | 270 | for _ in 0..number_of_entries { 271 | let (frame, update_index) = StackMapFrame::new(inputs, index); 272 | stack_map_frame.push(frame); 273 | index = update_index; 274 | } 275 | ( 276 | StackMapTable { 277 | attribute_name_index, 278 | attribute_length, 279 | number_of_entries, 280 | stack_map_frame, 281 | }, 282 | index, 283 | ) 284 | } 285 | } 286 | 287 | impl fmt::Display for StackMapTable { 288 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 289 | let mut stack_map_frame_strs = Vec::with_capacity(self.number_of_entries); 290 | for item in self.stack_map_frame.iter() { 291 | stack_map_frame_strs.push(format!("frame_type = {}", item)); 292 | } 293 | 294 | write!( 295 | f, 296 | "StackMapTable: number_of_entries = {} 297 | frame_type = {}", 298 | self.number_of_entries, 299 | stack_map_frame_strs.join("\n ") 300 | ) 301 | } 302 | } 303 | 304 | #[derive(Debug)] 305 | pub enum StackMapFrame { 306 | SameFrame(SameFrame), 307 | SameLocals1StackItemFrame, 308 | SameLocals1StackItemFrameExtended, 309 | ChopFrame(ChopFrame), 310 | SameFrameExtended, 311 | AppendFrame(AppendFrame), 312 | FullFrame(FullFrame), 313 | } 314 | 315 | #[derive(Debug)] 316 | pub struct SameFrame { 317 | frame_type: usize, 318 | } 319 | 320 | #[derive(Debug)] 321 | pub struct ChopFrame { 322 | frame_type: usize, 323 | offset_delta: usize, 324 | } 325 | 326 | #[derive(Debug)] 327 | pub struct AppendFrame { 328 | frame_type: usize, 329 | offset_delta: usize, 330 | locals: Vec, 331 | } 332 | 333 | #[derive(Debug)] 334 | pub struct FullFrame { 335 | frame_type: usize, 336 | offset_delta: usize, // u2 337 | number_of_locals: usize, // u2 338 | locals: Vec, // locals[number_of_locals] 339 | number_of_stack_items: usize, // u2 340 | stack: Vec, // stack[number_of_stack_items] 341 | } 342 | 343 | impl StackMapFrame { 344 | pub fn new(inputs: &[u8], index: usize) -> (StackMapFrame, usize) { 345 | let (frame_type, index) = extract_x_byte_as_usize(inputs, index, 1); 346 | match frame_type { 347 | 0..=63 => (StackMapFrame::SameFrame(SameFrame { frame_type }), index), 348 | 248..=250 => { 349 | let (offset_delta, index) = extract_x_byte_as_usize(inputs, index, 2); 350 | ( 351 | StackMapFrame::ChopFrame(ChopFrame { 352 | frame_type, 353 | offset_delta, 354 | }), 355 | index, 356 | ) 357 | } 358 | 252..=254 => { 359 | let (offset_delta, index) = extract_x_byte_as_usize(inputs, index, 2); 360 | let length = (frame_type as i32) - 251; 361 | let (locals, index) = if length > 0 { 362 | extract_verification_type_info(inputs, index, length as usize) 363 | } else { 364 | (vec![], index) 365 | }; 366 | 367 | ( 368 | StackMapFrame::AppendFrame(AppendFrame { 369 | frame_type, 370 | offset_delta, 371 | locals, 372 | }), 373 | index, 374 | ) 375 | } 376 | 255 => { 377 | let (offset_delta, index) = extract_x_byte_as_usize(inputs, index, 2); 378 | let (number_of_locals, index) = extract_x_byte_as_usize(inputs, index, 2); 379 | let (locals, index) = 380 | extract_verification_type_info(inputs, index, number_of_locals); 381 | let (number_of_stack_items, index) = extract_x_byte_as_usize(inputs, index, 2); 382 | let (stack, index) = 383 | extract_verification_type_info(inputs, index, number_of_stack_items); 384 | 385 | ( 386 | StackMapFrame::FullFrame(FullFrame { 387 | frame_type, 388 | offset_delta, 389 | number_of_locals, 390 | locals, 391 | number_of_stack_items, 392 | stack, 393 | }), 394 | index, 395 | ) 396 | } 397 | _ => unimplemented!(), 398 | } 399 | } 400 | } 401 | 402 | impl fmt::Display for StackMapFrame { 403 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 404 | match self { 405 | StackMapFrame::SameFrame(SameFrame { frame_type }) => { 406 | write!(f, "{} /* same */", frame_type) 407 | } 408 | StackMapFrame::ChopFrame(ChopFrame { 409 | frame_type, 410 | offset_delta, 411 | }) => write!( 412 | f, 413 | "{} /* chop */ 414 | offset_delta = {}", 415 | frame_type, offset_delta 416 | ), 417 | StackMapFrame::AppendFrame(AppendFrame { 418 | frame_type, 419 | offset_delta, 420 | locals, 421 | }) => write!( 422 | f, 423 | "{} /* append */ 424 | offset_delta = {} 425 | locals = [{}]", 426 | frame_type, 427 | offset_delta, 428 | locals 429 | .iter() 430 | .map(|local| format!("{}", local)) 431 | .collect::>() 432 | .join(", ") 433 | ), 434 | StackMapFrame::FullFrame(FullFrame { 435 | frame_type, 436 | offset_delta, 437 | locals, 438 | stack, 439 | .. 440 | }) => write!( 441 | f, 442 | "{} /* full_frame */ 443 | offset_delta = {} 444 | locals = [{}] 445 | stack = [{}]", 446 | frame_type, 447 | offset_delta, 448 | format!( 449 | "{}", 450 | locals 451 | .iter() 452 | .map(|local| format!("{}", local)) 453 | .collect::>() 454 | .join(", ") 455 | ), 456 | format!( 457 | "{}", 458 | stack 459 | .iter() 460 | .map(|item| format!("{}", item)) 461 | .collect::>() 462 | .join(", ") 463 | ) 464 | ), 465 | _ => unimplemented!(), 466 | } 467 | } 468 | } 469 | 470 | #[derive(Debug)] 471 | pub enum VerificationTypeInfo { 472 | TopVariableInfo, // 0 473 | IntegerVariableInfo, // 1 474 | FloatVariableInfo, // 2 475 | DoubleVariableInfo, // 3 476 | LongVariableInfo, // 4 477 | NullVariableInfo, // 5 478 | UninitializedThisVariableInfo, // 6 479 | ObjectVariableInfo(usize), // 7, u2(cpool_index) 480 | UninitializedVariableInfo(usize), // 8, u2(offset) 481 | } 482 | 483 | impl fmt::Display for VerificationTypeInfo { 484 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 485 | match self { 486 | VerificationTypeInfo::TopVariableInfo => write!(f, "top"), 487 | VerificationTypeInfo::IntegerVariableInfo => write!(f, "int"), 488 | VerificationTypeInfo::FloatVariableInfo => write!(f, "float"), 489 | VerificationTypeInfo::DoubleVariableInfo => write!(f, "double"), 490 | VerificationTypeInfo::LongVariableInfo => write!(f, "long"), 491 | VerificationTypeInfo::NullVariableInfo => write!(f, "null"), 492 | VerificationTypeInfo::UninitializedThisVariableInfo => write!(f, "uninitialized_this"), 493 | VerificationTypeInfo::ObjectVariableInfo(index) => { 494 | write!(f, "object_variable: #{}", index) 495 | } 496 | VerificationTypeInfo::UninitializedVariableInfo(index) => { 497 | write!(f, "uninitialized_variable: #{}", index) 498 | } 499 | } 500 | } 501 | } 502 | 503 | pub fn extract_verification_type_info( 504 | inputs: &[u8], 505 | original_index: usize, 506 | length: usize, 507 | ) -> (Vec, usize) { 508 | let mut index = original_index; 509 | let mut result = Vec::with_capacity(length); 510 | for _ in 0..length { 511 | let (tag, update_index) = extract_x_byte_as_usize(inputs, index, 1); 512 | let (type_info, update_index) = match tag { 513 | 0 => (VerificationTypeInfo::TopVariableInfo, update_index), 514 | 1 => (VerificationTypeInfo::IntegerVariableInfo, update_index), 515 | 2 => (VerificationTypeInfo::FloatVariableInfo, update_index), 516 | 3 => (VerificationTypeInfo::DoubleVariableInfo, update_index), 517 | 4 => (VerificationTypeInfo::LongVariableInfo, update_index), 518 | 5 => (VerificationTypeInfo::NullVariableInfo, update_index), 519 | 6 => ( 520 | VerificationTypeInfo::UninitializedThisVariableInfo, 521 | update_index, 522 | ), 523 | 7 => { 524 | let (cpool_index, update_index) = extract_x_byte_as_usize(inputs, update_index, 2); 525 | ( 526 | VerificationTypeInfo::ObjectVariableInfo(cpool_index), 527 | update_index, 528 | ) 529 | } 530 | 8 => { 531 | let (offset, update_index) = extract_x_byte_as_usize(inputs, update_index, 2); 532 | ( 533 | VerificationTypeInfo::UninitializedVariableInfo(offset), 534 | update_index, 535 | ) 536 | } 537 | _ => unreachable!( 538 | "should be below 8 for verification_type_info. actual {}", 539 | tag 540 | ), 541 | }; 542 | result.push(type_info); 543 | index = update_index; 544 | } 545 | (result, index) 546 | } 547 | -------------------------------------------------------------------------------- /src/attribute/instruction.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{extract_x_byte_as_usize, extract_x_byte_as_vec}; 2 | use std::fmt; 3 | 4 | #[derive(Debug)] 5 | pub enum Instruction { 6 | Aconstnull, // 0x01 7 | IconstN(i32), // 0x02(-1) - 0x08(5) 8 | LconstN(usize), // 0x09(0) - 0x0a(1) 9 | FconstN(f32), // 0x0b(0) - 0x0d(1) 10 | DconstN(usize), // 0x0e(0) - 0x0f(1) 11 | Bipush(i32), // 0x10 12 | Sipush(usize), // 0x11 13 | Ldc(usize), // 0x12 14 | Ldc2W(usize, usize), // 0x14 15 | Iload(usize), // 0x15 16 | Aload(usize), // 0x19 17 | IloadN(usize), // 0x1a(0) - 0x1d(3) 18 | LloadN(usize), // 0x1e(0) - 0x21(3) 19 | FloadN(usize), // 0x22(0) - 0x25(3) 20 | DloadN(usize), // 0x26(0) - 0x29(3) 21 | AloadN(usize), // 0x2a(0) - 0x2d(3) 22 | Iaload, // 0x2e 23 | Laload, // 0x2f 24 | Aaload, // 0x32 25 | Baload, // 0x33 26 | Istore(i32), // 0x36 27 | Astore(usize), // 0x3a 28 | IstoreN(i32), // 0x3b(0) - 0x3e(3) 29 | LstoreN(usize), // 0x3f(0) - 0x42(3) 30 | FstoreN(usize), // 0x43(0) - 0x46(3) 31 | DstoreN(usize), // 0x47(0) - 0x4a(3) 32 | AstoreN(usize), // 0x4b(0) - 0x4e(3) 33 | Iastore, // 0x4f 34 | Lastore, // 0x50 35 | Aastore, // 0x53 36 | Bastore, // 0x54 37 | Pop, // 0x57 38 | Dup, // 0x59 39 | Iadd, // 0x60 40 | Ladd, // 0x61 41 | Fadd, // 0x62 42 | Isub, // 0x64 43 | Lsub, // 0x65 44 | Fsub, // 0x66 45 | Imul, // 0x68 46 | Lmul, // 0x69 47 | Fmul, // 0x6a 48 | Idiv, // 0x6c 49 | Ldiv, // 0x6d 50 | Fdiv, // 0x6e 51 | Irem, // 0x70 52 | Lrem, // 0x71 53 | Iinc(usize, usize), // 0x84 54 | Lcmp, // 0x94 55 | Fcmpg, // 0x95 56 | Fcmpl, // 0x96 57 | Ifeq(usize, usize), // 0x99 58 | Ifne(usize, usize), // 0x9a 59 | Iflt(usize, usize), // 0x9b 60 | Ifge(usize, usize), // 0x9c 61 | Ifgt(usize, usize), // 0x9d 62 | Ifle(usize, usize), // 0x9e 63 | Ificmpeq(usize, usize), // 0x9f 64 | Ificmpne(usize, usize), // 0xa0 65 | Ificmplt(usize, usize), // 0xa1 66 | Ificmpge(usize, usize), // 0xa2 67 | Ificmpgt(usize, usize), // 0xa3 68 | Ificmple(usize, usize), // 0xa4 69 | Goto(usize), // 0xa7 70 | Lookupswitch(Vec<(Option, usize)>), // 0xab 71 | Ireturn, // 0xac 72 | Areturn, // 0xb0 73 | Return, // 0xb1 74 | Getstatic(usize), // 0xb2 75 | Putstatic(usize), // 0xb3 76 | Getfield(usize), // 0xb4 77 | Putfield(usize), // 0xb5 78 | Invokevirtual(usize), // 0xb6 79 | Invokespecial(usize), // 0xb7 80 | Invokestatic(usize), // 0xb8 81 | New(usize), // 0xbb 82 | Newarray(usize), // 0xbc 83 | Anewarray(usize), // 0xbd 84 | Multianewarray(usize, usize), // 0xc5 85 | Noope, // custom command for Ificmple etc. 86 | } 87 | 88 | impl fmt::Display for Instruction { 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 90 | match self { 91 | Instruction::Aconstnull => write!(f, "aconst_null"), 92 | Instruction::IconstN(val) => write!(f, "iconst_{}", val), 93 | Instruction::LconstN(val) => write!(f, "lconst_{}", val), 94 | Instruction::FconstN(val) => write!(f, "fconst_{}", val), 95 | Instruction::DconstN(val) => write!(f, "dconst_{}", val), 96 | Instruction::Bipush(val) => write!(f, "bipush {}", val), 97 | Instruction::Sipush(val) => write!(f, "sipush {}", val), 98 | Instruction::Ldc(val) => write!(f, "ldc #{}", val), 99 | Instruction::Ldc2W(a, b) => write!(f, "ldc2_w #{},{}", a, b), 100 | Instruction::Iload(val) => write!(f, "iload #{}", val), 101 | Instruction::Aload(val) => write!(f, "aload #{}", val), 102 | Instruction::IloadN(val) => write!(f, "iload_{}", val), 103 | Instruction::LloadN(val) => write!(f, "lload_{}", val), 104 | Instruction::FloadN(val) => write!(f, "fload_{}", val), 105 | Instruction::DloadN(val) => write!(f, "dload_{}", val), 106 | Instruction::AloadN(val) => write!(f, "aload_{}", val), 107 | Instruction::Iaload => write!(f, "iaload"), 108 | Instruction::Laload => write!(f, "laload"), 109 | Instruction::Aaload => write!(f, "aaload"), 110 | Instruction::Baload => write!(f, "baload"), 111 | Instruction::Istore(val) => write!(f, "istore #{}", val), 112 | Instruction::Astore(val) => write!(f, "astore #{}", val), 113 | Instruction::Aastore => write!(f, "aastore"), 114 | Instruction::Bastore => write!(f, "bastore"), 115 | Instruction::IstoreN(val) => write!(f, "istore_{}", val), 116 | Instruction::LstoreN(val) => write!(f, "lstore_{}", val), 117 | Instruction::FstoreN(val) => write!(f, "fstore_{}", val), 118 | Instruction::DstoreN(val) => write!(f, "dstore_{}", val), 119 | Instruction::Iastore => write!(f, "iastore"), 120 | Instruction::Lastore => write!(f, "lastore"), 121 | Instruction::AstoreN(val) => write!(f, "astore_{}", val), 122 | Instruction::Pop => write!(f, "pop"), 123 | Instruction::Dup => write!(f, "dup"), 124 | Instruction::Iadd => write!(f, "iadd"), 125 | Instruction::Ladd => write!(f, "ladd"), 126 | Instruction::Fadd => write!(f, "fadd"), 127 | Instruction::Isub => write!(f, "isub"), 128 | Instruction::Lsub => write!(f, "lsub"), 129 | Instruction::Fsub => write!(f, "fsub"), 130 | Instruction::Imul => write!(f, "imul"), 131 | Instruction::Lmul => write!(f, "lmul"), 132 | Instruction::Fmul => write!(f, "fmul"), 133 | Instruction::Idiv => write!(f, "idiv"), 134 | Instruction::Ldiv => write!(f, "ldiv"), 135 | Instruction::Fdiv => write!(f, "fdiv"), 136 | Instruction::Irem => write!(f, "irem"), 137 | Instruction::Lrem => write!(f, "lrem"), 138 | Instruction::Iinc(a, b) => write!(f, "iinc {}, {}", a, b), 139 | Instruction::Lcmp => write!(f, "lcmp"), 140 | Instruction::Fcmpg => write!(f, "fcmpg"), 141 | Instruction::Fcmpl => write!(f, "fcmpl"), 142 | Instruction::Ifeq(a, b) => write!(f, "if_eq {}, {}", a, b), 143 | Instruction::Ifne(a, b) => write!(f, "if_ne {}, {}", a, b), 144 | Instruction::Iflt(a, b) => write!(f, "if_lt {}, {}", a, b), 145 | Instruction::Ifge(a, b) => write!(f, "if_ge {}, {}", a, b), 146 | Instruction::Ifgt(a, b) => write!(f, "if_gt {}, {}", a, b), 147 | Instruction::Ifle(a, b) => write!(f, "if_le {}, {}", a, b), 148 | Instruction::Ificmpeq(a, b) => write!(f, "if_icmpeq {}, {}", a, b), 149 | Instruction::Ificmpne(a, b) => write!(f, "if_icmpne {}, {}", a, b), 150 | Instruction::Ificmplt(a, b) => write!(f, "if_icmplt {}, {}", a, b), 151 | Instruction::Ificmpge(a, b) => write!(f, "if_icmpge {}, {}", a, b), 152 | Instruction::Ificmpgt(a, b) => write!(f, "if_icmpgt {}, {}", a, b), 153 | Instruction::Ificmple(a, b) => write!(f, "if_icmple {}, {}", a, b), 154 | Instruction::Goto(val) => write!(f, "goto {}", val), 155 | Instruction::Ireturn => write!(f, "ireturn"), 156 | Instruction::Lookupswitch(vals) => { 157 | let vals_length = vals.len(); 158 | let mut output_strings = Vec::with_capacity(vals_length); 159 | for (key, val) in &vals[1..vals_length] { 160 | output_strings.push(format!(" {}: {}", key.unwrap(), val)); 161 | } 162 | output_strings.push(format!(" default: {}", vals.last().unwrap().1)); 163 | write!( 164 | f, 165 | "lookupswitch {{ // {} 166 | {} 167 | }}", 168 | vals_length - 1, 169 | output_strings.join("\n") 170 | ) 171 | } 172 | Instruction::Areturn => write!(f, "areturn"), 173 | Instruction::Return => write!(f, "return"), 174 | Instruction::Getstatic(val) => write!(f, "getstatic #{}", val), 175 | Instruction::Putstatic(val) => write!(f, "putstatic #{}", val), 176 | Instruction::Getfield(val) => write!(f, "getfield #{}", val), 177 | Instruction::Putfield(val) => write!(f, "putfield #{}", val), 178 | Instruction::Invokevirtual(val) => write!(f, "invokevirtual #{}", val), 179 | Instruction::Invokespecial(val) => write!(f, "invokespecial #{}", val), 180 | Instruction::Invokestatic(val) => write!(f, "invokestatic #{}", val), 181 | Instruction::New(val) => write!(f, "new #{}", val), 182 | Instruction::Newarray(val) => write!(f, "newarray #{}", val), 183 | Instruction::Anewarray(val) => write!(f, "anewarray #{}", val), 184 | Instruction::Multianewarray(index, dimensions) => { 185 | write!(f, "multianewarray #{} {}", index, dimensions) 186 | } 187 | Instruction::Noope => write!(f, "noope"), 188 | } 189 | } 190 | } 191 | 192 | impl Instruction { 193 | pub fn create_and_push( 194 | codes: &mut Vec, 195 | inputs: &[u8], 196 | index: usize, 197 | tag: usize, 198 | ) -> (usize, usize) { 199 | macro_rules! simple_instruct { 200 | ($expr:expr) => { 201 | codes.push($expr); 202 | return (index, 1); 203 | }; 204 | } 205 | 206 | match tag { 207 | // aconst_null 208 | 0x01 => { 209 | simple_instruct!(Instruction::Aconstnull); 210 | } 211 | // iconst_n 212 | val @ 0x02..=0x08 => { 213 | simple_instruct!(Instruction::IconstN(val as i32 - 0x03)); 214 | } 215 | // lconst_n 216 | val @ 0x09..=0x0a => { 217 | simple_instruct!(Instruction::LconstN(val - 0x09)); 218 | } 219 | // fconst_n 220 | val @ 0x0b..=0x0d => { 221 | simple_instruct!(Instruction::FconstN((val - 0x0b) as f32)); 222 | } 223 | // dconst_n 224 | val @ 0x0e..=0x0f => { 225 | simple_instruct!(Instruction::DconstN(val - 0x0e)); 226 | } 227 | // bipush 228 | 0x10 => { 229 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 230 | let val = if val > 0x79 { 231 | -1 * ((val ^ 0xff) + 1) as i32 232 | } else { 233 | val as i32 234 | }; 235 | codes.push(Instruction::Bipush(val)); 236 | codes.push(Instruction::Noope); 237 | (index, 2) 238 | } 239 | // sipush 240 | 0x11 => { 241 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 242 | codes.push(Instruction::Sipush(val)); 243 | codes.push(Instruction::Noope); 244 | codes.push(Instruction::Noope); 245 | (index, 3) 246 | } 247 | // ldc 248 | 0x12 => { 249 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 250 | codes.push(Instruction::Ldc(val)); 251 | codes.push(Instruction::Noope); 252 | (index, 2) 253 | } 254 | // ldc2_w 255 | 0x14 => { 256 | let (val, index) = extract_x_byte_as_vec(inputs, index, 2); 257 | codes.push(Instruction::Ldc2W(val[0] as usize, val[1] as usize)); 258 | codes.push(Instruction::Noope); 259 | codes.push(Instruction::Noope); 260 | (index, 3) 261 | } 262 | // iload 263 | 0x15 => { 264 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 265 | codes.push(Instruction::Iload(val)); 266 | codes.push(Instruction::Noope); 267 | (index, 2) 268 | } 269 | // aload 270 | 0x19 => { 271 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 272 | codes.push(Instruction::Aload(val)); 273 | codes.push(Instruction::Noope); 274 | (index, 2) 275 | } 276 | // iload_n 277 | val @ 0x1a..=0x1d => { 278 | simple_instruct!(Instruction::IloadN(val - 0x1a)); 279 | } 280 | // lload_n 281 | val @ 0x1e..=0x21 => { 282 | simple_instruct!(Instruction::LloadN(val - 0x1e)); 283 | } 284 | // fload_n 285 | val @ 0x22..=0x25 => { 286 | simple_instruct!(Instruction::FloadN(val - 0x22)); 287 | } 288 | // dload_n 289 | val @ 0x26..=0x29 => { 290 | simple_instruct!(Instruction::DloadN(val - 0x26)); 291 | } 292 | // aload_n 293 | val @ 0x2a..=0x2d => { 294 | simple_instruct!(Instruction::AloadN(val - 0x2a)); 295 | } 296 | // iaload 297 | 0x2e => { 298 | simple_instruct!(Instruction::Iaload); 299 | } 300 | // laload 301 | 0x2f => { 302 | simple_instruct!(Instruction::Laload); 303 | } 304 | // iaload 305 | 0x32 => { 306 | simple_instruct!(Instruction::Aaload); 307 | } 308 | // baload 309 | 0x33 => { 310 | simple_instruct!(Instruction::Baload); 311 | } 312 | // istore 313 | 0x36 => { 314 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 315 | codes.push(Instruction::Istore(val as i32)); 316 | codes.push(Instruction::Noope); 317 | (index, 2) 318 | } 319 | // astore 320 | 0x3a => { 321 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 322 | codes.push(Instruction::Astore(val)); 323 | codes.push(Instruction::Noope); 324 | (index, 2) 325 | } 326 | // istore_n 327 | val @ 0x3b..=0x3e => { 328 | simple_instruct!(Instruction::IstoreN(val as i32 - 0x3b)); 329 | } 330 | // lstore_n 331 | val @ 0x3f..=0x42 => { 332 | simple_instruct!(Instruction::LstoreN(val - 0x3f)); 333 | } 334 | // fstore_n 335 | val @ 0x43..=0x46 => { 336 | simple_instruct!(Instruction::FstoreN(val - 0x43)); 337 | } 338 | // dstore_n 339 | val @ 0x47..=0x4a => { 340 | simple_instruct!(Instruction::DstoreN(val - 0x47)); 341 | } 342 | // astore_n 343 | val @ 0x4b..=0x4e => { 344 | simple_instruct!(Instruction::AstoreN(val - 0x4b)); 345 | } 346 | // iastore 347 | 0x4f => { 348 | simple_instruct!(Instruction::Iastore); 349 | } 350 | // lastore 351 | 0x50 => { 352 | simple_instruct!(Instruction::Lastore); 353 | } 354 | // aastore 355 | 0x53 => { 356 | simple_instruct!(Instruction::Aastore); 357 | } 358 | // bastore 359 | 0x54 => { 360 | simple_instruct!(Instruction::Bastore); 361 | } 362 | // pop 363 | 0x57 => { 364 | simple_instruct!(Instruction::Pop); 365 | } 366 | // dup 367 | 0x59 => { 368 | simple_instruct!(Instruction::Dup); 369 | } 370 | // iadd 371 | 0x60 => { 372 | simple_instruct!(Instruction::Iadd); 373 | } 374 | // ladd 375 | 0x61 => { 376 | simple_instruct!(Instruction::Ladd); 377 | } 378 | // fadd 379 | 0x62 => { 380 | simple_instruct!(Instruction::Fadd); 381 | } 382 | // isub 383 | 0x64 => { 384 | simple_instruct!(Instruction::Isub); 385 | } 386 | // lsub 387 | 0x65 => { 388 | simple_instruct!(Instruction::Lsub); 389 | } 390 | // fsub 391 | 0x66 => { 392 | simple_instruct!(Instruction::Fsub); 393 | } 394 | // imul 395 | 0x68 => { 396 | simple_instruct!(Instruction::Imul); 397 | } 398 | // lmul 399 | 0x69 => { 400 | simple_instruct!(Instruction::Lmul); 401 | } 402 | // fmul 403 | 0x6a => { 404 | simple_instruct!(Instruction::Fmul); 405 | } 406 | // idiv 407 | 0x6c => { 408 | simple_instruct!(Instruction::Idiv); 409 | } 410 | // ldiv 411 | 0x6d => { 412 | simple_instruct!(Instruction::Ldiv); 413 | } 414 | // fdiv 415 | 0x6e => { 416 | simple_instruct!(Instruction::Fdiv); 417 | } 418 | // irem 419 | 0x70 => { 420 | simple_instruct!(Instruction::Irem); 421 | } 422 | // lrem 423 | 0x71 => { 424 | simple_instruct!(Instruction::Lrem); 425 | } 426 | // iinc 427 | 0x84 => { 428 | let (val, index) = extract_x_byte_as_vec(inputs, index, 2); 429 | codes.push(Instruction::Iinc(val[0] as usize, val[1] as usize)); 430 | codes.push(Instruction::Noope); 431 | codes.push(Instruction::Noope); 432 | (index, 3) 433 | } 434 | // lcmp 435 | 0x94 => { 436 | simple_instruct!(Instruction::Lcmp); 437 | } 438 | // fcmpg 439 | 0x95 => { 440 | simple_instruct!(Instruction::Fcmpg); 441 | } 442 | // fcmpl 443 | 0x96 => { 444 | simple_instruct!(Instruction::Fcmpl); 445 | } 446 | // ifeq 447 | 0x99 => { 448 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 449 | let code_length = codes.len(); 450 | codes.push(Instruction::Ifeq( 451 | (val + code_length - 1) & 0xffff, 452 | code_length + 2, 453 | )); 454 | codes.push(Instruction::Noope); 455 | codes.push(Instruction::Noope); 456 | (index, 3) 457 | } 458 | // ifne 459 | 0x9a => { 460 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 461 | let code_length = codes.len(); 462 | codes.push(Instruction::Ifne( 463 | (val + code_length - 1) & 0xffff, 464 | code_length + 2, 465 | )); 466 | codes.push(Instruction::Noope); 467 | codes.push(Instruction::Noope); 468 | (index, 3) 469 | } 470 | // iflt 471 | 0x9b => { 472 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 473 | let code_length = codes.len(); 474 | codes.push(Instruction::Iflt( 475 | (val + code_length - 1) & 0xffff, 476 | code_length + 2, 477 | )); 478 | codes.push(Instruction::Noope); 479 | codes.push(Instruction::Noope); 480 | (index, 3) 481 | } 482 | // ifge 483 | 0x9c => { 484 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 485 | let code_length = codes.len(); 486 | codes.push(Instruction::Ifge( 487 | (val + code_length - 1) & 0xffff, 488 | code_length + 2, 489 | )); 490 | codes.push(Instruction::Noope); 491 | codes.push(Instruction::Noope); 492 | (index, 3) 493 | } 494 | // ifgt 495 | 0x9d => { 496 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 497 | let code_length = codes.len(); 498 | codes.push(Instruction::Ifgt( 499 | (val + code_length - 1) & 0xffff, 500 | code_length + 2, 501 | )); 502 | codes.push(Instruction::Noope); 503 | codes.push(Instruction::Noope); 504 | (index, 3) 505 | } 506 | // ifle 507 | 0x9e => { 508 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 509 | let code_length = codes.len(); 510 | codes.push(Instruction::Ifle( 511 | (val + code_length - 1) & 0xffff, 512 | code_length + 2, 513 | )); 514 | codes.push(Instruction::Noope); 515 | codes.push(Instruction::Noope); 516 | (index, 3) 517 | } 518 | // if_icmpeq 519 | 0x9f => { 520 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 521 | let code_length = codes.len(); 522 | codes.push(Instruction::Ificmpeq( 523 | (val + code_length - 1) & 0xffff, 524 | code_length + 2, 525 | )); 526 | codes.push(Instruction::Noope); 527 | codes.push(Instruction::Noope); 528 | (index, 3) 529 | } 530 | // if_icmpne 531 | 0xa0 => { 532 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 533 | let code_length = codes.len(); 534 | codes.push(Instruction::Ificmpne( 535 | (val + code_length - 1) & 0xffff, 536 | code_length + 2, 537 | )); 538 | codes.push(Instruction::Noope); 539 | codes.push(Instruction::Noope); 540 | (index, 3) 541 | } 542 | // if_icmplt 543 | 0xa1 => { 544 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 545 | let code_length = codes.len(); 546 | codes.push(Instruction::Ificmplt( 547 | (val + code_length - 1) & 0xffff, 548 | code_length + 2, 549 | )); 550 | codes.push(Instruction::Noope); 551 | codes.push(Instruction::Noope); 552 | (index, 3) 553 | } 554 | // if_icmpge 555 | 0xa2 => { 556 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 557 | let code_length = codes.len(); 558 | codes.push(Instruction::Ificmpge( 559 | (val + code_length - 1) & 0xffff, 560 | code_length + 2, 561 | )); 562 | codes.push(Instruction::Noope); 563 | codes.push(Instruction::Noope); 564 | (index, 3) 565 | } 566 | // if_icmpgt 567 | 0xa3 => { 568 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 569 | let code_length = codes.len(); 570 | codes.push(Instruction::Ificmpgt( 571 | (val + code_length - 1) & 0xffff, 572 | code_length + 2, 573 | )); 574 | codes.push(Instruction::Noope); 575 | codes.push(Instruction::Noope); 576 | (index, 3) 577 | } 578 | // if_icmple 579 | 0xa4 => { 580 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 581 | let code_length = codes.len(); 582 | codes.push(Instruction::Ificmple( 583 | (val + code_length - 1) & 0xffff, 584 | code_length + 2, 585 | )); 586 | codes.push(Instruction::Noope); 587 | codes.push(Instruction::Noope); 588 | (index, 3) 589 | } 590 | // goto 591 | 0xa7 => { 592 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 593 | let code_length = codes.len(); 594 | codes.push(Instruction::Goto((val + code_length - 1) & 0xFFFF)); 595 | codes.push(Instruction::Noope); 596 | codes.push(Instruction::Noope); 597 | (index, 3) 598 | } 599 | // lookupswitch 600 | 0xab => { 601 | let (offset, index) = extract_x_byte_as_usize(inputs, index, 4); 602 | // default_value can be used for branch_length 603 | let (default_value, mut index) = extract_x_byte_as_usize(inputs, index, 4); 604 | // default + branch_length 605 | let mut switch_values = Vec::with_capacity(1 + default_value); 606 | switch_values.push((None, offset + default_value)); 607 | 608 | for _ in 0..default_value { 609 | let (key, update_index) = extract_x_byte_as_usize(inputs, index, 4); 610 | let (val, update_index) = extract_x_byte_as_usize(inputs, update_index, 4); 611 | switch_values.push((Some(key), val + default_value)); 612 | index = update_index 613 | } 614 | codes.push(Instruction::Lookupswitch(switch_values)); 615 | 616 | let set_length = default_value + 1; 617 | let switch_instructions_len = set_length * 4 * 2; 618 | for _ in 0..switch_instructions_len { 619 | codes.push(Instruction::Noope) 620 | } 621 | (index, switch_instructions_len + 1) 622 | } 623 | // ireturn 624 | 0xac => { 625 | simple_instruct!(Instruction::Ireturn); 626 | } 627 | // areturn 628 | 0xb0 => { 629 | simple_instruct!(Instruction::Areturn); 630 | } 631 | // return 632 | 0xb1 => { 633 | simple_instruct!(Instruction::Return); 634 | } 635 | // getstatic 636 | 0xb2 => { 637 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 638 | codes.push(Instruction::Getstatic(val)); 639 | codes.push(Instruction::Noope); 640 | codes.push(Instruction::Noope); 641 | (index, 3) 642 | } 643 | // getstatic 644 | 0xb3 => { 645 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 646 | codes.push(Instruction::Putstatic(val)); 647 | codes.push(Instruction::Noope); 648 | codes.push(Instruction::Noope); 649 | (index, 3) 650 | } 651 | // getfield 652 | 0xb4 => { 653 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 654 | codes.push(Instruction::Getfield(val)); 655 | codes.push(Instruction::Noope); 656 | codes.push(Instruction::Noope); 657 | (index, 3) 658 | } 659 | // putfield 660 | 0xb5 => { 661 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 662 | codes.push(Instruction::Putfield(val)); 663 | codes.push(Instruction::Noope); 664 | codes.push(Instruction::Noope); 665 | (index, 3) 666 | } 667 | // invokevirtual 668 | 0xb6 => { 669 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 670 | codes.push(Instruction::Invokevirtual(val)); 671 | codes.push(Instruction::Noope); 672 | codes.push(Instruction::Noope); 673 | (index, 3) 674 | } 675 | // invokespecial 676 | 0xb7 => { 677 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 678 | codes.push(Instruction::Invokespecial(val)); 679 | codes.push(Instruction::Noope); 680 | codes.push(Instruction::Noope); 681 | (index, 3) 682 | } 683 | // invokestatic 684 | 0xb8 => { 685 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 686 | codes.push(Instruction::Invokestatic(val)); 687 | codes.push(Instruction::Noope); 688 | codes.push(Instruction::Noope); 689 | (index, 3) 690 | } 691 | // new 692 | 0xbb => { 693 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 694 | codes.push(Instruction::New(val)); 695 | codes.push(Instruction::Noope); 696 | codes.push(Instruction::Noope); 697 | (index, 3) 698 | } 699 | // newarray 700 | 0xbc => { 701 | let (val, index) = extract_x_byte_as_usize(inputs, index, 1); 702 | codes.push(Instruction::Newarray(val)); 703 | codes.push(Instruction::Noope); 704 | (index, 2) 705 | } 706 | // anewarray 707 | 0xbd => { 708 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 709 | codes.push(Instruction::Anewarray(val)); 710 | codes.push(Instruction::Noope); 711 | codes.push(Instruction::Noope); 712 | (index, 3) 713 | } 714 | // multianewarray 715 | 0xc5 => { 716 | let (val, index) = extract_x_byte_as_usize(inputs, index, 2); 717 | let (dimentions, index) = extract_x_byte_as_usize(inputs, index, 1); 718 | codes.push(Instruction::Multianewarray(val, dimentions)); 719 | codes.push(Instruction::Noope); 720 | codes.push(Instruction::Noope); 721 | codes.push(Instruction::Noope); 722 | (index, 4) 723 | } 724 | _ => unimplemented!("tag: {:x}", tag), 725 | } 726 | } 727 | 728 | pub fn counsume_index(&self) -> usize { 729 | match self { 730 | Instruction::Lookupswitch(vals) => vals.len() * 4, 731 | Instruction::Multianewarray(_, _) => 3, 732 | Instruction::Ificmple(_, _) 733 | | Instruction::Getstatic(_) 734 | | Instruction::Putstatic(_) 735 | | Instruction::Getfield(_) 736 | | Instruction::Putfield(_) 737 | | Instruction::Iinc(_, _) 738 | | Instruction::Sipush(_) 739 | | Instruction::Ldc2W(_, _) 740 | | Instruction::Invokevirtual(_) 741 | | Instruction::Invokespecial(_) 742 | | Instruction::Invokestatic(_) 743 | | Instruction::New(_) 744 | | Instruction::Anewarray(_) => 2, 745 | Instruction::Iload(_) 746 | | Instruction::Aload(_) 747 | | Instruction::Istore(_) 748 | | Instruction::Astore(_) 749 | | Instruction::Bipush(_) 750 | | Instruction::Newarray(_) 751 | | Instruction::Ldc(_) => 1, 752 | Instruction::Aconstnull 753 | | Instruction::IconstN(_) 754 | | Instruction::LconstN(_) 755 | | Instruction::FconstN(_) 756 | | Instruction::DconstN(_) 757 | | Instruction::IstoreN(_) 758 | | Instruction::IloadN(_) 759 | | Instruction::LstoreN(_) 760 | | Instruction::FstoreN(_) 761 | | Instruction::DstoreN(_) 762 | | Instruction::LloadN(_) 763 | | Instruction::FloadN(_) 764 | | Instruction::DloadN(_) 765 | | Instruction::AstoreN(_) 766 | | Instruction::AloadN(_) 767 | | Instruction::Pop 768 | | Instruction::Dup 769 | | Instruction::Iadd 770 | | Instruction::Ladd 771 | | Instruction::Fadd 772 | | Instruction::Isub 773 | | Instruction::Lsub 774 | | Instruction::Fsub 775 | | Instruction::Imul 776 | | Instruction::Lmul 777 | | Instruction::Fmul 778 | | Instruction::Idiv 779 | | Instruction::Ldiv 780 | | Instruction::Fdiv 781 | | Instruction::Irem 782 | | Instruction::Lrem 783 | | Instruction::Lcmp 784 | | Instruction::Fcmpg 785 | | Instruction::Fcmpl 786 | | Instruction::Ireturn 787 | | Instruction::Areturn 788 | | Instruction::Iaload 789 | | Instruction::Laload 790 | | Instruction::Aaload 791 | | Instruction::Baload 792 | | Instruction::Iastore 793 | | Instruction::Lastore 794 | | Instruction::Aastore 795 | | Instruction::Bastore 796 | | Instruction::Return => 0, 797 | instruction => unimplemented!("{}", instruction), 798 | } 799 | } 800 | } 801 | -------------------------------------------------------------------------------- /src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | use clap::{App, Arg}; 3 | 4 | use r_jvm; 5 | 6 | fn main() { 7 | let matches = App::new("rj") 8 | .version("0.1") 9 | .author("rchaser53 ") 10 | .about("toy jvm implemented by Rust") 11 | .arg( 12 | Arg::with_name("debug") 13 | .help("emits the debug information") 14 | .long("debug") 15 | .takes_value(true), 16 | ) 17 | .args_from_usage( 18 | " 19 | 'Sets the input file to use'", 20 | ) 21 | .get_matches(); 22 | 23 | if let Some(file_name) = matches.value_of("INPUT") { 24 | r_jvm::execute( 25 | file_name.to_string(), 26 | matches 27 | .value_of("debug") 28 | .unwrap_or("0") 29 | .parse::() 30 | .unwrap_or(0), 31 | ); 32 | } else { 33 | println!("should input the file"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/constant.rs: -------------------------------------------------------------------------------- 1 | use crate::operand::Item; 2 | use crate::string_pool::StringPool; 3 | use crate::utils::*; 4 | use std::fmt; 5 | 6 | #[derive(Debug, PartialEq)] 7 | pub struct ConstantPool(pub Vec); 8 | impl ConstantPool { 9 | pub fn new( 10 | string_map: &mut StringPool, 11 | inputs: &[u8], 12 | mut index: usize, 13 | length: usize, 14 | ) -> (ConstantPool, usize) { 15 | let mut items = vec![ConstPoolItem::ConstantNull]; 16 | let mut constant_index = 0; 17 | let constant_pool_length = length - 1; 18 | while constant_pool_length > constant_index { 19 | let (tag, update_index) = extract_x_byte_as_usize(inputs, index, 1); 20 | 21 | let (item, update_index) = match ConstPoolTag::from(tag) { 22 | ConstPoolTag::ConstantClass => { 23 | let (item, update_index) = 24 | ConstantClass::create_and_update_index(inputs, update_index); 25 | (ConstPoolItem::ConstantClass(item), update_index) 26 | } 27 | ConstPoolTag::ConstantMethodref => { 28 | let (item, update_index) = 29 | ConstantMethodref::create_and_update_index(inputs, update_index); 30 | (ConstPoolItem::ConstantMethodref(item), update_index) 31 | } 32 | ConstPoolTag::ConstantNameAndType => { 33 | let (item, update_index) = 34 | ConstantNameAndType::create_and_update_index(inputs, update_index); 35 | (ConstPoolItem::ConstantNameAndType(item), update_index) 36 | } 37 | ConstPoolTag::ConstantUtf8 => { 38 | let (item, update_index) = 39 | ConstantUtf8::create_and_update_index(string_map, inputs, update_index); 40 | (ConstPoolItem::ConstantUtf8(item), update_index) 41 | } 42 | ConstPoolTag::ConstantString => { 43 | let (item, update_index) = 44 | ConstantString::create_and_update_index(inputs, update_index); 45 | (ConstPoolItem::ConstantString(item), update_index) 46 | } 47 | ConstPoolTag::ConstantFieldref => { 48 | let (item, update_index) = 49 | ConstantFieldref::create_and_update_index(inputs, update_index); 50 | (ConstPoolItem::ConstantFieldref(item), update_index) 51 | } 52 | ConstPoolTag::ConstantFloat => { 53 | let (item, update_index) = 54 | ConstantFloat::create_and_update_index(inputs, update_index); 55 | (ConstPoolItem::ConstantFloat(item), update_index) 56 | } 57 | ConstPoolTag::ConstantLong => { 58 | let (item, update_index) = 59 | ConstantLong::create_and_update_index(inputs, update_index); 60 | constant_index += 2; 61 | index = update_index; 62 | items.push(ConstPoolItem::ConstantLong(item)); 63 | items.push(ConstPoolItem::ConstantNull); 64 | continue; 65 | } 66 | ConstPoolTag::ConstantDouble => { 67 | let (item, update_index) = 68 | ConstantDouble::create_and_update_index(inputs, update_index); 69 | constant_index += 2; 70 | index = update_index; 71 | items.push(ConstPoolItem::ConstantDouble(item)); 72 | items.push(ConstPoolItem::ConstantNull); 73 | continue; 74 | } 75 | _ => unimplemented!( 76 | " 77 | constant pool purse failed. 78 | current constant pool 79 | {}. 80 | next tag: {}", 81 | ConstantPool(items), 82 | tag 83 | ), 84 | }; 85 | constant_index += 1; 86 | index = update_index; 87 | items.push(item); 88 | } 89 | 90 | (ConstantPool(items), index) 91 | } 92 | 93 | pub fn get_main_index(&self) -> Option { 94 | self.0.iter().position(|item| { 95 | if let ConstPoolItem::ConstantUtf8(utf8) = item { 96 | utf8.bytes == vec![0x6D, 0x61, 0x69, 0x6E] // main 97 | } else { 98 | false 99 | } 100 | }) 101 | } 102 | 103 | pub fn get_clinit_index(&self) -> Option { 104 | self.0.iter().position(|item| { 105 | if let ConstPoolItem::ConstantUtf8(utf8) = item { 106 | // 107 | utf8.bytes == vec![0x3C, 0x63, 0x6C, 0x69, 0x6E, 0x69, 0x74, 0x3E] 108 | } else { 109 | false 110 | } 111 | }) 112 | } 113 | 114 | pub fn create_and_set_operand_stack_item(&self, stack: &mut Vec, index: usize) { 115 | match self.0.get(index) { 116 | Some(ref item) => match item { 117 | // ConstPoolItem::ConstantClass(ConstantClass), 118 | // ConstPoolItem::ConstantMethodref(ConstantMethodref), 119 | // ConstPoolItem::ConstantInterfaceMethodref, 120 | // ConstPoolItem::ConstantNameAndType(ConstantNameAndType), 121 | ConstPoolItem::ConstantString(ref item) => { 122 | stack.push(Item::String(self.get_string(item.string_index))); 123 | } 124 | ConstPoolItem::ConstantFieldref(_) => stack.push(Item::Fieldref(index)), 125 | ConstPoolItem::ConstantUtf8(item) => stack.push(Item::String(item.id)), 126 | ConstPoolItem::ConstantLong(ref item) => { 127 | stack.push(Item::Long(item.high_bytes)); 128 | stack.push(Item::Long(item.low_bytes)); 129 | } 130 | ConstPoolItem::ConstantNull => { 131 | unreachable!("index: {}. should not come ConstantNull", index) 132 | } 133 | _ => unimplemented!("{:?}", item), 134 | }, 135 | _ => unreachable!("index: {} is not found", index), 136 | }; 137 | } 138 | 139 | pub fn get_name_and_type(&self, index: usize) -> &ConstantNameAndType { 140 | match self.0.get(index) { 141 | Some(ConstPoolItem::ConstantNameAndType(ref item)) => item, 142 | _ => unreachable!( 143 | "should be ConstantNameAndType. actual {:?}", 144 | self.0.get(index) 145 | ), 146 | } 147 | } 148 | 149 | pub fn get_class_ref(&self, index: usize) -> &ConstantClass { 150 | match self.0.get(index) { 151 | Some(ConstPoolItem::ConstantClass(ref item)) => item, 152 | _ => unreachable!("should be ConstantClass. actual {:?}", self.0.get(index)), 153 | } 154 | } 155 | 156 | pub fn get_class_ref_name(&self, index: usize) -> usize { 157 | match self.0.get(index) { 158 | Some(ConstPoolItem::ConstantClass(ref item)) => self.get_utf8(item.name_index), 159 | _ => unreachable!("should be ConstantClass. actual {:?}", self.0.get(index)), 160 | } 161 | } 162 | 163 | // (class_name, field_name) 164 | pub fn get_class_and_field_name(&self, index: usize) -> (usize, usize) { 165 | let field = self.get_field_ref(index); 166 | let name_and_type = self.get_name_and_type(field.name_and_type_index); 167 | let class_name = self.get_class_ref_name(field.class_index); 168 | let field_name = self.get_utf8(name_and_type.name_index); 169 | (class_name, field_name) 170 | } 171 | 172 | pub fn get_method_ref(&self, index: usize) -> &ConstantMethodref { 173 | match self.0.get(index) { 174 | Some(ConstPoolItem::ConstantMethodref(ref item)) => item, 175 | _ => unreachable!( 176 | "should be ConstantMethodref. actual {:?}", 177 | self.0.get(index) 178 | ), 179 | } 180 | } 181 | 182 | pub fn get_field_ref(&self, index: usize) -> &ConstantFieldref { 183 | match self.0.get(index) { 184 | Some(ConstPoolItem::ConstantFieldref(ref item)) => item, 185 | _ => unreachable!("should be ConstantFieldref. actual {:?}", self.0.get(index)), 186 | } 187 | } 188 | 189 | pub fn get_string(&self, index: usize) -> usize { 190 | match self.0.get(index) { 191 | Some(ConstPoolItem::ConstantString(ref item)) => self.get_utf8(item.string_index), 192 | _ => unreachable!("should be ConstantString. actual {:?}", self.0.get(index)), 193 | } 194 | } 195 | 196 | pub fn get_float(&self, index: usize) -> f32 { 197 | match self.0.get(index) { 198 | Some(ConstPoolItem::ConstantFloat(ConstantFloat { bytes, .. })) => { 199 | let s: f32 = if bytes >> 31 == 0 as usize { 1.0 } else { -1.0 }; 200 | let e = ((bytes >> 23 as usize) & 0xff) as f32; 201 | let m = if e == 0.0 { 202 | ((bytes & 0x7fffff) << 1) as f32 203 | } else { 204 | ((bytes & 0x7fffff) | 0x800000) as f32 205 | }; 206 | // s * m * 2e-150 207 | s * m * f32::powf(2.0f32, e - 150 as f32) 208 | } 209 | _ => unreachable!("should be ConstantFloat. actual {:?}", self.0.get(index)), 210 | } 211 | } 212 | 213 | pub fn get_item_tag(&self, index: usize) -> ConstPoolTag { 214 | match self.0.get(index) { 215 | Some(ConstPoolItem::ConstantString(_)) => ConstPoolTag::ConstantString, 216 | Some(ConstPoolItem::ConstantFloat(_)) => ConstPoolTag::ConstantFloat, 217 | _ => unimplemented!(), 218 | } 219 | } 220 | 221 | pub fn get_utf8(&self, index: usize) -> usize { 222 | match self.0.get(index) { 223 | Some(ConstPoolItem::ConstantUtf8(item)) => item.id, 224 | _ => unreachable!("should be ConstantUtf8. actual {:?}", self.0.get(index)), 225 | } 226 | } 227 | 228 | pub fn get_utf8_as_string(&self, string_pool: &mut StringPool, index: usize) -> String { 229 | let id = self.get_utf8(index); 230 | string_pool.get_value(&id) 231 | } 232 | 233 | pub fn get_fieldref_as_utf8(&self, index: usize) -> usize { 234 | match self.0.get(index) { 235 | Some(ConstPoolItem::ConstantFieldref(ref item)) => { 236 | let name_and_type = self.get_name_and_type(item.name_and_type_index); 237 | self.get_utf8(name_and_type.name_index) 238 | } 239 | _ => unreachable!("should be ConstantFieldRef. actual {:?}", self.0.get(index)), 240 | } 241 | } 242 | } 243 | 244 | impl fmt::Display for ConstantPool { 245 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 246 | let mut result = Vec::with_capacity(self.0.len()); 247 | for (index, item) in self.0.iter().enumerate() { 248 | let rw = match item { 249 | ConstPoolItem::ConstantNull => { 250 | continue; 251 | } 252 | ConstPoolItem::ConstantMethodref(item) => format!( 253 | " #{} = Methodref #{}.#{}", 254 | index, item.class_index, item.name_and_type_index, 255 | ), 256 | ConstPoolItem::ConstantFieldref(item) => format!( 257 | " #{} = Fieldref #{}.#{}", 258 | index, item.class_index, item.name_and_type_index 259 | ), 260 | ConstPoolItem::ConstantString(item) => { 261 | format!(" #{} = String #{}", index, item.string_index) 262 | } 263 | ConstPoolItem::ConstantClass(item) => { 264 | format!(" #{} = Class #{}", index, item.name_index) 265 | } 266 | ConstPoolItem::ConstantUtf8(item) => format!( 267 | " #{} = Utf8 {}", 268 | index, 269 | String::from_utf8_lossy(item.bytes.as_slice()) 270 | ), 271 | ConstPoolItem::ConstantNameAndType(item) => format!( 272 | " #{} = NameAndType #{}:#{}", 273 | index, item.name_index, item.descriptor_index 274 | ), 275 | ConstPoolItem::ConstantLong(item) => format!( 276 | " #{} = Long {}l", 277 | index, 278 | ((item.high_bytes as i64) << 32) as i64 | item.low_bytes as i64 279 | ), 280 | ConstPoolItem::ConstantDouble(item) => format!( 281 | " #{} = Double {}", 282 | index, 283 | ((item.high_bytes << 8) | item.low_bytes) & 0xFFFF 284 | ), 285 | _ => unimplemented!(), 286 | }; 287 | result.push(rw); 288 | } 289 | write!(f, "{}", result.join("\n")) 290 | } 291 | } 292 | 293 | #[derive(Debug, PartialEq)] 294 | pub enum ConstPoolTag { 295 | ConstantNull = 0, // custom tag for index 0 296 | ConstantClass = 7, 297 | ConstantFieldref = 9, 298 | ConstantMethodref = 10, 299 | ConstantInterfaceMethodref = 11, 300 | ConstantString = 8, 301 | ConstantInteger = 3, 302 | ConstantFloat = 4, 303 | ConstantLong = 5, 304 | ConstantDouble = 6, 305 | ConstantNameAndType = 12, 306 | ConstantUtf8 = 1, 307 | ConstantMethodHandle = 15, 308 | ConstantMethodType = 16, 309 | ConstantInvokeDynamic = 18, 310 | } 311 | 312 | impl From for ConstPoolTag { 313 | fn from(num: usize) -> ConstPoolTag { 314 | match num { 315 | 0 => ConstPoolTag::ConstantNull, 316 | 7 => ConstPoolTag::ConstantClass, 317 | 9 => ConstPoolTag::ConstantFieldref, 318 | 10 => ConstPoolTag::ConstantMethodref, 319 | 11 => ConstPoolTag::ConstantInterfaceMethodref, 320 | 8 => ConstPoolTag::ConstantString, 321 | 3 => ConstPoolTag::ConstantInteger, 322 | 4 => ConstPoolTag::ConstantFloat, 323 | 5 => ConstPoolTag::ConstantLong, 324 | 6 => ConstPoolTag::ConstantDouble, 325 | 12 => ConstPoolTag::ConstantNameAndType, 326 | 1 => ConstPoolTag::ConstantUtf8, 327 | 15 => ConstPoolTag::ConstantMethodHandle, 328 | 16 => ConstPoolTag::ConstantMethodType, 329 | 18 => ConstPoolTag::ConstantInvokeDynamic, 330 | _ => panic!("failed to convert {} to ConstPoolTag", num), 331 | } 332 | } 333 | } 334 | 335 | #[derive(Debug, PartialEq)] 336 | pub enum ConstPoolItem { 337 | ConstantNull, 338 | ConstantClass(ConstantClass), 339 | ConstantFieldref(ConstantFieldref), 340 | ConstantMethodref(ConstantMethodref), 341 | ConstantInterfaceMethodref, 342 | ConstantString(ConstantString), 343 | ConstantInteger, 344 | ConstantFloat(ConstantFloat), 345 | ConstantLong(ConstantLong), 346 | ConstantDouble(ConstantDouble), 347 | ConstantNameAndType(ConstantNameAndType), 348 | ConstantUtf8(ConstantUtf8), 349 | ConstantMethodHandle, 350 | ConstantMethodType, 351 | ConstantInvokeDynamic, 352 | } 353 | 354 | #[derive(Debug, PartialEq)] 355 | pub struct ConstantLong { 356 | pub tag: ConstPoolTag, 357 | pub high_bytes: usize, // u4 358 | pub low_bytes: usize, // u4 359 | } 360 | 361 | impl ConstantLong { 362 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantLong, usize) { 363 | let (high_bytes, index) = extract_x_byte_as_usize(inputs, index, 4); 364 | let (low_bytes, index) = extract_x_byte_as_usize(inputs, index, 4); 365 | ( 366 | ConstantLong { 367 | tag: ConstPoolTag::ConstantString, 368 | high_bytes, 369 | low_bytes, 370 | }, 371 | index, 372 | ) 373 | } 374 | } 375 | 376 | #[derive(Debug, PartialEq)] 377 | pub struct ConstantDouble { 378 | pub tag: ConstPoolTag, 379 | pub high_bytes: usize, // u4 380 | pub low_bytes: usize, // u4 381 | } 382 | 383 | impl ConstantDouble { 384 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantDouble, usize) { 385 | let (high_bytes, index) = extract_x_byte_as_usize(inputs, index, 4); 386 | let (low_bytes, index) = extract_x_byte_as_usize(inputs, index, 4); 387 | ( 388 | ConstantDouble { 389 | tag: ConstPoolTag::ConstantString, 390 | high_bytes, 391 | low_bytes, 392 | }, 393 | index, 394 | ) 395 | } 396 | } 397 | 398 | #[derive(Debug, PartialEq)] 399 | pub struct ConstantString { 400 | pub tag: ConstPoolTag, 401 | pub string_index: usize, // u2 402 | } 403 | 404 | impl ConstantString { 405 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantString, usize) { 406 | let (string_index, index) = extract_x_byte_as_usize(inputs, index, 2); 407 | ( 408 | ConstantString { 409 | tag: ConstPoolTag::ConstantString, 410 | string_index, 411 | }, 412 | index, 413 | ) 414 | } 415 | } 416 | 417 | #[derive(Debug, PartialEq)] 418 | pub struct ConstantFloat { 419 | pub tag: ConstPoolTag, 420 | pub bytes: usize, // u4 421 | } 422 | 423 | impl ConstantFloat { 424 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantFloat, usize) { 425 | let (bytes, index) = extract_x_byte_as_usize(inputs, index, 4); 426 | ( 427 | ConstantFloat { 428 | tag: ConstPoolTag::ConstantFloat, 429 | bytes, 430 | }, 431 | index, 432 | ) 433 | } 434 | } 435 | 436 | #[derive(Debug, PartialEq)] 437 | pub struct ConstantFieldref { 438 | pub tag: ConstPoolTag, 439 | pub class_index: usize, // u2 440 | pub name_and_type_index: usize, // u2 441 | } 442 | 443 | impl ConstantFieldref { 444 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantFieldref, usize) { 445 | let (class_index, index) = extract_x_byte_as_usize(inputs, index, 2); 446 | let (name_and_type_index, index) = extract_x_byte_as_usize(inputs, index, 2); 447 | ( 448 | ConstantFieldref { 449 | tag: ConstPoolTag::ConstantFieldref, 450 | class_index, 451 | name_and_type_index, 452 | }, 453 | index, 454 | ) 455 | } 456 | } 457 | 458 | #[derive(Debug, PartialEq)] 459 | pub struct ConstantNameAndType { 460 | pub tag: ConstPoolTag, 461 | pub name_index: usize, // u2 462 | pub descriptor_index: usize, // u2 463 | } 464 | 465 | impl ConstantNameAndType { 466 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantNameAndType, usize) { 467 | let (name_index, index) = extract_x_byte_as_usize(inputs, index, 2); 468 | let (descriptor_index, index) = extract_x_byte_as_usize(inputs, index, 2); 469 | 470 | ( 471 | ConstantNameAndType { 472 | tag: ConstPoolTag::ConstantNameAndType, 473 | name_index, 474 | descriptor_index, 475 | }, 476 | index, 477 | ) 478 | } 479 | } 480 | 481 | #[derive(Debug, PartialEq)] 482 | pub struct ConstantClass { 483 | pub tag: ConstPoolTag, 484 | pub name_index: usize, // u2 485 | } 486 | 487 | impl ConstantClass { 488 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantClass, usize) { 489 | let (name_index, index) = extract_x_byte_as_usize(inputs, index, 2); 490 | ( 491 | ConstantClass { 492 | tag: ConstPoolTag::ConstantClass, 493 | name_index, 494 | }, 495 | index, 496 | ) 497 | } 498 | } 499 | 500 | #[derive(Debug, PartialEq)] 501 | pub struct ConstantMethodref { 502 | pub tag: ConstPoolTag, 503 | pub class_index: usize, // u2 504 | pub name_and_type_index: usize, // u2 505 | } 506 | 507 | impl ConstantMethodref { 508 | pub fn create_and_update_index(inputs: &[u8], index: usize) -> (ConstantMethodref, usize) { 509 | let (class_index, index) = extract_x_byte_as_usize(inputs, index, 2); 510 | let (name_and_type_index, index) = extract_x_byte_as_usize(inputs, index, 2); 511 | 512 | ( 513 | ConstantMethodref { 514 | tag: ConstPoolTag::ConstantMethodref, 515 | class_index, 516 | name_and_type_index, 517 | }, 518 | index, 519 | ) 520 | } 521 | } 522 | 523 | #[derive(Debug, PartialEq)] 524 | pub struct ConstantUtf8 { 525 | pub id: usize, // custom value 526 | pub tag: ConstPoolTag, 527 | pub length: usize, // u2 528 | pub bytes: Vec, 529 | } 530 | 531 | impl ConstantUtf8 { 532 | pub fn create_and_update_index( 533 | string_map: &mut StringPool, 534 | inputs: &[u8], 535 | index: usize, 536 | ) -> (ConstantUtf8, usize) { 537 | let (utf8_length, index) = extract_x_byte_as_usize(inputs, index, 2); 538 | let (bytes, index) = extract_x_byte_as_vec(inputs, index, utf8_length); 539 | let value = String::from_utf8_lossy(bytes.as_slice()); 540 | let id = string_map.insert(value.to_string()); 541 | 542 | ( 543 | ConstantUtf8 { 544 | id, 545 | tag: ConstPoolTag::ConstantUtf8, 546 | length: utf8_length, 547 | bytes, 548 | }, 549 | index, 550 | ) 551 | } 552 | } 553 | 554 | #[cfg(test)] 555 | mod test { 556 | use super::*; 557 | #[test] 558 | fn constant_pool_constant_methodref() { 559 | let mut inputs = vec![ 560 | 0x0a, // class 561 | 0x00, 0x0a, // class_index 562 | 0x00, 0x0b, // name_and_type_index 563 | ]; 564 | 565 | let result = ConstantPool::new(&mut StringPool::new(), &mut inputs, 0, 2); 566 | 567 | assert_eq!( 568 | result, 569 | ( 570 | ConstantPool(vec![ 571 | ConstPoolItem::ConstantNull, 572 | ConstPoolItem::ConstantMethodref(ConstantMethodref { 573 | tag: ConstPoolTag::ConstantMethodref, 574 | class_index: 0x0a, 575 | name_and_type_index: 0x0b 576 | }) 577 | ]), 578 | inputs.len() 579 | ) 580 | ); 581 | } 582 | 583 | #[test] 584 | fn constant_pool_constant_class() { 585 | let mut inputs = vec![ 586 | 0x07, // class 587 | 0x00, 0x0b, // name_index 588 | ]; 589 | let result = ConstantPool::new(&mut StringPool::new(), &mut inputs, 0, 2); 590 | 591 | assert_eq!( 592 | result, 593 | ( 594 | ConstantPool(vec![ 595 | ConstPoolItem::ConstantNull, 596 | ConstPoolItem::ConstantClass(ConstantClass { 597 | tag: ConstPoolTag::ConstantClass, 598 | name_index: 0x0b 599 | }) 600 | ]), 601 | inputs.len() 602 | ) 603 | ); 604 | } 605 | 606 | // #[test] 607 | // fn constant_pool_utf8() { 608 | // let mut inputs = [ 609 | // 0x01, // utf8 610 | // 0x00, 0x0A, // length 611 | // 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, // bytes(SourceFile) 612 | // ]; 613 | // let result = ConstantPool::new(&mut inputs, 0, 2); 614 | 615 | // assert_eq!( 616 | // result, 617 | // ( 618 | // ConstantPool(vec![ 619 | // ConstPoolItem::ConstantNull, 620 | // ConstPoolItem::ConstantUtf8(ConstantUtf8 { 621 | 622 | // tag: ConstPoolTag::ConstantUtf8, 623 | // length: 0x0a, 624 | // bytes: vec![0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65] 625 | // }) 626 | // ]), 627 | // inputs.len() 628 | // ) 629 | // ); 630 | // } 631 | 632 | #[test] 633 | fn constant_pool_name_and_type() { 634 | let mut inputs = vec![ 635 | 0x0c, // name_and_type 636 | 0x00, 0x0a, // name_index 637 | 0x00, 0x0b, // descriptor_index 638 | ]; 639 | let result = ConstantPool::new(&mut StringPool::new(), &mut inputs, 0, 2); 640 | 641 | assert_eq!( 642 | result, 643 | ( 644 | ConstantPool(vec![ 645 | ConstPoolItem::ConstantNull, 646 | ConstPoolItem::ConstantNameAndType(ConstantNameAndType { 647 | tag: ConstPoolTag::ConstantNameAndType, 648 | name_index: 0x0a, 649 | descriptor_index: 0x0b 650 | }) 651 | ]), 652 | inputs.len() 653 | ) 654 | ); 655 | } 656 | 657 | #[test] 658 | fn constant_pool_name_and_type_print() { 659 | let mut inputs = vec![ 660 | 0x0c, // name_and_type 661 | 0x00, 0x0a, // name_index 662 | 0x00, 0x0b, // descriptor_index 663 | ]; 664 | let (constant_pool, _) = ConstantPool::new(&mut StringPool::new(), &mut inputs, 0, 2); 665 | 666 | assert_eq!( 667 | format!("{}", constant_pool), 668 | " #1 = NameAndType #10:#11" 669 | ); 670 | } 671 | } 672 | -------------------------------------------------------------------------------- /src/field.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute::defs::Attribute; 2 | use crate::utils::extract_x_byte_as_usize; 3 | use std::fmt; 4 | 5 | #[derive(Debug)] 6 | pub struct Field { 7 | pub access_flags: FieldAccessFlags, // u2 8 | pub name_index: usize, // u2 9 | pub descriptor_index: usize, // u2 10 | pub attributes_count: usize, // u2 11 | pub attribute_info: Vec, 12 | } 13 | 14 | impl Field { 15 | pub fn new(inputs: &[u8], index: usize) -> (Field, usize) { 16 | let (access_flags, index) = extract_x_byte_as_usize(inputs, index, 2); 17 | let access_flags = extract_access_flags(access_flags); 18 | 19 | let (name_index, index) = extract_x_byte_as_usize(inputs, index, 2); 20 | let (descriptor_index, index) = extract_x_byte_as_usize(inputs, index, 2); 21 | let (attributes_count, index) = extract_x_byte_as_usize(inputs, index, 2); 22 | 23 | ( 24 | Field { 25 | access_flags, 26 | name_index, 27 | descriptor_index, 28 | attributes_count, 29 | attribute_info: vec![], 30 | }, 31 | index, 32 | ) 33 | } 34 | } 35 | 36 | impl fmt::Display for Field { 37 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 38 | let mut attribute_strs = Vec::with_capacity(self.attributes_count); 39 | for item in self.attribute_info.iter() { 40 | attribute_strs.push(format!("{}", item)); 41 | } 42 | 43 | write!( 44 | f, 45 | " name: #{} 46 | descriptor: #{} 47 | flags: {} 48 | attribute: 49 | {}", 50 | self.name_index, 51 | self.descriptor_index, 52 | self.access_flags, 53 | attribute_strs.join("\n ") 54 | ) 55 | } 56 | } 57 | 58 | #[derive(Debug)] 59 | pub enum FieldDescriptor { 60 | BaseType(BaseType), 61 | // L ClassName ; reference an instance of class ClassName 62 | ObjectType(String), 63 | ArrayType(Box), 64 | } 65 | 66 | #[derive(Debug)] 67 | pub enum BaseType { 68 | B, // byte 69 | C, // char 70 | D, // double 71 | F, // float 72 | I, // int 73 | J, // long 74 | S, // short 75 | Z, // boolean 76 | } 77 | 78 | impl From<&str> for FieldDescriptor { 79 | fn from(input: &str) -> FieldDescriptor { 80 | match &input[0..1] { 81 | "B" => FieldDescriptor::BaseType(BaseType::B), 82 | "C" => FieldDescriptor::BaseType(BaseType::C), 83 | "D" => FieldDescriptor::BaseType(BaseType::D), 84 | "F" => FieldDescriptor::BaseType(BaseType::F), 85 | "I" => FieldDescriptor::BaseType(BaseType::I), 86 | "J" => FieldDescriptor::BaseType(BaseType::J), 87 | "S" => FieldDescriptor::BaseType(BaseType::S), 88 | "Z" => FieldDescriptor::BaseType(BaseType::Z), 89 | "L" => FieldDescriptor::ObjectType(input[1..].to_string()), 90 | "[" => unimplemented!("need to implement for Array"), 91 | _ => panic!("failed to convert {} to FieldDescriptor", input), 92 | } 93 | } 94 | } 95 | 96 | fn extract_access_flags(num: usize) -> FieldAccessFlags { 97 | let mut access_flags = vec![]; 98 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccPublic); 99 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccPrivate); 100 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccProtected); 101 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccStatic); 102 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccFinal); 103 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccVolatitle); 104 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccTransient); 105 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccSynthetic); 106 | crate::add_flags!(&mut access_flags, num, FieldAccessFlag::AccEnum); 107 | 108 | FieldAccessFlags(access_flags) 109 | } 110 | 111 | #[derive(Debug)] 112 | pub struct FieldAccessFlags(Vec); 113 | impl fmt::Display for FieldAccessFlags { 114 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 115 | let mut result = Vec::with_capacity(self.0.len()); 116 | for item in self.0.iter() { 117 | result.push(format!("{}", item)); 118 | } 119 | write!(f, "{}", result.join(", ")) 120 | } 121 | } 122 | 123 | #[derive(Debug)] 124 | pub enum FieldAccessFlag { 125 | Unknown = 0x0000, 126 | AccPublic = 0x0001, 127 | AccPrivate = 0x0002, 128 | AccProtected = 0x0004, 129 | AccStatic = 0x0008, 130 | AccFinal = 0x0010, 131 | AccVolatitle = 0x0040, 132 | AccTransient = 0x0080, 133 | AccSynthetic = 0x1000, 134 | AccEnum = 0x4000, 135 | } 136 | 137 | impl fmt::Display for FieldAccessFlag { 138 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 139 | match self { 140 | FieldAccessFlag::Unknown => write!(f, ""), 141 | FieldAccessFlag::AccPublic => write!(f, "ACC_PUBLIC"), 142 | FieldAccessFlag::AccPrivate => write!(f, "ACC_PRIVATE"), 143 | FieldAccessFlag::AccProtected => write!(f, "ACC_PROTECTED"), 144 | FieldAccessFlag::AccStatic => write!(f, "ACC_STATIC"), 145 | FieldAccessFlag::AccFinal => write!(f, "ACC_FINAL"), 146 | FieldAccessFlag::AccVolatitle => write!(f, "ACC_VOLATITLE"), 147 | FieldAccessFlag::AccTransient => write!(f, "ACC_TRANSIENT"), 148 | FieldAccessFlag::AccSynthetic => write!(f, "ACC_SYNTHETIC"), 149 | FieldAccessFlag::AccEnum => write!(f, "ACC_ENUM"), 150 | } 151 | } 152 | } 153 | 154 | impl From for FieldAccessFlag { 155 | fn from(num: usize) -> FieldAccessFlag { 156 | match num { 157 | 0x0000 => FieldAccessFlag::Unknown, // custom 158 | 0x0001 => FieldAccessFlag::AccPublic, 159 | 0x0002 => FieldAccessFlag::AccPrivate, 160 | 0x0004 => FieldAccessFlag::AccProtected, 161 | 0x0008 => FieldAccessFlag::AccStatic, 162 | 0x0010 => FieldAccessFlag::AccFinal, 163 | 0x0040 => FieldAccessFlag::AccVolatitle, 164 | 0x0080 => FieldAccessFlag::AccTransient, 165 | 0x1000 => FieldAccessFlag::AccSynthetic, 166 | 0x4000 => FieldAccessFlag::AccEnum, 167 | _ => panic!("failed to convert {} to FieldAccessFlag", num), 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/java_class.rs: -------------------------------------------------------------------------------- 1 | pub mod builtin; 2 | pub mod custom; 3 | pub mod default; 4 | 5 | #[derive(Debug)] 6 | pub enum JavaClass { 7 | BuiltIn(builtin::BuiltIn), 8 | Custom(custom::Custom), 9 | } 10 | 11 | impl JavaClass { 12 | pub fn this_class_name(&self) -> usize { 13 | match self { 14 | JavaClass::BuiltIn(builtin) => builtin.class_name, 15 | JavaClass::Custom(custom) => custom.this_class_name(), 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/java_class/builtin.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::constant::ConstantPool; 4 | use crate::operand::Item; 5 | use crate::stackframe::Stackframe; 6 | use crate::string_pool::StringPool; 7 | use crate::wasm::print_log; 8 | 9 | #[derive(Debug)] 10 | pub struct BuiltIn { 11 | pub class_name: usize, 12 | pub methods: HashMap, 13 | } 14 | 15 | impl BuiltIn { 16 | pub fn new(class_name: usize) -> BuiltIn { 17 | BuiltIn { 18 | class_name, 19 | methods: HashMap::new(), 20 | } 21 | } 22 | } 23 | 24 | #[derive(Debug)] 25 | pub enum BuiltInLocal { 26 | Println, 27 | } 28 | 29 | #[derive(Debug)] 30 | pub struct BuiltInMethod { 31 | pub name: usize, 32 | pub code_type: BuitlInCodeType, 33 | } 34 | 35 | impl BuiltInMethod { 36 | pub fn new(name: usize, code_type: BuitlInCodeType) -> BuiltInMethod { 37 | BuiltInMethod { name, code_type } 38 | } 39 | 40 | pub fn parameter_length(&self, string_map: &mut StringPool, descriptor: usize) -> usize { 41 | let descriptor = string_map.get_value(&descriptor); 42 | match self.code_type { 43 | BuitlInCodeType::Println => match descriptor.as_ref() { 44 | "(J)V" | "(D)V" => 2, 45 | _ => 1, 46 | }, 47 | BuitlInCodeType::JavaLangSystemInit 48 | | BuitlInCodeType::JavaLangObjectInit 49 | | BuitlInCodeType::JavaLangObjectToString => 1, 50 | } 51 | } 52 | 53 | pub fn execute( 54 | &mut self, 55 | string_map: &mut StringPool, 56 | constant_pool: &ConstantPool, 57 | stackframes: &mut Vec, 58 | ) { 59 | let mut stackframe = stackframes.pop().expect("should has stack_frame"); 60 | match self.code_type { 61 | BuitlInCodeType::Println => { 62 | if let Some(item) = stackframe.local_variables.get(0) { 63 | match item { 64 | Item::Fieldref(index) => { 65 | let value = 66 | string_map.get_value(&constant_pool.get_fieldref_as_utf8(*index)); 67 | print_log(&format!("{}", value)); 68 | } 69 | Item::String(id) => { 70 | let value = string_map.get_value(id); 71 | print_log(&value); 72 | } 73 | Item::Int(value) => { 74 | print_log(&format!("{}", value)); 75 | } 76 | Item::Long(second) => { 77 | if let Some(Item::Long(first)) = stackframe.local_variables.get(1) { 78 | let value: u64 = (((*first as u64) << 32) as u64) | *second as u64; 79 | let value = if value > 0x7fffffffffffffff { 80 | -1 * ((value ^ 0xffffffffffffffff) + 1) as i64 81 | } else { 82 | value as i64 83 | }; 84 | print_log(&format!("{}", value)); 85 | } else { 86 | unreachable!("should exist long second item") 87 | } 88 | let _ = stackframe.operand_stack.stack.pop(); 89 | } 90 | // TBD should fix to output value correctly 91 | Item::Objectref(object_ref) => { 92 | print_log(&format!("objectref: {}", object_ref)); 93 | } 94 | Item::Float(value) => { 95 | print_log(&format!("{}", value)); 96 | } 97 | Item::Double(second) => { 98 | if let Some(Item::Double(first)) = stackframe.local_variables.get(1) { 99 | let value: u64 = (((*first as u64) << 32) as u64) | *second as u64; 100 | let s: i64 = if value >> 63 == 0 as u64 { 1 } else { -1 }; 101 | let e = (value >> 52 as i32) & 0x7ff; 102 | let m = if e == 0 { 103 | ((value & 0xfffffffffffff) << 1) as i64 104 | } else { 105 | ((value & 0xfffffffffffff) | 0x10000000000000) as i64 106 | }; 107 | print_log(&format!( 108 | "{}", 109 | (s * m) as f64 * f64::powf(2.0f64, e as f64 - 1075 as f64) 110 | )); 111 | } else { 112 | unreachable!("should exist long second item") 113 | } 114 | let _ = stackframe.operand_stack.stack.pop(); 115 | } 116 | _ => unimplemented!(), 117 | }; 118 | let _ = stackframe.operand_stack.stack.pop(); 119 | } else { 120 | unreachable!("should have a argument for println") 121 | } 122 | } 123 | BuitlInCodeType::JavaLangSystemInit | BuitlInCodeType::JavaLangObjectInit => {} 124 | BuitlInCodeType::JavaLangObjectToString => { 125 | let val = if let Some(Item::Int(val)) = stackframe.local_variables.get(0) { 126 | val 127 | } else { 128 | unreachable!("should have a argument for toString") 129 | }; 130 | let stackframe = stackframes.last_mut().expect("should exist stackframe"); 131 | let string_id = string_map.insert(val.to_string()); 132 | stackframe.operand_stack.stack.push(Item::String(string_id)); 133 | } 134 | } 135 | } 136 | } 137 | 138 | #[derive(Debug)] 139 | pub enum BuitlInCodeType { 140 | Println, 141 | JavaLangObjectInit, 142 | JavaLangSystemInit, 143 | JavaLangObjectToString, 144 | } 145 | -------------------------------------------------------------------------------- /src/java_class/custom.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute::code::Code; 2 | use crate::attribute::defs::Attribute; 3 | use crate::constant::ConstantPool; 4 | use crate::field::{Field, FieldDescriptor}; 5 | use crate::method::{Method, MethodAccessFlag}; 6 | use crate::string_pool::StringPool; 7 | use crate::utils::*; 8 | use std::fmt; 9 | 10 | #[derive(Debug)] 11 | pub struct Interface(usize); 12 | 13 | #[derive(Debug)] 14 | pub struct Custom { 15 | pub magic: u32, // u4 16 | pub minor_version: u16, // u2 17 | pub major_version: u16, // u2 18 | pub constant_pool_count: usize, // u2 19 | pub cp_info: ConstantPool, // cp_info constant_pool[constant_pool_count-1]; 20 | pub access_flags: AccessFlags, // u2 21 | pub this_class: usize, // u2 22 | pub super_class: usize, // u2 23 | pub interfaces_count: usize, // u2 24 | pub interfaces: Vec, // u2 interfaces[interfaces_count]; 25 | pub fields_count: usize, // u2 26 | pub fields: Vec, // field_info fields[fields_count]; 27 | pub methods_count: usize, // u2 28 | pub methods: Vec, // method_info methods[methods_count]; 29 | pub attributes_count: usize, // u2 30 | pub attributes: Vec, // attribute_info attributes[attributes_count]; 31 | } 32 | 33 | impl Custom { 34 | pub fn new(string_pool: &mut StringPool, input: &[u8], index: usize) -> (Custom, usize) { 35 | let (magic, index) = extract_x_byte_as_usize(input, index, 4); 36 | let magic = magic as u32; 37 | 38 | let (minor_version, index) = extract_x_byte_as_usize(input, index, 2); 39 | let minor_version = minor_version as u16; 40 | let (major_version, index) = extract_x_byte_as_usize(input, index, 2); 41 | let major_version = major_version as u16; 42 | 43 | let (constant_pool_count, index) = extract_x_byte_as_usize(input, index, 2); 44 | let (cp_info, index) = ConstantPool::new(string_pool, input, index, constant_pool_count); 45 | 46 | let (access_flags_num, index) = extract_x_byte_as_usize(input, index, 2); 47 | let access_flags = extract_access_flags(access_flags_num); 48 | 49 | let (this_class, index) = extract_x_byte_as_usize(input, index, 2); 50 | let (super_class, index) = extract_x_byte_as_usize(input, index, 2); 51 | 52 | let (interfaces_count, mut index) = extract_x_byte_as_usize(input, index, 2); 53 | let mut interfaces = Vec::with_capacity(interfaces_count); 54 | for _ in 0..interfaces_count { 55 | let (interface_index, updated_index) = extract_x_byte_as_usize(input, index, 2); 56 | index = updated_index; 57 | interfaces.push(Interface(interface_index)); 58 | } 59 | 60 | let (fields_count, mut index) = extract_x_byte_as_usize(input, index, 2); 61 | let mut fields = Vec::with_capacity(fields_count); 62 | for _ in 0..fields_count { 63 | let (field, updated_index) = Field::new(input, index); 64 | index = updated_index; 65 | fields.push(field); 66 | } 67 | 68 | let (methods_count, mut index) = extract_x_byte_as_usize(input, index, 2); 69 | let mut methods = Vec::with_capacity(methods_count); 70 | for _ in 0..methods_count { 71 | let (method, updated_index) = Method::new(string_pool, &cp_info, input, index); 72 | index = updated_index; 73 | methods.push(method); 74 | } 75 | 76 | let (attributes_count, mut index) = extract_x_byte_as_usize(input, index, 2); 77 | let mut attributes = Vec::with_capacity(attributes_count); 78 | 79 | for _ in 0..attributes_count { 80 | let (attribute, updated_index) = Attribute::new(string_pool, &cp_info, input, index); 81 | index = updated_index; 82 | attributes.push(attribute); 83 | } 84 | ( 85 | Custom { 86 | magic, 87 | minor_version, 88 | major_version, 89 | constant_pool_count, 90 | cp_info, 91 | access_flags, 92 | this_class, 93 | super_class, 94 | interfaces_count, 95 | interfaces, 96 | fields_count, 97 | fields, 98 | methods_count, 99 | methods, 100 | attributes_count, 101 | attributes, 102 | }, 103 | index, 104 | ) 105 | } 106 | 107 | pub fn get_entry_method(&self) -> Option<&Method> { 108 | if let Some(main_index) = self.cp_info.get_main_index() { 109 | return self.methods.iter().find(|method| { 110 | method 111 | .access_flags 112 | .0 113 | .iter() 114 | .find(|flag| **flag == MethodAccessFlag::AccPublic) 115 | .is_some() 116 | && method.name_index == main_index 117 | }); 118 | } 119 | panic!( 120 | "failed to find main method in this class. the detail is below 121 | {}", 122 | self 123 | ); 124 | } 125 | 126 | pub fn get_clinit_code(&self) -> Option<&Code> { 127 | if let Some(clinit_index) = self.cp_info.get_clinit_index() { 128 | let method_option = self.methods.iter().find(|method| { 129 | method 130 | .access_flags 131 | .0 132 | .iter() 133 | .find(|flag| **flag == MethodAccessFlag::AccStatic) 134 | .is_some() 135 | && method.name_index == clinit_index 136 | }); 137 | if let Some(method) = method_option { 138 | if let Some(Attribute::Code(code)) = method.attribute_info.iter().find(|attr| { 139 | if let Attribute::Code(_) = attr { 140 | true 141 | } else { 142 | false 143 | } 144 | }) { 145 | Some(code) 146 | } else { 147 | None 148 | } 149 | } else { 150 | None 151 | } 152 | } else { 153 | None 154 | } 155 | } 156 | 157 | pub fn this_class_name(&self) -> usize { 158 | let class_ref = self.cp_info.get_class_ref(self.this_class); 159 | self.cp_info.get_utf8(class_ref.name_index) 160 | } 161 | 162 | pub fn get_method(&self, name_index: usize, descriptor_index: usize) -> Option<&Method> { 163 | self.methods 164 | .iter() 165 | .find(|item| item.name_index == name_index && item.descriptor_index == descriptor_index) 166 | } 167 | 168 | pub fn get_method_by_string(&self, name: usize, descriptor: usize) -> Option<&Method> { 169 | self.methods.iter().find(|item| { 170 | (self.cp_info.get_utf8(item.name_index) == name) 171 | && (self.cp_info.get_utf8(item.descriptor_index) == descriptor) 172 | }) 173 | } 174 | 175 | pub fn get_method_code(&self, name_index: usize, descriptor_index: usize) -> Option<&Code> { 176 | if let Some(method) = self.get_method(name_index, descriptor_index) { 177 | if let Some(Attribute::Code(code)) = method.attribute_info.iter().find(|attr| { 178 | if let Attribute::Code(_) = attr { 179 | true 180 | } else { 181 | false 182 | } 183 | }) { 184 | Some(code) 185 | } else { 186 | None 187 | } 188 | } else { 189 | unreachable!( 190 | "method name: {} descriptor: {} is not found", 191 | name_index, descriptor_index 192 | ) 193 | } 194 | } 195 | 196 | pub fn get_method_code_by_string(&self, name: usize, descriptor: usize) -> Option<&Code> { 197 | if let Some(method) = self.get_method_by_string(name, descriptor) { 198 | if let Some(Attribute::Code(code)) = method.attribute_info.iter().find(|attr| { 199 | if let Attribute::Code(_) = attr { 200 | true 201 | } else { 202 | false 203 | } 204 | }) { 205 | Some(code) 206 | } else { 207 | None 208 | } 209 | } else { 210 | unreachable!( 211 | "method name: {} descriptor: {} is not found", 212 | name, descriptor 213 | ) 214 | } 215 | } 216 | 217 | pub fn get_descriptor( 218 | &self, 219 | string_map: &mut StringPool, 220 | descriptor_index: usize, 221 | ) -> FieldDescriptor { 222 | let descriptor_str = string_map.get_value(&self.cp_info.get_utf8(descriptor_index)); 223 | FieldDescriptor::from(descriptor_str.as_ref()) 224 | } 225 | } 226 | 227 | impl fmt::Display for Custom { 228 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 229 | let mut field_strs = Vec::with_capacity(self.fields_count); 230 | for item in self.fields.iter() { 231 | field_strs.push(format!("{}", item)); 232 | } 233 | 234 | let mut method_strs = Vec::with_capacity(self.methods_count); 235 | for item in self.methods.iter() { 236 | method_strs.push(format!("{}", item)); 237 | } 238 | 239 | let mut attribute_strs = Vec::with_capacity(self.attributes_count); 240 | for item in self.attributes.iter() { 241 | attribute_strs.push(format!("{}", item)); 242 | } 243 | write!( 244 | f, 245 | "minor version: {} 246 | major version: {} 247 | flags: {} 248 | Constant pool: 249 | {} 250 | 251 | Fields: 252 | {} 253 | Methods: 254 | {} 255 | 256 | Attributes: 257 | {}", 258 | self.minor_version, 259 | self.major_version, 260 | self.access_flags, 261 | self.cp_info, 262 | field_strs.join("\n"), 263 | method_strs.join("\n\n"), 264 | attribute_strs.join("\n ") 265 | ) 266 | } 267 | } 268 | 269 | fn extract_access_flags(num: usize) -> AccessFlags { 270 | let mut access_flags = vec![]; 271 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccPublic); 272 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccFinal); 273 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccSuper); 274 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccInterface); 275 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccAbstract); 276 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccSynthetic); 277 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccAnnotation); 278 | crate::add_flags!(&mut access_flags, num, AccessFlag::AccEnum); 279 | 280 | AccessFlags(access_flags) 281 | } 282 | 283 | #[derive(Debug)] 284 | pub enum AccessFlag { 285 | AccPublic = 0x0001, 286 | AccFinal = 0x0010, 287 | AccSuper = 0x0020, 288 | AccInterface = 0x0200, 289 | AccAbstract = 0x0400, 290 | AccSynthetic = 0x1000, 291 | AccAnnotation = 0x2000, 292 | AccEnum = 0x4000, 293 | } 294 | 295 | impl fmt::Display for AccessFlag { 296 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 297 | match self { 298 | AccessFlag::AccPublic => write!(f, "ACC_PUBLIC"), 299 | AccessFlag::AccFinal => write!(f, "ACC_FINAL"), 300 | AccessFlag::AccSuper => write!(f, "ACC_SUPER"), 301 | AccessFlag::AccInterface => write!(f, "ACC_INTERFACE"), 302 | AccessFlag::AccAbstract => write!(f, "ACC_ABSTRACT"), 303 | AccessFlag::AccSynthetic => write!(f, "ACC_SYNTHETIC"), 304 | AccessFlag::AccAnnotation => write!(f, "ACC_ANNOTATION"), 305 | AccessFlag::AccEnum => write!(f, "ACC_ENUM"), 306 | } 307 | } 308 | } 309 | 310 | impl From for AccessFlag { 311 | fn from(num: usize) -> AccessFlag { 312 | match num { 313 | 0x0001 => AccessFlag::AccPublic, 314 | 0x0010 => AccessFlag::AccFinal, 315 | 0x0020 => AccessFlag::AccSuper, 316 | 0x0200 => AccessFlag::AccInterface, 317 | 0x0400 => AccessFlag::AccAbstract, 318 | 0x1000 => AccessFlag::AccSynthetic, 319 | 0x2000 => AccessFlag::AccAnnotation, 320 | 0x4000 => AccessFlag::AccEnum, 321 | _ => panic!("failed to convert {} to AccessFlag", num), 322 | } 323 | } 324 | } 325 | 326 | #[derive(Debug)] 327 | pub struct AccessFlags(Vec); 328 | impl fmt::Display for AccessFlags { 329 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 330 | let mut result = Vec::with_capacity(self.0.len()); 331 | for item in self.0.iter() { 332 | result.push(format!("{}", item)); 333 | } 334 | write!(f, "flags: {}", result.join(", ")) 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/java_class/default.rs: -------------------------------------------------------------------------------- 1 | use crate::java_class::{ 2 | builtin::{BuiltIn, BuiltInMethod, BuitlInCodeType}, 3 | JavaClass, 4 | }; 5 | use crate::string_pool::StringPool; 6 | use std::collections::HashMap; 7 | 8 | pub fn setup_class_map(string_pool: &mut StringPool) -> HashMap { 9 | let mut class_map = HashMap::new(); 10 | let (print_stream_name, print_stream) = create_print_stream(string_pool); 11 | let (java_lang_object_name, java_lang_object) = create_java_lang_object(string_pool); 12 | let (java_lang_integer_name, java_lang_integer) = create_java_lang_integer(string_pool); 13 | let (java_lang_system_name, java_lang_system) = create_java_lang_system(string_pool); 14 | 15 | class_map.insert(print_stream_name, print_stream); 16 | class_map.insert(java_lang_object_name, java_lang_object); 17 | class_map.insert(java_lang_integer_name, java_lang_integer); 18 | class_map.insert(java_lang_system_name, java_lang_system); 19 | class_map 20 | } 21 | 22 | fn create_print_stream(string_pool: &mut StringPool) -> (usize, JavaClass) { 23 | let class_name_id = string_pool.insert(String::from("java/io/PrintStream")); 24 | let mut print_stream = BuiltIn::new(class_name_id); 25 | let println_name_id = string_pool.insert(String::from("println")); 26 | let println = BuiltInMethod::new(println_name_id, BuitlInCodeType::Println); 27 | print_stream.methods.insert(println_name_id, println); 28 | (class_name_id, JavaClass::BuiltIn(print_stream)) 29 | } 30 | 31 | fn create_java_lang_object(string_pool: &mut StringPool) -> (usize, JavaClass) { 32 | let java_lang_object_name_id = string_pool.insert(String::from("java/lang/Object")); 33 | let mut java_lang_object = BuiltIn::new(java_lang_object_name_id); 34 | let init_name_id = string_pool.insert(String::from("")); 35 | let init = BuiltInMethod::new(init_name_id, BuitlInCodeType::JavaLangObjectInit); 36 | java_lang_object.methods.insert(init_name_id, init); 37 | ( 38 | java_lang_object_name_id, 39 | JavaClass::BuiltIn(java_lang_object), 40 | ) 41 | } 42 | 43 | fn create_java_lang_system(string_pool: &mut StringPool) -> (usize, JavaClass) { 44 | let java_lang_system_name_id = string_pool.insert(String::from("java/lang/System")); 45 | let mut java_lang_system = BuiltIn::new(java_lang_system_name_id); 46 | let init_name_id = string_pool.insert(String::from("")); 47 | let init = BuiltInMethod::new(init_name_id, BuitlInCodeType::JavaLangSystemInit); 48 | java_lang_system.methods.insert(init_name_id, init); 49 | ( 50 | java_lang_system_name_id, 51 | JavaClass::BuiltIn(java_lang_system), 52 | ) 53 | } 54 | 55 | fn create_java_lang_integer(string_pool: &mut StringPool) -> (usize, JavaClass) { 56 | let java_lang_integer_name_id = string_pool.insert(String::from("java/lang/Integer")); 57 | let mut java_lang_integer = BuiltIn::new(java_lang_integer_name_id); 58 | let to_string_name_id = string_pool.insert(String::from("toString")); 59 | let to_string = BuiltInMethod::new(to_string_name_id, BuitlInCodeType::JavaLangObjectToString); 60 | java_lang_integer 61 | .methods 62 | .insert(to_string_name_id, to_string); 63 | ( 64 | java_lang_integer_name_id, 65 | JavaClass::BuiltIn(java_lang_integer), 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(exclusive_range_pattern)] 2 | #![allow(dead_code)] 3 | 4 | mod array; 5 | mod attribute; 6 | mod constant; 7 | mod context; 8 | mod field; 9 | mod java_class; 10 | mod method; 11 | mod object; 12 | mod operand; 13 | mod option; 14 | mod order; 15 | mod stackframe; 16 | mod string_pool; 17 | mod utils; 18 | mod wasm; 19 | 20 | use crate::context::Context; 21 | use crate::java_class::{custom::Custom, default::setup_class_map}; 22 | use crate::string_pool::StringPool; 23 | 24 | use crate::option::RJ_OPTION; 25 | use crate::wasm::get_file_content; 26 | 27 | #[allow(unused_imports)] 28 | use wasm_bindgen::prelude::*; 29 | 30 | use std::path::Path; 31 | 32 | #[macro_use] 33 | extern crate lazy_static; 34 | 35 | pub fn execute(file_name: String, debug_mode: usize) { 36 | RJ_OPTION.lock().unwrap().debug_mode = debug_mode; 37 | let class_name = file_name + ".class"; 38 | let buffer = get_file_content(&class_name); 39 | let mut string_pool = StringPool::new(); 40 | let (class_file, _pc_count) = Custom::new(&mut string_pool, &buffer, 0); 41 | let class_map = setup_class_map(&mut string_pool); 42 | let parent_path = if let Some(parent_path) = Path::new(&class_name).parent() { 43 | parent_path.to_str().unwrap() 44 | } else { 45 | "./" 46 | }; 47 | 48 | let mut context = Context::new(&mut string_pool, class_map, &class_file, parent_path); 49 | context.run_entry_file(&mut string_pool, class_file); 50 | } 51 | 52 | #[cfg(target_arch = "wasm32")] 53 | #[wasm_bindgen] 54 | pub fn run_wasm(class_name: &str) { 55 | use console_error_panic_hook::hook; 56 | use std::panic; 57 | panic::set_hook(Box::new(hook)); 58 | 59 | let mut string_pool = StringPool::new(); 60 | let inputs = get_file_content(class_name); 61 | let (class_file, _pc_count) = Custom::new(&mut string_pool, &inputs, 0); 62 | let class_map = setup_class_map(&mut string_pool); 63 | let parent_path = ""; 64 | 65 | let mut context = Context::new(&mut string_pool, class_map, &class_file, parent_path); 66 | context.run_entry_file(&mut string_pool, class_file); 67 | } 68 | -------------------------------------------------------------------------------- /src/method.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute::code::Code; 2 | use crate::attribute::defs::Attribute; 3 | use crate::constant::ConstantPool; 4 | use crate::string_pool::StringPool; 5 | use crate::utils::*; 6 | use std::fmt; 7 | 8 | #[derive(Debug)] 9 | pub struct Method { 10 | pub access_flags: MethodAccessFlags, // u2 11 | pub name_index: usize, // u2 12 | pub descriptor_index: usize, // u2 13 | pub attributes_count: usize, // u2 14 | pub attribute_info: Vec, 15 | } 16 | 17 | impl Method { 18 | pub fn new( 19 | string_pool: &mut StringPool, 20 | constant_pool: &ConstantPool, 21 | inputs: &[u8], 22 | index: usize, 23 | ) -> (Method, usize) { 24 | let (access_flag_num, index) = extract_x_byte_as_usize(inputs, index, 2); 25 | let access_flags = extract_access_flags(access_flag_num); 26 | let (name_index, index) = extract_x_byte_as_usize(inputs, index, 2); 27 | let (descriptor_index, index) = extract_x_byte_as_usize(inputs, index, 2); 28 | 29 | let (attributes_count, mut index) = extract_x_byte_as_usize(inputs, index, 2); 30 | let mut attribute_info = Vec::with_capacity(attributes_count); 31 | for _ in 0..attributes_count { 32 | let (attribute, updated_index) = 33 | Attribute::new(string_pool, constant_pool, inputs, index); 34 | index = updated_index; 35 | attribute_info.push(attribute); 36 | } 37 | 38 | ( 39 | Method { 40 | access_flags, 41 | name_index, 42 | descriptor_index, 43 | attributes_count, 44 | attribute_info, 45 | }, 46 | index, 47 | ) 48 | } 49 | 50 | pub fn extract_code<'a>(&self) -> Option<&Code> { 51 | if let Some(attribute) = self.attribute_info.iter().find(|attribute| { 52 | if let Attribute::Code(_) = attribute { 53 | true 54 | } else { 55 | false 56 | } 57 | }) { 58 | if let Attribute::Code(ref code) = attribute { 59 | Some(code) 60 | } else { 61 | unreachable!(); 62 | } 63 | } else { 64 | None 65 | } 66 | } 67 | 68 | pub fn run(&self) -> Result<(), String> { 69 | if let Some(code) = self.extract_code() { 70 | for instruction in code.code.iter() { 71 | println!("{}", instruction); 72 | } 73 | } 74 | Ok(()) 75 | } 76 | } 77 | 78 | impl fmt::Display for Method { 79 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 80 | let mut attribute_strs = Vec::with_capacity(self.attributes_count); 81 | for item in self.attribute_info.iter() { 82 | attribute_strs.push(format!("{}", item)); 83 | } 84 | 85 | write!( 86 | f, 87 | " name: #{} 88 | descriptor: #{} 89 | flags: {} 90 | {}", 91 | self.name_index, 92 | self.descriptor_index, 93 | self.access_flags, 94 | attribute_strs.join("\n\n") 95 | ) 96 | } 97 | } 98 | 99 | fn extract_access_flags(num: usize) -> MethodAccessFlags { 100 | let mut access_flags = vec![]; 101 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccPublic); 102 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccPrivate); 103 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccProtected); 104 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccStatic); 105 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccFinal); 106 | 107 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccSynchronized); 108 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccBridge); 109 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccVarargs); 110 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccNative); 111 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccAbstract); 112 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccStrict); 113 | crate::add_flags!(&mut access_flags, num, MethodAccessFlag::AccSynthetic); 114 | 115 | MethodAccessFlags(access_flags) 116 | } 117 | 118 | #[derive(Debug, PartialEq)] 119 | pub enum MethodAccessFlag { 120 | AccPublic = 0x0001, 121 | AccPrivate = 0x0002, 122 | AccProtected = 0x0004, 123 | AccStatic = 0x0008, 124 | AccFinal = 0x0010, 125 | AccSynchronized = 0x0020, 126 | AccBridge = 0x0040, 127 | AccVarargs = 0x0080, 128 | AccNative = 0x0100, 129 | AccAbstract = 0x0400, 130 | AccStrict = 0x0800, 131 | AccSynthetic = 0x1000, 132 | } 133 | 134 | impl fmt::Display for MethodAccessFlag { 135 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 136 | match self { 137 | MethodAccessFlag::AccPublic => write!(f, "ACC_PUBLIC"), 138 | MethodAccessFlag::AccPrivate => write!(f, "ACC_PRIVATE"), 139 | MethodAccessFlag::AccProtected => write!(f, "ACC_PROTECTED"), 140 | MethodAccessFlag::AccStatic => write!(f, "ACC_STATIC"), 141 | MethodAccessFlag::AccFinal => write!(f, "ACC_FINAL"), 142 | MethodAccessFlag::AccSynchronized => write!(f, "ACC_SYNCHRONIZED"), 143 | MethodAccessFlag::AccBridge => write!(f, "ACC_BRIDGE"), 144 | MethodAccessFlag::AccVarargs => write!(f, "ACC_VARARGS"), 145 | MethodAccessFlag::AccNative => write!(f, "ACC_NATIVE"), 146 | MethodAccessFlag::AccAbstract => write!(f, "ACC_ABSTRACT"), 147 | MethodAccessFlag::AccStrict => write!(f, "ACC_STRICT"), 148 | MethodAccessFlag::AccSynthetic => write!(f, "ACC_SYNTHETIC"), 149 | } 150 | } 151 | } 152 | 153 | impl From for MethodAccessFlag { 154 | fn from(num: usize) -> MethodAccessFlag { 155 | match num { 156 | 0x0001 => MethodAccessFlag::AccPublic, 157 | 0x0002 => MethodAccessFlag::AccPrivate, 158 | 0x0004 => MethodAccessFlag::AccProtected, 159 | 0x0008 => MethodAccessFlag::AccStatic, 160 | 0x0010 => MethodAccessFlag::AccFinal, 161 | 0x0020 => MethodAccessFlag::AccSynchronized, 162 | 0x0040 => MethodAccessFlag::AccBridge, 163 | 0x0080 => MethodAccessFlag::AccVarargs, 164 | 0x0100 => MethodAccessFlag::AccNative, 165 | 0x0400 => MethodAccessFlag::AccAbstract, 166 | 0x0800 => MethodAccessFlag::AccStrict, 167 | 0x1000 => MethodAccessFlag::AccSynthetic, 168 | _ => panic!("failed to convert {} to MethodAccessFlag", num), 169 | } 170 | } 171 | } 172 | 173 | #[derive(Debug)] 174 | pub struct MethodAccessFlags(pub Vec); 175 | impl fmt::Display for MethodAccessFlags { 176 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 177 | let mut result = Vec::with_capacity(self.0.len()); 178 | for item in self.0.iter() { 179 | result.push(format!("{}", item)); 180 | } 181 | if result.is_empty() { 182 | write!(f, "") 183 | } else { 184 | write!(f, "{}", result.join(", ")) 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/object.rs: -------------------------------------------------------------------------------- 1 | use crate::operand::Item; 2 | 3 | use std::cell::RefCell; 4 | use std::collections::HashMap; 5 | use std::fmt; 6 | 7 | #[derive(Debug)] 8 | pub struct ObjectMap { 9 | pub id: usize, 10 | pub map: HashMap, 11 | } 12 | impl ObjectMap { 13 | pub fn new() -> ObjectMap { 14 | ObjectMap { 15 | id: 0, 16 | map: HashMap::new(), 17 | } 18 | } 19 | 20 | pub fn add(&mut self, value: Objectref) -> usize { 21 | let id = self.id; 22 | self.id += 1; 23 | self.map.insert(id, value); 24 | id 25 | } 26 | 27 | pub fn get(&self, id: &usize) -> Option<&Objectref> { 28 | self.map.get(id) 29 | } 30 | 31 | pub fn get_mut(&mut self, id: &usize) -> Option<&mut Objectref> { 32 | self.map.get_mut(id) 33 | } 34 | } 35 | 36 | #[derive(PartialEq, Clone, Debug)] 37 | pub struct Objectref { 38 | pub class_name_id: usize, 39 | pub field_map: RefCell>, 40 | pub is_initialized: bool, 41 | } 42 | 43 | pub type FieldMap = RefCell>; 44 | 45 | impl Objectref { 46 | pub fn new(class_name_id: usize, field_map: FieldMap, is_initialized: bool) -> Objectref { 47 | Objectref { 48 | class_name_id, 49 | field_map, 50 | is_initialized, 51 | } 52 | } 53 | } 54 | 55 | impl fmt::Display for Objectref { 56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 57 | let field_map = self.field_map.borrow(); 58 | let keys = field_map.keys(); 59 | let mut val_strs = Vec::with_capacity(keys.len()); 60 | for key in keys { 61 | let val = field_map.get(key).unwrap(); 62 | match val.1 { 63 | Item::Null => val_strs.push(format!("{}.{}: {}", key.0, key.1, val.0)), 64 | _ => val_strs.push(format!("{}.{}: {} {}", key.0, key.1, val.0, val.1)), 65 | }; 66 | } 67 | 68 | write!( 69 | f, 70 | "object_ref: 71 | class {}: 72 | {}", 73 | self.class_name_id, 74 | val_strs.join("\n") 75 | ) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/operand.rs: -------------------------------------------------------------------------------- 1 | // use crate::utils::devide_u64_to_two_u32; 2 | use std::cmp::{Ordering, PartialOrd}; 3 | use std::fmt; 4 | 5 | pub fn devide_i64_two_usize(input: i64) -> (usize, usize) { 6 | let high_value = ((input >> 32) << 32) as usize; 7 | let low_value = (input & 0xFFFFFFFF) as usize; 8 | if input > 0 { 9 | (low_value, high_value) 10 | } else { 11 | (0xFFFFFFFF - low_value, 0xFFFFFFFF - high_value) 12 | } 13 | } 14 | 15 | #[derive(PartialEq, Clone, Debug)] 16 | pub enum Item { 17 | Null, 18 | Int(i32), 19 | Long(usize), 20 | Float(f32), 21 | Double(usize), 22 | String(usize), 23 | Boolean(bool), 24 | Classref(usize), 25 | Fieldref(usize), 26 | Objectref(usize), 27 | Arrayref(usize), 28 | } 29 | 30 | impl fmt::Display for Item { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | match self { 33 | Item::Null => write!(f, "null"), 34 | Item::Int(val) => write!(f, "int: {}", val), 35 | Item::Long(val) => write!(f, "long: {}", val), 36 | Item::Float(val) => write!(f, "float: {}", val), 37 | Item::Double(val) => write!(f, "double: {}", val), 38 | Item::Boolean(val) => write!(f, "boolean: {}", val), 39 | Item::String(val) => write!(f, "string: {}", val), 40 | Item::Classref(val) => write!(f, "class_ref: {}", val), 41 | Item::Fieldref(val) => write!(f, "field_ref: {}", val), 42 | Item::Objectref(val) => write!(f, "object_ref: {}", val), 43 | Item::Arrayref(val) => write!(f, "array_ref {}", val), 44 | } 45 | } 46 | } 47 | 48 | impl PartialOrd for Item { 49 | fn partial_cmp(&self, other: &Item) -> Option { 50 | match (self, other) { 51 | (Item::Null, Item::Null) => Some(Ordering::Equal), 52 | (Item::Int(left), Item::Int(right)) => Some(left.cmp(right)), 53 | (Item::Float(left), Item::Float(right)) => Some({ 54 | if left > right { 55 | Ordering::Greater 56 | } else if left == right { 57 | Ordering::Equal 58 | } else { 59 | Ordering::Less 60 | } 61 | }), 62 | (Item::Double(left), Item::Double(right)) => Some(left.cmp(right)), 63 | (Item::Boolean(left), Item::Boolean(right)) => Some(left.cmp(right)), 64 | (Item::Long(left), Item::Long(right)) => Some(left.cmp(right)), 65 | (Item::Classref(left), Item::Classref(right)) => Some(left.cmp(right)), 66 | (Item::Fieldref(left), Item::Fieldref(right)) => Some(left.cmp(right)), 67 | (Item::String(left), Item::String(right)) => Some(left.cmp(right)), 68 | _ => None, 69 | } 70 | } 71 | } 72 | 73 | #[derive(Debug)] 74 | pub struct OperandStack { 75 | pub stack: Vec, 76 | } 77 | 78 | macro_rules! culculate { 79 | ($name:ident, $extract_method:ident, $type:ident, $op:tt) => { 80 | pub fn $name(&mut self) -> Item { 81 | let (first, second) = self.$extract_method(); 82 | Item::$type(first $op second) 83 | } 84 | } 85 | } 86 | 87 | impl OperandStack { 88 | pub fn new() -> Self { 89 | OperandStack { stack: vec![] } 90 | } 91 | 92 | fn extract_long_values_as_i64(&mut self) -> (i64, i64) { 93 | match ( 94 | self.stack.pop(), 95 | self.stack.pop(), 96 | self.stack.pop(), 97 | self.stack.pop(), 98 | ) { 99 | ( 100 | Some(Item::Long(second_2)), 101 | Some(Item::Long(second_1)), 102 | Some(Item::Long(first_2)), 103 | Some(Item::Long(first_1)), 104 | ) => { 105 | let second: u64 = (((second_1 as u64) << 32) as u64) | second_2 as u64; 106 | let second = if second > 0x7fffffffffffffff { 107 | -1 * ((second ^ 0xffffffffffffffff) + 1) as i64 108 | } else { 109 | second as i64 110 | }; 111 | let first: u64 = (((first_1 as u64) << 32) as u64) | first_2 as u64; 112 | let first = if first > 0x7fffffffffffffff { 113 | -1 * ((first ^ 0xffffffffffffffff) + 1) as i64 114 | } else { 115 | first as i64 116 | }; 117 | (first, second) 118 | } 119 | (second_2, second_1, first_2, first_1) => panic!( 120 | "failed to extract long values 121 | first: {:?}, {:?} 122 | second: {:?}, {:?}", 123 | first_1, first_2, second_1, second_2 124 | ), 125 | } 126 | } 127 | 128 | fn extract_int_values(&mut self) -> (i32, i32) { 129 | match (self.stack.pop(), self.stack.pop()) { 130 | (Some(Item::Int(second)), Some(Item::Int(first))) => (first, second), 131 | (second, first) => panic!( 132 | "failed to extract int values 133 | first: {:?} 134 | second: {:?}", 135 | second, first 136 | ), 137 | } 138 | } 139 | 140 | fn extract_float_values(&mut self) -> (f32, f32) { 141 | match (self.stack.pop(), self.stack.pop()) { 142 | (Some(Item::Float(second)), Some(Item::Float(first))) => (first, second), 143 | (second, first) => panic!( 144 | "failed to extract float values 145 | first: {:?} 146 | second: {:?}", 147 | second, first 148 | ), 149 | } 150 | } 151 | 152 | culculate!(iadd, extract_int_values, Int, +); 153 | culculate!(isub, extract_int_values, Int, -); 154 | culculate!(imul, extract_int_values, Int, *); 155 | culculate!(idiv, extract_int_values, Int, /); 156 | culculate!(irem, extract_int_values, Int, %); 157 | 158 | culculate!(fadd, extract_float_values, Float, +); 159 | culculate!(fsub, extract_float_values, Float, -); 160 | culculate!(fmul, extract_float_values, Float, *); 161 | culculate!(fdiv, extract_float_values, Float, /); 162 | culculate!(frem, extract_float_values, Float, %); 163 | 164 | pub fn ladd(&mut self) -> (Item, Item) { 165 | let (first, second) = self.extract_long_values_as_i64(); 166 | let (first, second) = devide_i64_two_usize(first + second); 167 | (Item::Long(first), Item::Long(second)) 168 | } 169 | 170 | pub fn lsub(&mut self) -> (Item, Item) { 171 | let (first, second) = self.extract_long_values_as_i64(); 172 | let (first, second) = devide_i64_two_usize(first + second); 173 | (Item::Long(first), Item::Long(second)) 174 | } 175 | 176 | pub fn lmul(&mut self) -> (Item, Item) { 177 | let (first, second) = self.extract_long_values_as_i64(); 178 | let (first, second) = devide_i64_two_usize(first * second); 179 | (Item::Long(first), Item::Long(second)) 180 | } 181 | 182 | pub fn ldiv(&mut self) -> (Item, Item) { 183 | let (first, second) = self.extract_long_values_as_i64(); 184 | let (first, second) = devide_i64_two_usize(first / second); 185 | (Item::Long(first), Item::Long(second)) 186 | } 187 | 188 | pub fn lrem(&mut self) -> (Item, Item) { 189 | let (first, second) = self.extract_long_values_as_i64(); 190 | let (first, second) = devide_i64_two_usize(first % second); 191 | (Item::Long(first), Item::Long(second)) 192 | } 193 | 194 | pub fn lcmp(&mut self) -> Item { 195 | let (first, second) = self.extract_long_values_as_i64(); 196 | self.compare_value(first, second) 197 | } 198 | 199 | pub fn fcmp(&mut self) -> Item { 200 | let (first, second) = self.extract_float_values(); 201 | self.compare_value(first, second) 202 | } 203 | 204 | fn compare_value(&self, first: T, second: T) -> Item 205 | where 206 | T: PartialOrd, 207 | { 208 | if first > second { 209 | Item::Int(1) 210 | } else if first == second { 211 | Item::Int(0) 212 | } else { 213 | Item::Int(-1) 214 | } 215 | } 216 | } 217 | 218 | impl fmt::Display for OperandStack { 219 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 220 | let length = self.stack.len(); 221 | let mut item_string_vec = Vec::with_capacity(length); 222 | let mut index = 0; 223 | for item in self.stack.iter() { 224 | match item { 225 | Item::Long(_) => { 226 | item_string_vec.push(format!("#{}+#{} {}", index, index + 1, item)); 227 | index += 1; 228 | } 229 | _ => item_string_vec.push(format!("#{} {}", index, item)), 230 | }; 231 | index += 1; 232 | } 233 | 234 | write!( 235 | f, 236 | "length: {} 237 | item ==================== 238 | {} 239 | ========================", 240 | length, 241 | item_string_vec.join("\n") 242 | ) 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/option.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | /** 4 | * 0: no info 5 | * 1: emit instruction 6 | * 2: emit instruction + operandstack 7 | */ 8 | #[derive(Debug)] 9 | pub struct RjOption { 10 | pub debug_mode: usize, 11 | } 12 | 13 | impl RjOption { 14 | pub fn new() -> RjOption { 15 | RjOption { debug_mode: 0 } 16 | } 17 | } 18 | 19 | lazy_static! { 20 | pub static ref RJ_OPTION: Mutex = Mutex::new(RjOption::new()); 21 | } 22 | -------------------------------------------------------------------------------- /src/order.rs: -------------------------------------------------------------------------------- 1 | use crate::operand::Item; 2 | 3 | #[derive(Debug)] 4 | pub struct Order { 5 | pub opecode: Opecode, 6 | pub operand: Item, 7 | } 8 | impl Order { 9 | pub fn new(opecode: Opecode, operand: Item) -> Order { 10 | Order { opecode, operand } 11 | } 12 | } 13 | 14 | #[derive(Debug)] 15 | pub enum Opecode { 16 | Iadd, 17 | Iconst, 18 | Ireturn, 19 | IfIcmple, 20 | } 21 | -------------------------------------------------------------------------------- /src/stackframe.rs: -------------------------------------------------------------------------------- 1 | use crate::operand::{Item, OperandStack}; 2 | 3 | #[derive(Debug)] 4 | pub struct Stackframe { 5 | pub local_variables: Vec, 6 | pub operand_stack: OperandStack, 7 | } 8 | 9 | impl Stackframe { 10 | pub fn new(variables_number: usize) -> Self { 11 | Stackframe { 12 | local_variables: Vec::with_capacity(variables_number), 13 | operand_stack: OperandStack::new(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/string_pool.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub struct StringPool { 4 | pub id: usize, 5 | pub key_value_map: HashMap, 6 | pub value_key_map: HashMap, 7 | } 8 | 9 | impl StringPool { 10 | pub fn new() -> StringPool { 11 | StringPool { 12 | id: 0, 13 | key_value_map: HashMap::new(), 14 | value_key_map: HashMap::new(), 15 | } 16 | } 17 | 18 | pub fn insert(&mut self, value: String) -> usize { 19 | if let Some(id) = self.value_key_map.get(&value) { 20 | return *id; 21 | } 22 | 23 | let id = self.id; 24 | self.id += 1; 25 | 26 | self.value_key_map.insert(value.clone(), id); 27 | self.key_value_map.insert(id, value); 28 | 29 | id 30 | } 31 | 32 | pub fn get_value(&self, id: &usize) -> String { 33 | self.key_value_map 34 | .get(id) 35 | .expect("exist string in string_pool") 36 | .to_string() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute::instruction::Instruction; 2 | use crate::object::{ObjectMap, Objectref}; 3 | use crate::operand::Item; 4 | use crate::option::RJ_OPTION; 5 | use crate::stackframe::Stackframe; 6 | 7 | use std::cell::RefCell; 8 | use std::collections::HashMap; 9 | use std::fs::File; 10 | use std::io::prelude::*; 11 | use std::io::Result; 12 | use std::iter; 13 | use std::path::Path; 14 | 15 | pub fn read_file>(input: &P) -> Result> { 16 | let mut result = vec![]; 17 | let mut f = File::open(input)?; 18 | f.read_to_end(&mut result)?; 19 | Ok(result) 20 | } 21 | 22 | pub fn extract_x_byte_as_vec(input: &[u8], index: usize, x: usize) -> (Vec, usize) { 23 | let mut result = Vec::with_capacity(x); 24 | for i in 0..x { 25 | result.push(input[index + i]); 26 | } 27 | (result, index + x) 28 | } 29 | 30 | pub fn extract_x_byte_as_usize(input: &[u8], index: usize, x: usize) -> (usize, usize) { 31 | let mut result: usize = 0; 32 | for i in 0..x { 33 | result += (input[index + i] as usize) << (x - i - 1) * 8; 34 | } 35 | (result, index + x) 36 | } 37 | 38 | pub fn devide_i64_to_two_i32(input: i64) -> (i32, i32) { 39 | (((input >> 32) << 32) as i32, (input & 0xFFFFFFFF) as i32) 40 | } 41 | 42 | pub fn emit_debug_info(instruction: &Instruction, stackframe: Option<&Stackframe>) { 43 | match RJ_OPTION.lock().unwrap().debug_mode { 44 | 1 => { 45 | println!("instruction: {}", instruction,); 46 | } 47 | 2 => { 48 | println!( 49 | "instruction: {} 50 | operand_stack: 51 | {} 52 | ", 53 | instruction, 54 | stackframe.unwrap().operand_stack 55 | ); 56 | } 57 | _ => {} 58 | }; 59 | } 60 | 61 | pub fn iniailize_primitive_array(type_index: usize, length: usize) -> Vec<(Item, Item)> { 62 | let default_val = match type_index { 63 | // TBoolean 64 | 4 => (Item::Boolean(false), Item::Null), 65 | // // TChar 66 | // 5 => , 67 | // // TFloat 68 | // 6 => , 69 | // // TDouble 70 | // 7 => , 71 | // // TByte 72 | // 8 => , 73 | // // TShort 74 | // 9 => , 75 | // TInt 76 | 10 => (Item::Int(0), Item::Null), 77 | // TLong 78 | 11 => (Item::Long(0), Item::Long(0)), 79 | _ => unreachable!("type_index range should 4 - 11"), 80 | }; 81 | let mut initialize_vec = vec![]; 82 | initialize_vec.extend(iter::repeat(default_val).take(length)); 83 | initialize_vec 84 | } 85 | 86 | pub fn initialize_objectref_array( 87 | object_map: &mut ObjectMap, 88 | class_name_id: usize, 89 | length: usize, 90 | ) -> Vec { 91 | let mut initialize_vec = Vec::with_capacity(length); 92 | for _ in 0..length { 93 | let id = object_map.add(Objectref::new( 94 | class_name_id, 95 | RefCell::new(HashMap::new()), 96 | false, 97 | )); 98 | initialize_vec.push(id); 99 | } 100 | 101 | initialize_vec 102 | } 103 | 104 | #[macro_export] 105 | macro_rules! add_flags { 106 | ($flags:expr, $num:expr, $flag:expr) => { 107 | if $num & $flag as usize != 0 { 108 | $flags.push($flag) 109 | } 110 | }; 111 | } 112 | 113 | #[test] 114 | pub fn test_extract_x_byte_as_vec() { 115 | let mut input = vec![1, 2, 3, 4]; 116 | assert_eq!(extract_x_byte_as_vec(&mut input, 1, 2), (vec![2, 3], 3)); 117 | 118 | let mut input = vec![1, 2, 3, 4, 5, 6, 7, 8]; 119 | assert_eq!( 120 | extract_x_byte_as_vec(&mut input, 3, 4), 121 | (vec![4, 5, 6, 7], 7) 122 | ); 123 | } 124 | 125 | #[test] 126 | pub fn test_extract_x_byte_as_usize() { 127 | let mut input = vec![1, 2, 3, 4]; 128 | assert_eq!( 129 | extract_x_byte_as_usize(&mut input, 1, 2), 130 | ((2 << 8) as usize + 3, 3) 131 | ); 132 | assert_eq!(input[3], 4); 133 | 134 | let mut input = vec![1, 2, 3, 4, 5, 6, 7, 8]; 135 | let fourth = 1 << (8 * 3); 136 | let third = 2 << (8 * 2); 137 | let second = 3 << (8 * 1); 138 | let first = 4 << (8 * 0); 139 | 140 | assert_eq!( 141 | extract_x_byte_as_usize(&mut input, 0, 4), 142 | (first + second + third + fourth, 4) 143 | ); 144 | assert_eq!(input[4], 5); 145 | } 146 | -------------------------------------------------------------------------------- /src/wasm.rs: -------------------------------------------------------------------------------- 1 | #[cfg(unix)] 2 | use crate::utils::read_file; 3 | 4 | #[allow(unused_imports)] 5 | use wasm_bindgen::prelude::*; 6 | 7 | #[cfg(target_arch = "wasm32")] 8 | #[wasm_bindgen] 9 | extern "C" { 10 | pub fn alert(s: &str); 11 | 12 | #[wasm_bindgen(js_namespace = console)] 13 | pub fn log(s: &str); 14 | 15 | #[wasm_bindgen(js_namespace = console, js_name = log)] 16 | pub fn log_u8_array(a: &[u8]); 17 | 18 | #[wasm_bindgen(js_namespace = console, js_name = log)] 19 | pub fn log_u32(a: u32); 20 | 21 | #[wasm_bindgen(js_namespace = console, js_name = log)] 22 | pub fn log_u16(a: u16); 23 | } 24 | 25 | #[cfg(target_arch = "wasm32")] 26 | #[wasm_bindgen(module = "/web/map.js")] 27 | extern "C" { 28 | pub fn get_file_content_from_js(key: &str) -> Vec; 29 | pub fn output_log(key: &str); 30 | } 31 | 32 | #[cfg(target_arch = "wasm32")] 33 | #[wasm_bindgen] 34 | pub fn get_file_content(key: &str) -> Vec { 35 | let val = get_file_content_from_js(key); 36 | if val.len() == 0 { 37 | panic!("undefined value come. cannot continue") 38 | } 39 | val 40 | } 41 | 42 | #[cfg(unix)] 43 | pub fn get_file_content(key: &str) -> Vec { 44 | read_file(&key).expect(&format!( 45 | "need to add handler for the case failed to find the class file: {}", 46 | &key 47 | )) 48 | } 49 | 50 | #[cfg(unix)] 51 | pub fn print_log(value: &str) { 52 | println!("{}", value); 53 | } 54 | 55 | #[cfg(target_arch = "wasm32")] 56 | #[wasm_bindgen] 57 | pub fn print_log(value: &str) { 58 | output_log(value); 59 | } 60 | -------------------------------------------------------------------------------- /tests/class/ArrayElementClass.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/ArrayElementClass.class -------------------------------------------------------------------------------- /tests/class/CallInstanceMethod.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/CallInstanceMethod.class -------------------------------------------------------------------------------- /tests/class/CustomArray.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/CustomArray.class -------------------------------------------------------------------------------- /tests/class/CustomMultiDimentionArray.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/CustomMultiDimentionArray.class -------------------------------------------------------------------------------- /tests/class/DconstN.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/DconstN.class -------------------------------------------------------------------------------- /tests/class/Field.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/Field.class -------------------------------------------------------------------------------- /tests/class/FizzBuzz.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/FizzBuzz.class -------------------------------------------------------------------------------- /tests/class/FizzBuzz2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/FizzBuzz2.class -------------------------------------------------------------------------------- /tests/class/FloatCulculate.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/FloatCulculate.class -------------------------------------------------------------------------------- /tests/class/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/HelloWorld.class -------------------------------------------------------------------------------- /tests/class/InitializeStatic.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/InitializeStatic.class -------------------------------------------------------------------------------- /tests/class/InstanceField.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/InstanceField.class -------------------------------------------------------------------------------- /tests/class/LongArray.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/LongArray.class -------------------------------------------------------------------------------- /tests/class/LongCulculate.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/LongCulculate.class -------------------------------------------------------------------------------- /tests/class/MinusInt30.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/MinusInt30.class -------------------------------------------------------------------------------- /tests/class/NestFor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/NestFor.class -------------------------------------------------------------------------------- /tests/class/NestForElement.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/NestForElement.class -------------------------------------------------------------------------------- /tests/class/NewAndCallInstanceMethod.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/NewAndCallInstanceMethod.class -------------------------------------------------------------------------------- /tests/class/OtherClass.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/OtherClass.class -------------------------------------------------------------------------------- /tests/class/PrimitiveArray.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/PrimitiveArray.class -------------------------------------------------------------------------------- /tests/class/PrintMessage.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/PrintMessage.class -------------------------------------------------------------------------------- /tests/class/SimpleMultiDimentions.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/SimpleMultiDimentions.class -------------------------------------------------------------------------------- /tests/class/Switch.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchaser53/rust-jvm/3b86a60d362bb46fe2362ce65d330f3bfe7bf24b/tests/class/Switch.class -------------------------------------------------------------------------------- /tests/original/CustomArray.java: -------------------------------------------------------------------------------- 1 | public class CustomArray { 2 | public static void main(String[] args){ 3 | ArrayElementClass foo[] = new ArrayElementClass[3]; 4 | foo[0] = new ArrayElementClass(1); 5 | System.out.println(foo[0].a); 6 | } 7 | } 8 | 9 | class ArrayElementClass { 10 | int a; 11 | ArrayElementClass(int input) { 12 | a = input; 13 | } 14 | } -------------------------------------------------------------------------------- /tests/original/CustomMultiDimentionArray.java: -------------------------------------------------------------------------------- 1 | public class CustomMultiDimentionArray { 2 | public static void main(String[] args){ 3 | ArrayElementClass[] foo[] = new ArrayElementClass[3][4]; 4 | foo[1][2] = new ArrayElementClass(11); 5 | System.out.println(foo[1][2].a); 6 | } 7 | } 8 | 9 | class ArrayElementClass { 10 | int a; 11 | ArrayElementClass(int input) { 12 | a = input; 13 | } 14 | } -------------------------------------------------------------------------------- /tests/original/DconstN.java: -------------------------------------------------------------------------------- 1 | public class DconstN { 2 | public static void main(String[] args) { 3 | double a = 1D; 4 | double b = 0D; 5 | System.out.println(a); 6 | System.out.println(b); 7 | } 8 | } -------------------------------------------------------------------------------- /tests/original/FloatCulculate.java: -------------------------------------------------------------------------------- 1 | public class FloatCulculate { 2 | public static void main(String[] args) { 3 | float a = - 1.2F; 4 | System.out.println(a * 2); 5 | System.out.println(a / 3); 6 | System.out.println(a + 4); 7 | System.out.println(a - 5); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/original/InitializeStatic.java: -------------------------------------------------------------------------------- 1 | public class InitializeStatic { 2 | static int sa = 3; 3 | public static void main(String[] args){ 4 | System.out.println(InitializeStatic.sa + OtherClass.sb); 5 | InitializeStatic.sa = 5; 6 | OtherClass.sb += 1; 7 | System.out.println(InitializeStatic.sa + OtherClass.sb); 8 | } 9 | } 10 | 11 | class OtherClass { 12 | static int sb = 4; 13 | } -------------------------------------------------------------------------------- /tests/original/InstanceField.java: -------------------------------------------------------------------------------- 1 | public class InstanceField { 2 | public static void main(String[] args){ 3 | Field fa = new Field(1); 4 | fa.xxxx = 12; 5 | System.out.println(fa.xxxx); 6 | 7 | Field fb = new Field(2); 8 | fb.xxxx = 13; 9 | System.out.println(fb.xxxx); 10 | } 11 | } 12 | 13 | class Field { 14 | int xxxx; 15 | public Field(int a) { 16 | xxxx = a; 17 | System.out.println(xxxx); 18 | } 19 | } -------------------------------------------------------------------------------- /tests/original/LongArray.java: -------------------------------------------------------------------------------- 1 | public class LongArray { 2 | public static void main(String[] args){ 3 | long funyan[] = { 1, 2, 3, 4, 5 }; 4 | funyan[1] = 12; 5 | System.out.println(funyan[1]); 6 | System.out.println(funyan[2]); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/original/LongCulculate.java: -------------------------------------------------------------------------------- 1 | public class LongCulculate { 2 | public static void main(String[] args) { 3 | long a = -1; 4 | System.out.println(a * -3); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/original/MinusInt30.java: -------------------------------------------------------------------------------- 1 | public class MinusInt30 { 2 | public static void main(String[] args) { 3 | int a = -30; 4 | System.out.println(a); // 1.0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/original/NestFor.java: -------------------------------------------------------------------------------- 1 | public class NestFor { 2 | public static void main(String[] args){ 3 | int i = 5; 4 | int j = 6; 5 | int count = 0; 6 | NestForElement[] foo[] = new NestForElement[i][j]; 7 | for (int ii=0; ii>> created"); 6 | } 7 | } 8 | 9 | class CallInstanceMethod { 10 | CallInstanceMethod() { 11 | System.out.println(">>> constructor"); 12 | } 13 | 14 | void abc() { 15 | System.out.println(">>> abc"); 16 | } 17 | } -------------------------------------------------------------------------------- /tests/original/PrimitiveArray.java: -------------------------------------------------------------------------------- 1 | public class PrimitiveArray { 2 | public static void main(String[] args){ 3 | int foo[] = new int[3]; 4 | foo[1] = 100; 5 | System.out.println(foo[1]); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/original/SimpleMultiDimentions.java: -------------------------------------------------------------------------------- 1 | public class SimpleMultiDimentions { 2 | public static void main(String[] args){ 3 | int[] foo[] = new int [4][3]; 4 | foo[2][1] = 5; 5 | System.out.println(foo[2][1]); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/original/Switch.java: -------------------------------------------------------------------------------- 1 | public class Switch { 2 | public static void main(String[] args){ 3 | int i = 1; 4 | switch (i) { 5 | case 1: 6 | System.out.println("abc"); 7 | break; 8 | case 2: 9 | System.out.println("def"); 10 | break; 11 | default: 12 | System.out.println("def"); 13 | break; 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | use r_jvm; 2 | 3 | fn test_helper(file_name: String) { 4 | println!("** {} **", &file_name); 5 | r_jvm::execute(file_name, 0); 6 | println!(""); 7 | } 8 | 9 | fn main() { 10 | test_helper(String::from("tests/class/HelloWorld")); 11 | test_helper(String::from("tests/class/FizzBuzz")); 12 | test_helper(String::from("tests/class/FizzBuzz2")); 13 | test_helper(String::from("tests/class/NewAndCallInstanceMethod")); 14 | test_helper(String::from("tests/class/InitializeStatic")); 15 | test_helper(String::from("tests/class/Switch")); 16 | test_helper(String::from("tests/class/InstanceField")); 17 | test_helper(String::from("tests/class/PrimitiveArray")); 18 | test_helper(String::from("tests/class/CustomArray")); 19 | test_helper(String::from("tests/class/SimpleMultiDimentions")); 20 | test_helper(String::from("tests/class/CustomMultiDimentionArray")); 21 | test_helper(String::from("tests/class/NestFor")); 22 | test_helper(String::from("tests/class/LongArray")); 23 | test_helper(String::from("tests/class/MinusInt30")); 24 | test_helper(String::from("tests/class/LongCulculate")); 25 | test_helper(String::from("tests/class/FloatCulculate")); 26 | test_helper(String::from("tests/class/DconstN")); 27 | } 28 | -------------------------------------------------------------------------------- /web/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 72 | 73 | 114 | -------------------------------------------------------------------------------- /web/Result.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 43 | -------------------------------------------------------------------------------- /web/Setting.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 102 | 103 | 144 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 7 | 8 |
9 | 10 | -------------------------------------------------------------------------------- /web/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | 4 | window.map = {}; 5 | window.output = []; 6 | window.onload = async () => { 7 | const rust = await import("./pkg"); 8 | 9 | new Vue({ 10 | el: "#app", 11 | components: { 12 | App 13 | }, 14 | data() { 15 | return { 16 | entryFileName: "", 17 | fileNames: [] 18 | }; 19 | }, 20 | template: ``, 21 | computed: { 22 | rust() { 23 | return rust; 24 | }, 25 | window() { 26 | return window; 27 | } 28 | } 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /web/map.js: -------------------------------------------------------------------------------- 1 | export function get_file_content_from_js(key) { 2 | const value = window.map[key]; 3 | if (value == null) { 4 | console.error(`${key} is not found. upload ${key}`); 5 | return []; 6 | } 7 | return window.map[key]; 8 | } 9 | 10 | export function output_log(value) { 11 | console.log(value); 12 | window.output.push(value); 13 | } 14 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rj", 3 | "version": "1.0.0", 4 | "description": "![build_status](https://travis-ci.org/rchaser53/rust-jvm.svg?branch=master)", 5 | "main": "index.js", 6 | "scripts": { 7 | "format": "prettier --write \"**/*.{md,js,ts,vue,json}\"", 8 | "serve": "webpack-dev-server" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/rchaser53/rj.git" 13 | }, 14 | "keywords": [], 15 | "author": "rchaser53", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/rchaser53/rj/issues" 19 | }, 20 | "homepage": "https://github.com/rchaser53/rj#readme", 21 | "dependencies": { 22 | "@wasm-tool/wasm-pack-plugin": "1.0.1", 23 | "css-loader": "^3.2.1", 24 | "html-webpack-plugin": "^3.2.0", 25 | "node-sass": "^4.13.0", 26 | "prettier": "^1.19.1", 27 | "sass-loader": "^8.0.0", 28 | "serialize-javascript": ">=3.1.0", 29 | "style-loader": "^1.0.1", 30 | "text-encoding": "^0.7.0", 31 | "vue": "^2.6.10", 32 | "vue-loader": "^15.7.2", 33 | "vue-style-loader": "^4.1.2", 34 | "vue-template-compiler": "^2.6.10", 35 | "webpack": "^4.41.2", 36 | "webpack-cli": "^3.3.10", 37 | "webpack-dev-server": "^3.9.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /web/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 4 | const { VueLoaderPlugin } = require("vue-loader"); 5 | 6 | const webpack = require("webpack"); 7 | const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin"); 8 | 9 | module.exports = { 10 | entry: "./index.js", 11 | output: { 12 | path: path.resolve(__dirname, "dist"), 13 | filename: "index.js" 14 | }, 15 | resolve: { 16 | alias: { 17 | vue$: "vue/dist/vue.esm.js" 18 | } 19 | }, 20 | plugins: [ 21 | new HtmlWebpackPlugin({ 22 | template: path.resolve(__dirname, "index.html") 23 | }), 24 | new WasmPackPlugin({ 25 | crateDirectory: path.resolve(__dirname, ".."), 26 | outDir: path.resolve(__dirname, "./pkg") 27 | }), 28 | new webpack.ProvidePlugin({ 29 | TextDecoder: ["text-encoding", "TextDecoder"], 30 | TextEncoder: ["text-encoding", "TextEncoder"] 31 | }), 32 | new VueLoaderPlugin() 33 | ], 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.vue$/, 38 | loader: "vue-loader", 39 | options: { 40 | esModule: true 41 | } 42 | }, 43 | { 44 | test: /\.css$/, 45 | use: [ 46 | "vue-style-loader", 47 | { 48 | loader: "css-loader" 49 | } 50 | ] 51 | } 52 | ] 53 | }, 54 | mode: "development", 55 | devServer: { 56 | open: true, 57 | } 58 | }; 59 | --------------------------------------------------------------------------------