├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── read_class.rs ├── classes ├── pick.py └── testing │ └── LookupSwitch.java ├── examples └── read │ ├── .gitignore │ ├── Cargo.toml │ ├── TestClass.java │ ├── build.rs │ └── src │ └── main.rs └── src ├── access.rs ├── ast.rs ├── attributes.rs ├── bin.rs ├── classfile.rs ├── code.rs ├── constantpool.rs ├── error.rs ├── field.rs ├── insnlist.rs ├── lib.rs ├── method.rs ├── types.rs ├── utils.rs └── version.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /target 3 | /classes/benchmarking 4 | /classes/benchmarking_ 5 | *.class 6 | Cargo.lock 7 | TestOut.class 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "classfile-rs" 3 | version = "0.1.0" 4 | authors = ["mastercooker "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "classfile" 9 | path = "src/lib.rs" 10 | 11 | [[bin]] 12 | name = "dissasembler" 13 | path = "src/bin.rs" 14 | [profile.release] 15 | debug = true 16 | 17 | [dependencies] 18 | byteorder = "1.3.4" 19 | derive_more = { version = "0.99.11", default-features = false, features = ["constructor"] } 20 | thiserror = "1.0.21" 21 | enum-display-derive = { git = "https://github.com/bytechef/enum-display-derive" } 22 | linked-hash-map = "0.5.3" 23 | mutf8 = "0.4.1" 24 | bitflags = "1.2.1" 25 | 26 | [dev-dependencies] 27 | criterion = "0.3.3" 28 | rayon = "1.4.1" 29 | 30 | [[bench]] 31 | name = "read_class" 32 | harness = false 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 x4e 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # classfile-rs 2 | Speedy class file implementation for rust 3 | 4 | This is a library for reading, manipulating, and writing class files. 5 | 6 | Goals: 7 | * Simple, abstract AST 8 | * Stackmap frame generation 9 | * Resistant to malicious/malformed class files 10 | 11 | The AST is designed to be more logical and intuitive to a Java developer than the official bytecode 12 | specification. 13 | 14 | For example, `iconst_0`s are represented as `ldc(0)`s, `invokestatic(...)`s are represented as `invoke(static, ...)`. 15 | Bytecode offsets are also transformed into labels. 16 | The constant pool is fully abstracted. 17 | 18 | Todos: 19 | * Stackmap frame generation 20 | * Class writing 21 | (Feel free to contribute on these) 22 | 23 | ## Speed 24 | The library should take <1ms to read an averagely sized class file, but can take longer depending on the amount of 25 | control flow within the class (Label reading is not optimal at the moment). 26 | 27 | Here is a benchmark: 28 | ![Throughput benchmark](https://cdn.discordapp.com/attachments/665688984302649354/803225667399057448/unknown.png) 29 | 30 | ## Examples 31 | [Reading a class file](https://github.com/x4e/classfile-rs/tree/master/examples/read/src/main.rs) 32 | 33 | -------------------------------------------------------------------------------- /benches/read_class.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion, Throughput, BatchSize, BenchmarkId}; 2 | use classfile::classfile::ClassFile; 3 | use std::io::{Cursor}; 4 | use std::fs; 5 | 6 | fn read_class_bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("read_class"); 8 | 9 | for entry in fs::read_dir("classes/benchmarking").unwrap() { 10 | let entry = entry.unwrap(); 11 | let path = entry.path(); 12 | if path.is_file() { 13 | if let Some(ex) = path.extension() { 14 | if let Some(ex) = ex.to_str() { 15 | let ex = ex.to_string(); 16 | if ex == "class" { 17 | let bytes: Vec = fs::read(path).unwrap(); 18 | group.throughput(Throughput::Bytes(bytes.len() as u64)); 19 | group.bench_with_input(BenchmarkId::from_parameter(entry.file_name().into_string().unwrap()), &bytes, |b, bytes| { 20 | b.iter_batched(|| Cursor::new(bytes), | mut slice |{ 21 | ClassFile::parse(&mut slice) 22 | }, BatchSize::SmallInput); 23 | }); 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | 31 | criterion_group!(benches, read_class_bench); 32 | criterion_main!(benches); 33 | -------------------------------------------------------------------------------- /classes/pick.py: -------------------------------------------------------------------------------- 1 | # make an even distribution of class sizes 2 | import os 3 | 4 | 5 | dir = "benchmarking" 6 | 7 | 8 | largeStep = int(100 * 1024) 9 | smallStep = int(1.5 * 1024) 10 | stepThreshold = 100000 11 | 12 | largeSizes = set() 13 | smallSizes = set() 14 | 15 | for file in os.scandir(dir): 16 | try: 17 | if file.path.endswith(".class"): 18 | stats = file.stat() 19 | size = stats.st_size 20 | 21 | step = largeStep 22 | sizes = largeSizes 23 | if size < stepThreshold: 24 | step = smallStep 25 | sizes = smallSizes 26 | 27 | size = int(size / step) 28 | if size not in sizes: 29 | sizes.add(size) 30 | else: 31 | print("Removed", file) 32 | os.remove(file) 33 | else: 34 | os.remove(file) 35 | except: 36 | continue 37 | -------------------------------------------------------------------------------- /classes/testing/LookupSwitch.java: -------------------------------------------------------------------------------- 1 | public class LookupSwitch { 2 | static int i = 0; 3 | 4 | public static void main(String[] args) { 5 | switch (i) { 6 | case -1: 7 | throw new IllegalStateException("-1"); 8 | case 0: 9 | return; 10 | case 1: 11 | throw new IllegalStateException("1"); 12 | case 4: 13 | throw new IllegalStateException("4"); 14 | case 94132: 15 | throw new IllegalStateException("94132"); 16 | default: 17 | throw new IllegalStateException("default"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/read/.gitignore: -------------------------------------------------------------------------------- 1 | TestClass.class 2 | -------------------------------------------------------------------------------- /examples/read/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "read" 3 | version = "0.1.0" 4 | authors = ["x4e "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | classfile-rs = { path = "../../" } 11 | -------------------------------------------------------------------------------- /examples/read/TestClass.java: -------------------------------------------------------------------------------- 1 | public class TestClass { 2 | public static void main(String[] args) { 3 | System.out.println("Hello, World!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/read/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | fn main() { 4 | Command::new("javac") 5 | .args(&["TestClass.java"]) 6 | .output() 7 | .unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /examples/read/src/main.rs: -------------------------------------------------------------------------------- 1 | use classfile::classfile::ClassFile; 2 | use classfile::error::Result; 3 | 4 | use std::fs::{File}; 5 | use std::io::{BufReader}; 6 | 7 | 8 | /// This example will read a class file from disc and print it 9 | fn main() -> Result<()> { 10 | let f = File::open("TestClass.class").unwrap(); 11 | let mut reader = BufReader::new(f); 12 | let class = ClassFile::parse(&mut reader)?; 13 | println!("{:#x?}", class); 14 | 15 | Ok(()) 16 | } 17 | /// Output: 18 | /// ClassFile { 19 | // magic: 0xcafebabe, 20 | // version: ClassVersion { 21 | // major: JAVA_15, 22 | // minor: 0x0, 23 | // }, 24 | // access_flags: PUBLIC, 25 | // this_class: "TestClass", 26 | // super_class: Some( 27 | // "java/lang/Object", 28 | // ), 29 | // interfaces: [], 30 | // fields: [], 31 | // methods: [ 32 | // Method { 33 | // access_flags: PUBLIC, 34 | // name: "", 35 | // descriptor: "()V", 36 | // signature: None, 37 | // exceptions: [], 38 | // code: Some( 39 | // CodeAttribute { 40 | // max_stack: 0x1, 41 | // max_locals: 0x1, 42 | // insns: [ 43 | // LocalLoadInsn { 44 | // kind: Reference, 45 | // index: 0x0, 46 | // }, 47 | // InvokeInsn { 48 | // kind: Special, 49 | // class: "java/lang/Object", 50 | // name: "", 51 | // descriptor: "()V", 52 | // interface_method: false, 53 | // }, 54 | // ReturnInsn { 55 | // kind: Void, 56 | // }, 57 | // ], 58 | // exceptions: [], 59 | // attributes: [ 60 | // Unknown( 61 | // UnknownAttribute { 62 | // name: "LineNumberTable", 63 | // buf: [ 64 | // 0x0, 65 | // 0x1, 66 | // 0x0, 67 | // 0x0, 68 | // 0x0, 69 | // 0x1, 70 | // ], 71 | // }, 72 | // ), 73 | // ], 74 | // }, 75 | // ), 76 | // attributes: [], 77 | // }, 78 | // Method { 79 | // access_flags: PUBLIC | STATIC, 80 | // name: "main", 81 | // descriptor: "([Ljava/lang/String;)V", 82 | // signature: None, 83 | // exceptions: [], 84 | // code: Some( 85 | // CodeAttribute { 86 | // max_stack: 0x2, 87 | // max_locals: 0x1, 88 | // insns: [ 89 | // GetFieldInsn { 90 | // instance: false, 91 | // class: "java/lang/System", 92 | // name: "out", 93 | // descriptor: "Ljava/io/PrintStream;", 94 | // }, 95 | // LdcInsn { 96 | // constant: String( 97 | // "Hello, World!", 98 | // ), 99 | // }, 100 | // InvokeInsn { 101 | // kind: Instance, 102 | // class: "java/io/PrintStream", 103 | // name: "println", 104 | // descriptor: "(Ljava/lang/String;)V", 105 | // interface_method: false, 106 | // }, 107 | // ReturnInsn { 108 | // kind: Void, 109 | // }, 110 | // ], 111 | // exceptions: [], 112 | // attributes: [ 113 | // Unknown( 114 | // UnknownAttribute { 115 | // name: "LineNumberTable", 116 | // buf: [ 117 | // 0x0, 118 | // 0x2, 119 | // 0x0, 120 | // 0x0, 121 | // 0x0, 122 | // 0x3, 123 | // 0x0, 124 | // 0x8, 125 | // 0x0, 126 | // 0x4, 127 | // ], 128 | // }, 129 | // ), 130 | // ], 131 | // }, 132 | // ), 133 | // attributes: [], 134 | // }, 135 | // ], 136 | // attributes: [ 137 | // SourceFile( 138 | // SourceFileAttribute { 139 | // source_file: "TestClass.java", 140 | // }, 141 | // ), 142 | // ], 143 | // } 144 | -------------------------------------------------------------------------------- /src/access.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::Serializable; 4 | use std::io::{Read, Write}; 5 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 6 | use crate::error::Result; 7 | use bitflags::bitflags; 8 | 9 | bitflags! { 10 | pub struct ClassAccessFlags: u16 { 11 | const PUBLIC = 0x0001; 12 | const PRIVATE = 0x0002; 13 | const PROTECTED = 0x0004; 14 | const STATIC = 0x0008; 15 | const FINAL = 0x0010; 16 | const INTERFACE = 0x0200; 17 | const ABSTRACT = 0x0400; 18 | const SYNTHETIC = 0x1000; 19 | const ANNOTATION = 0x2000; 20 | const ENUM = 0x4000; 21 | } 22 | } 23 | 24 | impl ClassAccessFlags { 25 | pub fn clear(&mut self) { 26 | self.bits = 0; 27 | } 28 | } 29 | 30 | impl Serializable for ClassAccessFlags { 31 | fn parse(rdr: &mut R) -> Result { 32 | let bits = rdr.read_u16::()?; 33 | Ok(ClassAccessFlags::from_bits_truncate(bits)) 34 | } 35 | 36 | fn write(&self, wtr: &mut W) -> Result<()> { 37 | wtr.write_u16::(self.bits)?; 38 | Ok(()) 39 | } 40 | } 41 | 42 | bitflags! { 43 | pub struct FieldAccessFlags: u16 { 44 | const PUBLIC = 0x0001; 45 | const PRIVATE = 0x0002; 46 | const PROTECTED = 0x0004; 47 | const STATIC = 0x0008; 48 | const FINAL = 0x0010; 49 | const VOLATILE = 0x0040; 50 | const TRANSIENT = 0x0080; 51 | const SYNTHETIC = 0x1000; 52 | const ENUM = 0x4000; 53 | } 54 | } 55 | 56 | impl FieldAccessFlags { 57 | pub fn clear(&mut self) { 58 | self.bits = 0; 59 | } 60 | } 61 | 62 | impl Serializable for FieldAccessFlags { 63 | fn parse(rdr: &mut R) -> Result { 64 | let bits = rdr.read_u16::()?; 65 | Ok(FieldAccessFlags::from_bits_truncate(bits)) 66 | } 67 | 68 | fn write(&self, wtr: &mut W) -> Result<()> { 69 | wtr.write_u16::(self.bits)?; 70 | Ok(()) 71 | } 72 | } 73 | 74 | bitflags! { 75 | pub struct MethodAccessFlags: u16 { 76 | const PUBLIC = 0x0001; 77 | const PRIVATE = 0x0002; 78 | const PROTECTED = 0x0004; 79 | const STATIC = 0x0008; 80 | const FINAL = 0x0010; 81 | const SYNCHRONIZED = 0x0020; 82 | const BRIDGE = 0x0040; 83 | const VARARGS = 0x0080; 84 | const NATIVE = 0x0100; 85 | const ABSTRACT = 0x0400; 86 | const STRICT = 0x0800; 87 | const SYNTHETIC = 0x1000; 88 | } 89 | } 90 | 91 | impl MethodAccessFlags { 92 | pub fn clear(&mut self) { 93 | self.bits = 0; 94 | } 95 | } 96 | 97 | impl Serializable for MethodAccessFlags { 98 | fn parse(rdr: &mut R) -> Result { 99 | let bits = rdr.read_u16::()?; 100 | Ok(MethodAccessFlags::from_bits_truncate(bits)) 101 | } 102 | 103 | fn write(&self, wtr: &mut W) -> Result<()> { 104 | wtr.write_u16::(self.bits)?; 105 | Ok(()) 106 | } 107 | } 108 | 109 | bitflags! { 110 | pub struct InnerClassAccessFlags: u16 { 111 | const PUBLIC = 0x0001; 112 | const PRIVATE = 0x0002; 113 | const PROTECTED = 0x0004; 114 | const STATIC = 0x0008; 115 | const FINAL = 0x0010; 116 | const INTERFACE = 0x0200; 117 | const ABSTRACT = 0x0400; 118 | const SYNTHETIC = 0x1000; 119 | const ANNOTATION = 0x2000; 120 | const ENUM = 0x4000; 121 | } 122 | } 123 | 124 | impl InnerClassAccessFlags { 125 | pub fn clear(&mut self) { 126 | self.bits = 0; 127 | } 128 | } 129 | 130 | impl Serializable for InnerClassAccessFlags { 131 | fn parse(rdr: &mut R) -> Result { 132 | let bits = rdr.read_u16::()?; 133 | Ok(InnerClassAccessFlags::from_bits_truncate(bits)) 134 | } 135 | 136 | fn write(&self, wtr: &mut W) -> Result<()> { 137 | wtr.write_u16::(self.bits)?; 138 | Ok(()) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use crate::types::Type; 2 | use derive_more::Constructor; 3 | use std::collections::{BTreeMap}; 4 | use std::fmt::{Debug, Formatter}; 5 | use enum_display_derive::DisplayDebug; 6 | 7 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 8 | pub enum PrimitiveType { 9 | Boolean, 10 | Byte, 11 | Char, 12 | Short, 13 | Int, 14 | Long, 15 | Float, 16 | Double 17 | } 18 | 19 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 20 | pub enum OpType { 21 | Reference, 22 | Boolean, 23 | Byte, 24 | Char, 25 | Short, 26 | Int, 27 | Long, 28 | Float, 29 | Double 30 | } 31 | 32 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 33 | pub enum ReturnType { 34 | Void, 35 | Reference, 36 | Boolean, 37 | Byte, 38 | Char, 39 | Short, 40 | Int, 41 | Long, 42 | Float, 43 | Double 44 | } 45 | 46 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 47 | pub enum IntegerType { 48 | Int, 49 | Long 50 | } 51 | 52 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 53 | pub struct LabelInsn { 54 | /// unique identifier 55 | pub(crate) id: u32 56 | } 57 | 58 | impl LabelInsn { 59 | pub(crate) fn new(id: u32) -> Self { 60 | LabelInsn { id } 61 | } 62 | } 63 | 64 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 65 | pub struct ArrayLoadInsn { 66 | pub kind: Type, 67 | } 68 | 69 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 70 | pub struct ArrayStoreInsn { 71 | pub kind: Type, 72 | } 73 | 74 | #[derive(Constructor, Clone, Debug, PartialEq)] 75 | pub struct LdcInsn { 76 | pub constant: LdcType 77 | } 78 | 79 | #[derive(Clone, Debug, PartialEq)] 80 | pub enum LdcType { 81 | Null, 82 | String(String), 83 | Int(i32), 84 | Float(f32), 85 | Long(i64), 86 | Double(f64), 87 | Class(String), 88 | /// Method Descriptor (java.lang.invoke.MethodType) 89 | MethodType(String), 90 | /// TODO: Method Handle (java.lang.invoke.MethodHandle) 91 | MethodHandle(), 92 | // TODO: Constant_Dynamic 93 | Dynamic() 94 | } 95 | 96 | /// Loads a value from the local array slot 97 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 98 | pub struct LocalLoadInsn { 99 | pub kind: OpType, 100 | pub index: u16 // u8 with normal load, u16 with wide load 101 | } 102 | 103 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 104 | pub struct LocalStoreInsn { 105 | pub kind: OpType, 106 | pub index: u16 // u8 with normal load, u16 with wide load 107 | } 108 | 109 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 110 | pub struct NewArrayInsn { 111 | pub kind: Type, 112 | } 113 | 114 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 115 | pub struct ReturnInsn { 116 | pub kind: ReturnType 117 | } 118 | 119 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 120 | pub struct ArrayLengthInsn {} 121 | 122 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 123 | pub struct ThrowInsn {} 124 | 125 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 126 | pub struct CheckCastInsn { 127 | pub kind: String 128 | } 129 | 130 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 131 | pub struct ConvertInsn { 132 | pub from: PrimitiveType, 133 | pub to: PrimitiveType 134 | } 135 | 136 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 137 | pub struct AddInsn { 138 | pub kind: PrimitiveType 139 | } 140 | 141 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 142 | pub struct CompareInsn { 143 | pub kind: PrimitiveType, 144 | /// If both values are NAN and this flag is set, 1 will be pushed. Otherwise -1 will be pushed. 145 | pub pos_on_nan: bool 146 | } 147 | 148 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 149 | pub struct DivideInsn { 150 | pub kind: PrimitiveType 151 | } 152 | 153 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 154 | pub struct MultiplyInsn { 155 | pub kind: PrimitiveType 156 | } 157 | 158 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 159 | pub struct NegateInsn { 160 | pub kind: PrimitiveType 161 | } 162 | 163 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 164 | pub struct RemainderInsn { 165 | pub kind: PrimitiveType 166 | } 167 | 168 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 169 | pub struct SubtractInsn { 170 | pub kind: PrimitiveType 171 | } 172 | 173 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 174 | pub struct AndInsn { 175 | pub kind: IntegerType 176 | } 177 | 178 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 179 | pub struct OrInsn { 180 | pub kind: IntegerType 181 | } 182 | 183 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 184 | pub struct XorInsn { 185 | pub kind: IntegerType 186 | } 187 | 188 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 189 | pub struct ShiftLeftInsn { 190 | pub kind: IntegerType 191 | } 192 | 193 | /// Arithmetically shift right 194 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 195 | pub struct ShiftRightInsn { 196 | pub kind: IntegerType 197 | } 198 | 199 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 200 | pub struct LogicalShiftRightInsn { 201 | pub kind: IntegerType 202 | } 203 | 204 | /// duplicates the value at the top of the stack 205 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 206 | pub struct DupInsn { 207 | /// The number of items to duplicate 208 | pub num: u8, 209 | /// The number of slots down to insert it 210 | pub down: u8 211 | } 212 | 213 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 214 | pub struct PopInsn { 215 | /// if false, pop a single 32bit item off the stack (not long or double) 216 | /// if true, pop either two 32bit items, or one 64bit item (long or double) 217 | pub pop_two: bool 218 | } 219 | 220 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 221 | pub struct GetFieldInsn { 222 | /// Is this field an instance or static field? 223 | pub instance: bool, 224 | /// The declaring class 225 | pub class: String, 226 | /// The field name 227 | pub name: String, 228 | /// The field descriptor 229 | pub descriptor: String, 230 | } 231 | 232 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 233 | pub struct PutFieldInsn { 234 | /// Is this field an instance or static field? 235 | pub instance: bool, 236 | /// The declaring class 237 | pub class: String, 238 | /// The field name 239 | pub name: String, 240 | /// The field descriptor 241 | pub descriptor: String, 242 | } 243 | 244 | /// Unconditional Jump 245 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 246 | pub struct JumpInsn { 247 | pub jump_to: LabelInsn 248 | } 249 | 250 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 251 | pub struct ConditionalJumpInsn { 252 | pub condition: JumpCondition, 253 | pub jump_to: LabelInsn 254 | } 255 | 256 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 257 | pub enum JumpCondition { 258 | /// The reference at the top of the stack is null 259 | IsNull, 260 | /// The reference at the top of the stack is not null 261 | NotNull, 262 | /// The two references at the top of the stack are the same 263 | ReferencesEqual, 264 | /// The two references at the top of the stack are not the same 265 | ReferencesNotEqual, 266 | /// The two ints at the top of the stack are equal 267 | IntsEq, 268 | /// The two ints at the top of the stack are not equal 269 | IntsNotEq, 270 | /// The int second on the stack is less than the int at the top of the stack 271 | IntsLessThan, 272 | /// The int second on the stack is less than or equal to the int at the top of the stack 273 | IntsLessThanOrEq, 274 | /// The int second on the stack is greater than the int at the top of the stack 275 | IntsGreaterThan, 276 | /// The int second on the stack is greater than or equal to the int at the top of the stack 277 | IntsGreaterThanOrEq, 278 | /// The int at the top of the stack is 0 279 | IntEqZero, 280 | /// The int at the top of the stack is not 0 281 | IntNotEqZero, 282 | /// The int at the top of the stack is less than 0 283 | IntLessThanZero, 284 | /// The int at the top of the stack is less than or equal to 0 285 | IntLessThanOrEqZero, 286 | /// The int at the top of the stack is greater than 0 287 | IntGreaterThanZero, 288 | /// The int at the top of the stack is less greater or equal to 0 289 | IntGreaterThanOrEqZero, 290 | } 291 | 292 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 293 | pub struct IncrementIntInsn { 294 | /// Index of the local variable 295 | pub index: u16, 296 | /// Amount to increment by 297 | pub amount: i16 298 | } 299 | 300 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 301 | pub struct InstanceOfInsn { 302 | pub class: String 303 | } 304 | 305 | #[derive(Constructor, Clone, Debug, PartialEq)] 306 | pub struct InvokeDynamicInsn { 307 | pub name: String, 308 | pub descriptor: String, 309 | pub bootstrap_type: BootstrapMethodType, 310 | pub bootstrap_class: String, 311 | pub bootstrap_method: String, 312 | pub bootstrap_descriptor: String, 313 | pub bootstrap_arguments: Vec 314 | } 315 | 316 | #[derive(Clone, Debug, PartialEq)] 317 | pub enum BootstrapArgument { 318 | Int(i32), 319 | Float(f32), 320 | Long(i64), 321 | Double(f64), 322 | Class(String) 323 | // TODO: Continue. Do we have to do this for every constant type? Spec seems to suggest so 324 | } 325 | 326 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 327 | pub enum BootstrapMethodType { 328 | InvokeStatic, 329 | NewInvokeSpecial 330 | } 331 | 332 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 333 | pub struct InvokeInsn { 334 | pub kind: InvokeType, 335 | pub class: String, 336 | pub name: String, 337 | pub descriptor: String, 338 | pub interface_method: bool 339 | } 340 | 341 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 342 | pub enum InvokeType { 343 | Instance, 344 | Static, 345 | Interface, 346 | Special 347 | } 348 | 349 | #[derive(Clone, PartialEq, Eq)] 350 | pub struct LookupSwitchInsn { 351 | pub default: LabelInsn, 352 | pub(crate) cases: BTreeMap 353 | } 354 | 355 | impl LookupSwitchInsn { 356 | pub fn new(default: LabelInsn) -> Self { 357 | LookupSwitchInsn { 358 | default, 359 | cases: BTreeMap::new() 360 | } 361 | } 362 | 363 | pub fn get(&self, case: i32) -> Option { 364 | self.cases.get(&case).cloned() 365 | } 366 | } 367 | 368 | impl Debug for LookupSwitchInsn { 369 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 370 | struct DebugCases<'u> { 371 | tbl: &'u LookupSwitchInsn 372 | } 373 | impl <'u> Debug for DebugCases<'u> { 374 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 375 | let mut map = f.debug_map(); 376 | map.entry(&"default", &self.tbl.default); 377 | for (index, case) in self.tbl.cases.iter() { 378 | map.entry(&index, case); 379 | } 380 | map.finish() 381 | } 382 | } 383 | 384 | f.debug_struct("LookupSwitchInsn") 385 | .field("cases", &DebugCases{ tbl: &self }) 386 | .finish() 387 | } 388 | } 389 | 390 | #[derive(Constructor, Clone, PartialEq, Eq)] 391 | pub struct TableSwitchInsn { 392 | pub default: LabelInsn, 393 | pub(crate) low: i32, 394 | pub(crate) cases: Vec 395 | } 396 | 397 | impl TableSwitchInsn { 398 | #[allow(dead_code)] 399 | pub fn get(&self, case: i32) -> Option { 400 | if let Some(x) = self.cases.get((case - self.low) as usize) { 401 | Some(*x) 402 | } else { 403 | None 404 | } 405 | } 406 | } 407 | 408 | impl Debug for TableSwitchInsn { 409 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 410 | struct DebugCases<'u> { 411 | tbl: &'u TableSwitchInsn 412 | } 413 | impl <'u> Debug for DebugCases<'u> { 414 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 415 | let mut map = f.debug_map(); 416 | map.entry(&"default", &self.tbl.default); 417 | for (index, case) in self.tbl.cases.iter().enumerate() { 418 | map.entry(&(index as i32 + self.tbl.low), case); 419 | } 420 | map.finish() 421 | } 422 | } 423 | 424 | f.debug_struct("TableSwitchInsn") 425 | .field("cases", &DebugCases{ tbl: &self }) 426 | .finish() 427 | } 428 | } 429 | 430 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 431 | pub struct MonitorEnterInsn {} 432 | 433 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 434 | pub struct MonitorExitInsn {} 435 | 436 | /// New multi dimensional object array 437 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 438 | pub struct MultiNewArrayInsn { 439 | pub kind: String, 440 | pub dimensions: u8 441 | } 442 | 443 | #[derive(Constructor, Clone, Debug, PartialEq, Eq)] 444 | pub struct NewObjectInsn { 445 | pub kind: String 446 | } 447 | 448 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 449 | pub struct NopInsn {} 450 | 451 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 452 | pub struct SwapInsn {} 453 | 454 | /// Implementation dependent insn 455 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 456 | pub struct ImpDep1Insn {} 457 | 458 | /// Implementation dependent insn 459 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 460 | pub struct ImpDep2Insn {} 461 | 462 | /// Used by debuggers 463 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq)] 464 | pub struct BreakPointInsn {} 465 | 466 | #[derive(Clone, PartialEq, DisplayDebug)] 467 | pub enum Insn { 468 | Label(LabelInsn), 469 | ArrayLoad(ArrayLoadInsn), 470 | ArrayStore(ArrayStoreInsn), 471 | Ldc(LdcInsn), 472 | LocalLoad(LocalLoadInsn), 473 | LocalStore(LocalStoreInsn), 474 | NewArray(NewArrayInsn), 475 | Return(ReturnInsn), 476 | ArrayLength(ArrayLengthInsn), 477 | Throw(ThrowInsn), 478 | CheckCast(CheckCastInsn), 479 | Convert(ConvertInsn), 480 | Add(AddInsn), 481 | Compare(CompareInsn), 482 | Divide(DivideInsn), 483 | Multiply(MultiplyInsn), 484 | Negate(NegateInsn), 485 | Remainder(RemainderInsn), 486 | Subtract(SubtractInsn), 487 | And(AndInsn), 488 | Or(OrInsn), 489 | Xor(XorInsn), 490 | ShiftLeft(ShiftLeftInsn), 491 | ShiftRight(ShiftRightInsn), 492 | LogicalShiftRight(LogicalShiftRightInsn), 493 | Dup(DupInsn), 494 | Pop(PopInsn), 495 | GetField(GetFieldInsn), 496 | PutField(PutFieldInsn), 497 | Jump(JumpInsn), 498 | ConditionalJump(ConditionalJumpInsn), 499 | IncrementInt(IncrementIntInsn), 500 | InstanceOf(InstanceOfInsn), 501 | InvokeDynamic(InvokeDynamicInsn), 502 | Invoke(InvokeInsn), 503 | LookupSwitch(LookupSwitchInsn), 504 | TableSwitch(TableSwitchInsn), 505 | MonitorEnter(MonitorEnterInsn), 506 | MonitorExit(MonitorExitInsn), 507 | MultiNewArray(MultiNewArrayInsn), 508 | NewObject(NewObjectInsn), 509 | Nop(NopInsn), 510 | Swap(SwapInsn), 511 | ImpDep1(ImpDep1Insn), 512 | ImpDep2(ImpDep2Insn), 513 | BreakPoint(BreakPointInsn) 514 | } 515 | -------------------------------------------------------------------------------- /src/attributes.rs: -------------------------------------------------------------------------------- 1 | use crate::constantpool::{ConstantPool, ConstantType, ConstantPoolWriter}; 2 | use crate::version::{MajorVersion, ClassVersion}; 3 | use crate::code::CodeAttribute; 4 | use crate::error::{Result, ParserError}; 5 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 6 | use std::io::{Write, Read, Cursor}; 7 | use derive_more::Constructor; 8 | use crate::ast::LabelInsn; 9 | use crate::utils::{ReadUtils, MapUtils}; 10 | use std::collections::HashMap; 11 | 12 | #[allow(non_snake_case)] 13 | pub mod Attributes { 14 | use std::io::{Read, Write}; 15 | use crate::constantpool::{ConstantPool, ConstantPoolWriter}; 16 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 17 | use crate::version::{ClassVersion}; 18 | use crate::attributes::{Attribute, AttributeSource}; 19 | use std::collections::HashMap; 20 | use crate::ast::LabelInsn; 21 | 22 | pub fn parse(rdr: &mut R, source: AttributeSource, version: &ClassVersion, constant_pool: &ConstantPool, pc_label_map: &mut Option>) -> crate::Result> { 23 | let num_attributes = rdr.read_u16::()? as usize; 24 | let mut attributes: Vec = Vec::with_capacity(num_attributes); 25 | for _ in 0..num_attributes { 26 | attributes.push(Attribute::parse(rdr, &source, version, constant_pool, pc_label_map.as_mut())?); 27 | } 28 | Ok(attributes) 29 | } 30 | 31 | pub fn write(wtr: &mut W, attributes: &[Attribute], constant_pool: &mut ConstantPoolWriter, label_pc_map: Option<&HashMap>) -> crate::Result<()> { 32 | wtr.write_u16::(attributes.len() as u16)?; 33 | for attribute in attributes.iter() { 34 | attribute.write(wtr, constant_pool, &label_pc_map)?; 35 | } 36 | Ok(()) 37 | } 38 | } 39 | 40 | #[derive(Clone, Debug, PartialEq)] 41 | pub struct ConstantValueAttribute { 42 | value: ConstantValue 43 | } 44 | 45 | #[derive(Clone, Debug, PartialEq)] 46 | pub enum ConstantValue { 47 | Long(i64), 48 | Float(f32), 49 | Double(f64), 50 | Int(i32), 51 | String(String) 52 | } 53 | 54 | impl ConstantValueAttribute { 55 | pub fn parse(constant_pool: &ConstantPool, buf: Vec) -> Result { 56 | let index = buf.as_slice().read_u16::()?; 57 | let value = match constant_pool.get(index)? { 58 | ConstantType::Long(x) => ConstantValue::Long(x.inner()), 59 | ConstantType::Float(x) => ConstantValue::Float(x.inner()), 60 | ConstantType::Double(x) => ConstantValue::Double(x.inner()), 61 | ConstantType::Integer(x) => ConstantValue::Int(x.inner()), 62 | ConstantType::String(x) => ConstantValue::String(constant_pool.utf8(x.utf_index)?.str.clone()), 63 | x => panic!("Invalid constant value type {:#?} at index {}", x, index) 64 | }; 65 | Ok(ConstantValueAttribute { 66 | value 67 | }) 68 | } 69 | 70 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 71 | let const_ref = match self.value.clone() { 72 | ConstantValue::Long(x) => constant_pool.long(x), 73 | ConstantValue::Float(x) => constant_pool.float(x), 74 | ConstantValue::Double(x) => constant_pool.double(x), 75 | ConstantValue::Int(x) => constant_pool.integer(x), 76 | ConstantValue::String(x) => { 77 | let utf = constant_pool.utf8(x); 78 | constant_pool.string(utf) 79 | } 80 | }; 81 | wtr.write_u16::(const_ref)?; // cp ref 82 | Ok(()) 83 | } 84 | } 85 | 86 | #[derive(Clone, Debug, PartialEq)] 87 | pub struct SignatureAttribute { 88 | pub signature: String 89 | } 90 | 91 | impl SignatureAttribute { 92 | pub fn new(signature: String) -> Self { 93 | SignatureAttribute { 94 | signature 95 | } 96 | } 97 | 98 | pub fn parse(constant_pool: &ConstantPool, buf: Vec) -> Result { 99 | let index = buf.as_slice().read_u16::()?; 100 | let signature = constant_pool.utf8(index)?.str.clone(); 101 | Ok(SignatureAttribute { 102 | signature 103 | }) 104 | } 105 | 106 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 107 | wtr.write_u16::(constant_pool.utf8(self.signature.clone()))?; // cp ref 108 | Ok(()) 109 | } 110 | } 111 | 112 | #[derive(Clone, Debug, PartialEq)] 113 | pub struct ExceptionsAttribute { 114 | pub exceptions: Vec 115 | } 116 | 117 | impl ExceptionsAttribute { 118 | pub fn new(exceptions: Vec) -> Self { 119 | ExceptionsAttribute { 120 | exceptions 121 | } 122 | } 123 | 124 | pub fn parse(constant_pool: &ConstantPool, buf: Vec) -> Result { 125 | let mut slice = buf.as_slice(); 126 | let num_exceptions = slice.read_u16::()?; 127 | let mut exceptions: Vec = Vec::with_capacity(num_exceptions as usize); 128 | for _ in 0..num_exceptions { 129 | exceptions.push(constant_pool.utf8(constant_pool.class(slice.read_u16::()?)?.name_index)?.str.clone()); 130 | } 131 | Ok(ExceptionsAttribute { 132 | exceptions 133 | }) 134 | } 135 | 136 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 137 | let num_exceptions = self.exceptions.len(); 138 | wtr.write_u16::(num_exceptions as u16)?; 139 | for exception in self.exceptions.iter() { 140 | wtr.write_u16::(constant_pool.utf8(exception.clone()))?; 141 | } 142 | Ok(()) 143 | } 144 | } 145 | 146 | #[derive(Constructor, Clone, Debug, PartialEq)] 147 | pub struct UnknownAttribute { 148 | pub name: String, 149 | pub buf: Vec 150 | } 151 | 152 | impl UnknownAttribute { 153 | pub fn parse(name: String, buf: Vec) -> Result { 154 | Ok(UnknownAttribute::new(name, buf)) 155 | } 156 | 157 | pub fn write(&self, wtr: &mut T, _constant_pool: &mut ConstantPoolWriter) -> Result<()> { 158 | wtr.write_all(self.buf.as_slice())?; 159 | Ok(()) 160 | } 161 | 162 | pub fn len(&self) -> usize { 163 | self.buf.len() 164 | } 165 | 166 | pub fn is_empty(&self) -> bool { 167 | self.buf.is_empty() 168 | } 169 | } 170 | 171 | #[derive(Clone, Debug, PartialEq)] 172 | pub struct SourceFileAttribute { 173 | pub source_file: String 174 | } 175 | 176 | impl SourceFileAttribute { 177 | pub fn parse(constant_pool: &ConstantPool, buf: Vec) -> Result { 178 | let index = buf.as_slice().read_u16::()?; 179 | let source_file = constant_pool.utf8(index)?.str.clone(); 180 | Ok(SourceFileAttribute { 181 | source_file 182 | }) 183 | } 184 | 185 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 186 | wtr.write_u16::(constant_pool.utf8(self.source_file.clone()))?; 187 | Ok(()) 188 | } 189 | } 190 | 191 | #[derive(Clone, Debug, PartialEq)] 192 | pub struct LocalVariableTableAttribute { 193 | pub variables: Vec 194 | } 195 | 196 | #[derive(Clone, Debug, PartialEq)] 197 | pub struct LocalVariable { 198 | pub start: LabelInsn, 199 | pub end: LabelInsn, 200 | pub name: String, 201 | pub descriptor: String, 202 | pub index: u16 203 | } 204 | 205 | impl LocalVariableTableAttribute { 206 | pub fn parse(constant_pool: &ConstantPool, buf: Vec, pc_label_map: &mut HashMap) -> Result { 207 | let mut buf = Cursor::new(buf); 208 | let num_vars = buf.read_u16::()? as usize; 209 | let mut variables: Vec = Vec::with_capacity(num_vars); 210 | for _ in 0..num_vars { 211 | variables.push(LocalVariable::parse(constant_pool, &mut buf, pc_label_map)?) 212 | } 213 | Ok(LocalVariableTableAttribute { 214 | variables 215 | }) 216 | } 217 | 218 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter, label_pc_map: &HashMap) -> Result<()> { 219 | wtr.write_u16::(self.variables.len() as u16)?; 220 | for var in self.variables.iter() { 221 | var.write(wtr, constant_pool, label_pc_map)?; 222 | } 223 | Ok(()) 224 | } 225 | } 226 | 227 | impl LocalVariable { 228 | pub fn parse(constant_pool: &ConstantPool, buf: &mut Cursor>, pc_label_map: &mut HashMap) -> Result { 229 | let start_pc = buf.read_u16::()? as u32; 230 | let end_pc = start_pc + (buf.read_u16::()? as u32); 231 | pc_label_map.insert_if_not_present(start_pc, LabelInsn::new(pc_label_map.len() as u32)); 232 | pc_label_map.insert_if_not_present(end_pc, LabelInsn::new(pc_label_map.len() as u32)); 233 | 234 | let name = constant_pool.utf8_inner(buf.read_u16::()?)?; 235 | let descriptor = constant_pool.utf8_inner(buf.read_u16::()?)?; 236 | let index = buf.read_u16::()?; 237 | 238 | Ok(LocalVariable { 239 | start: *pc_label_map.get(&start_pc).ok_or_else(ParserError::unmapped_label)?, 240 | end: *pc_label_map.get(&end_pc).ok_or_else(ParserError::unmapped_label)?, 241 | name, 242 | descriptor, 243 | index 244 | }) 245 | } 246 | 247 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter, label_pc_map: &HashMap) -> Result<()> { 248 | let start_pc = *label_pc_map.get(&self.start).ok_or_else(ParserError::unmapped_label)?; 249 | wtr.write_u16::(start_pc as u16)?; 250 | let end_pc = *label_pc_map.get(&self.end).ok_or_else(ParserError::unmapped_label)?; 251 | wtr.write_u16::((end_pc - start_pc) as u16)?; 252 | wtr.write_u16::(constant_pool.utf8(self.name.clone()))?; 253 | wtr.write_u16::(constant_pool.utf8(self.descriptor.clone()))?; 254 | 255 | wtr.write_u16::(self.index)?; 256 | Ok(()) 257 | } 258 | } 259 | 260 | #[derive(Clone, Debug, PartialEq)] 261 | pub enum Attribute { 262 | ConstantValue(ConstantValueAttribute), 263 | Signature(SignatureAttribute), 264 | Code(CodeAttribute), 265 | Exceptions(ExceptionsAttribute), 266 | SourceFile(SourceFileAttribute), 267 | LocalVariableTable(LocalVariableTableAttribute), 268 | Unknown(UnknownAttribute) 269 | } 270 | 271 | impl Attribute { 272 | pub fn parse(rdr: &mut R, source: &AttributeSource, version: &ClassVersion, constant_pool: &ConstantPool, pc_label_map: Option<&mut HashMap>) -> Result { 273 | let name = constant_pool.utf8(rdr.read_u16::()?)?.str.clone(); 274 | let attribute_length = rdr.read_u32::()? as usize; 275 | let buf: Vec = rdr.read_nbytes(attribute_length as usize)?; 276 | let str = name.as_str(); 277 | 278 | let attr = match source { 279 | AttributeSource::Class => { 280 | if str == "SourceFile" { 281 | Attribute::SourceFile(SourceFileAttribute::parse(constant_pool, buf)?) 282 | } else { 283 | Attribute::Unknown(UnknownAttribute::parse(name, buf)?) 284 | } 285 | }, 286 | AttributeSource::Field => { 287 | if str == "ConstantValue" { 288 | Attribute::ConstantValue(ConstantValueAttribute::parse(constant_pool, buf)?) 289 | } else if str == "Signature" && version.major >= MajorVersion::JAVA_5 { 290 | Attribute::Signature(SignatureAttribute::parse(constant_pool, buf)?) 291 | } else { 292 | Attribute::Unknown(UnknownAttribute::parse(name, buf)?) 293 | } 294 | }, 295 | AttributeSource::Method => { 296 | if str == "Code" { 297 | Attribute::Code(CodeAttribute::parse(version, constant_pool, buf)?) 298 | } else if str == "Signature" && version.major >= MajorVersion::JAVA_5 { 299 | Attribute::Signature(SignatureAttribute::parse(constant_pool, buf)?) 300 | } else if str == "Exceptions" { 301 | Attribute::Exceptions(ExceptionsAttribute::parse(constant_pool, buf)?) 302 | } else { 303 | Attribute::Unknown(UnknownAttribute::parse(name, buf)?) 304 | } 305 | } 306 | AttributeSource::Code => { 307 | let pc_label_map = pc_label_map.unwrap(); 308 | if str == "LocalVariableTable" { 309 | Attribute::LocalVariableTable(LocalVariableTableAttribute::parse(constant_pool, buf, pc_label_map)?) 310 | //} else if str == "LocalVariableTypeTable" && version.major >= MajorVersion::JAVA_5 { 311 | 312 | } else { 313 | Attribute::Unknown(UnknownAttribute::parse(name, buf)?) 314 | } 315 | } 316 | }; 317 | Ok(attr) 318 | } 319 | 320 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter, label_pc_map: &Option<&HashMap>) -> Result<()> { 321 | match self { 322 | Attribute::ConstantValue(t) => { 323 | let mut buf: Vec = Vec::new(); 324 | wtr.write_u16::(constant_pool.utf8("ConstantValue"))?; 325 | t.write(&mut buf, constant_pool)?; 326 | wtr.write_u32::(buf.len() as u32)?; 327 | wtr.write_all(buf.as_slice())?; 328 | }, 329 | Attribute::Signature(t) => { 330 | let mut buf: Vec = Vec::new(); 331 | wtr.write_u16::(constant_pool.utf8("Signature"))?; 332 | t.write(&mut buf, constant_pool)?; 333 | wtr.write_u32::(buf.len() as u32)?; 334 | wtr.write_all(buf.as_slice())?; 335 | }, 336 | Attribute::Code(t) => { 337 | let mut buf: Vec = Vec::new(); 338 | wtr.write_u16::(constant_pool.utf8("Code"))?; 339 | t.write(&mut buf, constant_pool)?; 340 | wtr.write_u32::(buf.len() as u32)?; 341 | wtr.write_all(buf.as_slice())?; 342 | }, 343 | Attribute::Exceptions(t) => { 344 | let mut buf: Vec = Vec::new(); 345 | wtr.write_u16::(constant_pool.utf8("Exceptions"))?; 346 | t.write(&mut buf, constant_pool)?; 347 | wtr.write_u32::(buf.len() as u32)?; 348 | wtr.write_all(buf.as_slice())?; 349 | }, 350 | Attribute::SourceFile(t) => { 351 | let mut buf: Vec = Vec::new(); 352 | wtr.write_u16::(constant_pool.utf8("SourceFile"))?; 353 | t.write(&mut buf, constant_pool)?; 354 | wtr.write_u32::(buf.len() as u32)?; 355 | wtr.write_all(buf.as_slice())?; 356 | }, 357 | Attribute::LocalVariableTable(t) => { 358 | let label_pc_map = label_pc_map.unwrap(); 359 | let mut buf: Vec = Vec::new(); 360 | wtr.write_u16::(constant_pool.utf8("LocalVariableTable"))?; 361 | t.write(&mut buf, constant_pool, label_pc_map)?; 362 | wtr.write_u32::(buf.len() as u32)?; 363 | wtr.write_all(buf.as_slice())?; 364 | }, 365 | Attribute::Unknown(t) => { 366 | wtr.write_u16::(constant_pool.utf8(t.name.clone()))?; 367 | wtr.write_u32::(t.len() as u32)?; 368 | t.write(wtr, constant_pool)?; 369 | } 370 | }; 371 | Ok(()) 372 | } 373 | } 374 | 375 | #[allow(dead_code)] 376 | #[derive(Clone, Copy)] 377 | pub enum AttributeSource { 378 | Class, 379 | Field, 380 | Method, 381 | Code 382 | } 383 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use std::fs::File; 3 | use std::io::{BufReader, BufWriter}; 4 | use std::env; 5 | 6 | use classfile::classfile::ClassFile; 7 | 8 | fn main() { 9 | let args: Vec = env::args().collect(); 10 | 11 | if let Some(file) = args.get(1) { 12 | if file == "-h" { 13 | print_usage(); 14 | return; 15 | } 16 | 17 | // Read 18 | let start = Instant::now(); 19 | let class = { 20 | let f = File::open(file).unwrap(); 21 | let mut reader = BufReader::new(f); 22 | ClassFile::parse(&mut reader) 23 | }; 24 | 25 | let elapsed = start.elapsed(); 26 | println!("{:#x?}", class); 27 | println!("Finished parsing {} in {:#?}", file, elapsed); 28 | 29 | // If the user has provided an output file we will write there 30 | if let Ok(class) = class { 31 | if let Some(file) = args.get(2) { 32 | let f = File::create(file).unwrap(); 33 | let mut writer = BufWriter::new(f); 34 | class.write(&mut writer).unwrap(); 35 | } 36 | } 37 | } else { 38 | print_usage(); 39 | } 40 | } 41 | 42 | fn print_usage() { 43 | eprintln!("Usage: ./dissasembler classFileIn.class (classFileOut.class)"); 44 | } 45 | -------------------------------------------------------------------------------- /src/classfile.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Write, Read, Cursor}; 2 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 3 | use crate::Serializable; 4 | use crate::version::ClassVersion; 5 | use crate::constantpool::{ConstantPool, ConstantPoolWriter}; 6 | use crate::access::ClassAccessFlags; 7 | use crate::field::{Field, Fields}; 8 | use crate::method::{Methods, Method}; 9 | use crate::error::{Result, ParserError}; 10 | use crate::attributes::{Attribute, Attributes, AttributeSource}; 11 | 12 | #[derive(Clone, Debug, PartialEq)] 13 | pub struct ClassFile { 14 | /// 0xCAFEBABE 15 | pub magic: u32, 16 | pub version: ClassVersion, 17 | pub access_flags: ClassAccessFlags, 18 | pub this_class: String, 19 | /// Can be None for example for java/lang/Object 20 | pub super_class: Option, 21 | pub interfaces: Vec, 22 | pub fields: Vec, 23 | pub methods: Vec, 24 | pub attributes: Vec 25 | } 26 | 27 | impl ClassFile { 28 | pub fn parse(rdr: &mut R) -> Result { 29 | let magic = rdr.read_u32::()?; 30 | if magic != 0xCAFEBABE { 31 | return Err(ParserError::unrecognised("header", magic.to_string())); 32 | } 33 | let version = ClassVersion::parse(rdr)?; 34 | let constant_pool = ConstantPool::parse(rdr)?; 35 | let access_flags = ClassAccessFlags::parse(rdr)?; 36 | let this_class = constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone(); 37 | let super_class = match rdr.read_u16::()? { 38 | 0 => None, 39 | i => Some(constant_pool.utf8(constant_pool.class(i)?.name_index)?.str.clone()) 40 | }; 41 | 42 | let num_interfaces = rdr.read_u16::()? as usize; 43 | let mut interfaces: Vec = Vec::with_capacity(num_interfaces); 44 | for _ in 0..num_interfaces { 45 | interfaces.push(constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone()); 46 | } 47 | 48 | let fields = Fields::parse(rdr, &version, &constant_pool)?; 49 | let methods = Methods::parse(rdr, &version, &constant_pool)?; 50 | let attributes = Attributes::parse(rdr, AttributeSource::Class, &version, &constant_pool, &mut None)?; 51 | 52 | Ok(ClassFile { 53 | magic, 54 | version, 55 | access_flags, 56 | this_class, 57 | super_class, 58 | interfaces, 59 | fields, 60 | methods, 61 | attributes 62 | }) 63 | } 64 | 65 | pub fn write(&self, wtr: &mut W) -> Result<()> { 66 | wtr.write_u32::(self.magic)?; 67 | self.version.write(wtr)?; 68 | 69 | let mut constant_pool = ConstantPoolWriter::new(); 70 | 71 | // we need to write fields/methods etc after the constant pool, however they rely upon 72 | // mutable access to the constant pool. therefore we will write them to memory and then to 73 | // the wtr parameter 74 | let buf: Vec = Vec::with_capacity(2 + (self.fields.len() * 8) + (self.methods.len() * 8)); 75 | let mut cursor = Cursor::new(buf); 76 | self.access_flags.write(&mut cursor)?; 77 | 78 | // this class 79 | let utf = constant_pool.utf8(self.this_class.clone()); 80 | cursor.write_u16::(constant_pool.class(utf))?; 81 | // super class 82 | if let Some(x) = &self.super_class { 83 | let utf = constant_pool.utf8(x.clone()); 84 | cursor.write_u16::(constant_pool.class(utf))?; 85 | } else { 86 | cursor.write_u16::(0)?; 87 | } 88 | // interfaces 89 | cursor.write_u16::(self.interfaces.len() as u16)?; 90 | for interface in self.interfaces.iter() { 91 | let utf = constant_pool.utf8(interface.clone()); 92 | cursor.write_u16::(constant_pool.class(utf))?; 93 | } 94 | 95 | Fields::write(&mut cursor, &self.fields, &mut constant_pool)?; 96 | Methods::write(&mut cursor, &self.methods, &mut constant_pool)?; 97 | Attributes::write(&mut cursor, &self.attributes, &mut constant_pool, None)?; 98 | 99 | constant_pool.write(wtr)?; 100 | wtr.write_all(cursor.get_ref().as_slice())?; 101 | 102 | Ok(()) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/code.rs: -------------------------------------------------------------------------------- 1 | use crate::attributes::{Attribute, AttributeSource, Attributes}; 2 | use crate::constantpool::{ConstantPool, ConstantType, CPIndex, ConstantPoolWriter}; 3 | use crate::version::ClassVersion; 4 | use crate::error::{Result, ParserError}; 5 | use crate::ast::*; 6 | use crate::insnlist::InsnList; 7 | use crate::utils::{ReadUtils, MapUtils}; 8 | use crate::types::{Type, parse_method_desc}; 9 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 10 | use std::io::{Read, Write, Cursor, Seek, SeekFrom}; 11 | use std::collections::HashMap; 12 | use derive_more::Constructor; 13 | use std::convert::TryFrom; 14 | 15 | #[derive(Constructor, Clone, Debug, PartialEq)] 16 | pub struct CodeAttribute { 17 | pub max_stack: u16, 18 | pub max_locals: u16, 19 | pub insns: InsnList, 20 | pub exceptions: Vec, 21 | pub attributes: Vec 22 | } 23 | 24 | impl CodeAttribute { 25 | pub fn empty() -> Self { 26 | CodeAttribute { 27 | max_stack: 0, 28 | max_locals: 0, 29 | insns: InsnList::with_capacity(0), 30 | exceptions: Vec::with_capacity(0), 31 | attributes: Vec::with_capacity(0) 32 | } 33 | } 34 | 35 | pub fn parse(version: &ClassVersion, constant_pool: &ConstantPool, buf: Vec) -> Result { 36 | let mut buf = Cursor::new(buf); 37 | 38 | let max_stack = buf.read_u16::()?; 39 | let max_locals = buf.read_u16::()?; 40 | 41 | let code_length = buf.read_u32::()?; 42 | 43 | let code: Vec = buf.read_nbytes(code_length as usize)?; 44 | let mut code = Cursor::new(code); 45 | 46 | let mut pc_label_map: HashMap = HashMap::new(); 47 | InsnParser::find_insn_refs(&mut code, code_length, &mut pc_label_map)?; 48 | 49 | let num_exceptions = buf.read_u16::()?; 50 | let mut exceptions: Vec = Vec::with_capacity(num_exceptions as usize); 51 | for _ in 0..num_exceptions { 52 | exceptions.push(ExceptionHandler::parse(constant_pool, &mut buf)?); 53 | } 54 | 55 | let mut pc_label_map = Some(pc_label_map); 56 | let attributes = Attributes::parse(&mut buf, AttributeSource::Code, version, constant_pool, &mut pc_label_map)?; 57 | let mut pc_label_map = pc_label_map.unwrap(); 58 | 59 | code.set_position(0); 60 | let code = InsnParser::parse_insns(constant_pool, &mut code, code_length, &mut pc_label_map)?; 61 | 62 | Ok(CodeAttribute { 63 | max_stack, 64 | max_locals, 65 | insns: code, 66 | exceptions, 67 | attributes 68 | }) 69 | } 70 | 71 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 72 | wtr.write_u16::(self.max_stack)?; 73 | wtr.write_u16::(self.max_locals)?; 74 | let (code_bytes, label_pc_map) = InsnParser::write_insns(self, constant_pool)?; 75 | wtr.write_u32::(code_bytes.len() as u32)?; 76 | wtr.write_all(code_bytes.as_slice())?; 77 | wtr.write_u16::(self.exceptions.len() as u16)?; 78 | for excep in self.exceptions.iter() { 79 | excep.write(wtr, constant_pool)?; 80 | } 81 | Attributes::write(wtr, &self.attributes, constant_pool, Some(&label_pc_map))?; 82 | Ok(()) 83 | } 84 | } 85 | 86 | 87 | #[derive(Clone, Debug, PartialEq)] 88 | pub struct ExceptionHandler { 89 | pub start_pc: u16, 90 | pub end_pc: u16, 91 | pub handler_pc: u16, 92 | pub catch_type: Option 93 | } 94 | 95 | impl ExceptionHandler { 96 | // TODO: exception handlers should use labels 97 | pub fn parse(constant_pool: &ConstantPool, buf: &mut T) -> Result { 98 | let start_pc = buf.read_u16::()?; 99 | let end_pc = buf.read_u16::()?; 100 | let handler_pc = buf.read_u16::()?; 101 | let catch_index = buf.read_u16::()?; 102 | let catch_type = if catch_index > 0 { 103 | Some(constant_pool.utf8(constant_pool.class(catch_index)?.name_index)?.str.clone()) 104 | } else { 105 | None 106 | }; 107 | 108 | Ok(ExceptionHandler { 109 | start_pc, 110 | end_pc, 111 | handler_pc, 112 | catch_type 113 | }) 114 | } 115 | 116 | pub fn write(&self, wtr: &mut T, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 117 | wtr.write_u16::(self.start_pc)?; 118 | wtr.write_u16::(self.end_pc)?; 119 | wtr.write_u16::(self.handler_pc)?; 120 | let catch_type = match self.catch_type.clone() { 121 | Some(x) => constant_pool.class_utf8(x), 122 | None => 0 123 | }; 124 | wtr.write_u16::(catch_type)?; 125 | Ok(()) 126 | } 127 | } 128 | 129 | struct InsnParser {} 130 | #[allow(unused_variables)] 131 | #[allow(dead_code)] 132 | impl InsnParser { 133 | const AALOAD: u8 = 0x32; 134 | const AASTORE: u8 = 0x53; 135 | const ACONST_NULL: u8 = 0x01; 136 | const ALOAD: u8 = 0x19; 137 | const ALOAD_0: u8 = 0x2A; 138 | const ALOAD_1: u8 = 0x2B; 139 | const ALOAD_2: u8 = 0x2C; 140 | const ALOAD_3: u8 = 0x2D; 141 | const ANEWARRAY: u8 = 0xBD; 142 | const ARETURN: u8 = 0xB0; 143 | const ARRAYLENGTH: u8 = 0xBE; 144 | const ASTORE: u8 = 0x3A; 145 | const ASTORE_0: u8 = 0x4B; 146 | const ASTORE_1: u8 = 0x4C; 147 | const ASTORE_2: u8 = 0x4D; 148 | const ASTORE_3: u8 = 0x4E; 149 | const ATHROW: u8 = 0xBF; 150 | const BALOAD: u8 = 0x33; 151 | const BASTORE: u8 = 0x54; 152 | const BIPUSH: u8 = 0x10; 153 | const BREAKPOINT: u8 = 0xCA; 154 | const CALOAD: u8 = 0x34; 155 | const CASTORE: u8 = 0x55; 156 | const CHECKCAST: u8 = 0xC0; 157 | const D2F: u8 = 0x90; 158 | const D2I: u8 = 0x8E; 159 | const D2L: u8 = 0x8F; 160 | const DADD: u8 = 0x63; 161 | const DALOAD: u8 = 0x31; 162 | const DASTORE: u8 = 0x52; 163 | const DCMPG: u8 = 0x98; 164 | const DCMPL: u8 = 0x97; 165 | const DCONST_0: u8 = 0x0E; 166 | const DCONST_1: u8 = 0x0F; 167 | const DDIV: u8 = 0x6F; 168 | const DLOAD: u8 = 0x18; 169 | const DLOAD_0: u8 = 0x26; 170 | const DLOAD_1: u8 = 0x27; 171 | const DLOAD_2: u8 = 0x28; 172 | const DLOAD_3: u8 = 0x29; 173 | const DMUL: u8 = 0x6B; 174 | const DNEG: u8 = 0x77; 175 | const DREM: u8 = 0x73; 176 | const DRETURN: u8 = 0xAF; 177 | const DSTORE: u8 = 0x39; 178 | const DSTORE_0: u8 = 0x47; 179 | const DSTORE_1: u8 = 0x48; 180 | const DSTORE_2: u8 = 0x49; 181 | const DSTORE_3: u8 = 0x4A; 182 | const DSUB: u8 = 0x67; 183 | const DUP: u8 = 0x59; 184 | const DUP_X1: u8 = 0x5A; 185 | const DUP_X2: u8 = 0x5B; 186 | const DUP2: u8 = 0x5C; 187 | const DUP2_X1: u8 = 0x5D; 188 | const DUP2_X2: u8 = 0x5E; 189 | const F2D: u8 = 0x8D; 190 | const F2I: u8 = 0x8B; 191 | const F2L: u8 = 0x8C; 192 | const FADD: u8 = 0x62; 193 | const FALOAD: u8 = 0x30; 194 | const FASTORE: u8 = 0x51; 195 | const FCMPG: u8 = 0x96; 196 | const FCMPL: u8 = 0x95; 197 | const FCONST_0: u8 = 0x0B; 198 | const FCONST_1: u8 = 0x0C; 199 | const FCONST_2: u8 = 0x0D; 200 | const FDIV: u8 = 0x6E; 201 | const FLOAD: u8 = 0x17; 202 | const FLOAD_0: u8 = 0x22; 203 | const FLOAD_1: u8 = 0x23; 204 | const FLOAD_2: u8 = 0x24; 205 | const FLOAD_3: u8 = 0x25; 206 | const FMUL: u8 = 0x6A; 207 | const FNEG: u8 = 0x76; 208 | const FREM: u8 = 0x72; 209 | const FRETURN: u8 = 0xAE; 210 | const FSTORE: u8 = 0x38; 211 | const FSTORE_0: u8 = 0x43; 212 | const FSTORE_1: u8 = 0x44; 213 | const FSTORE_2: u8 = 0x45; 214 | const FSTORE_3: u8 = 0x46; 215 | const FSUB: u8 = 0x66; 216 | const GETFIELD: u8 = 0xB4; 217 | const GETSTATIC: u8 = 0xB2; 218 | const GOTO: u8 = 0xA7; 219 | const GOTO_W: u8 = 0xC8; 220 | const I2B: u8 = 0x91; 221 | const I2C: u8 = 0x92; 222 | const I2D: u8 = 0x87; 223 | const I2F: u8 = 0x86; 224 | const I2L: u8 = 0x85; 225 | const I2S: u8 = 0x93; 226 | const IADD: u8 = 0x60; 227 | const IALOAD: u8 = 0x2E; 228 | const IAND: u8 = 0x7E; 229 | const IASTORE: u8 = 0x4F; 230 | const ICONST_M1: u8 = 0x02; 231 | const ICONST_0: u8 = 0x03; 232 | const ICONST_1: u8 = 0x04; 233 | const ICONST_2: u8 = 0x05; 234 | const ICONST_3: u8 = 0x06; 235 | const ICONST_4: u8 = 0x07; 236 | const ICONST_5: u8 = 0x08; 237 | const IDIV: u8 = 0x6C; 238 | const IF_ACMPEQ: u8 = 0xA5; 239 | const IF_ACMPNE: u8 = 0xA6; 240 | const IF_ICMPEQ: u8 = 0x9F; 241 | const IF_ICMPGE: u8 = 0xA2; 242 | const IF_ICMPGT: u8 = 0xA3; 243 | const IF_ICMPLE: u8 = 0xA4; 244 | const IF_ICMPLT: u8 = 0xA1; 245 | const IF_ICMPNE: u8 = 0xA0; 246 | const IFEQ: u8 = 0x99; 247 | const IFGE: u8 = 0x9C; 248 | const IFGT: u8 = 0x9D; 249 | const IFLE: u8 = 0x9E; 250 | const IFLT: u8 = 0x9B; 251 | const IFNE: u8 = 0x9A; 252 | const IFNONNULL: u8 = 0xC7; 253 | const IFNULL: u8 = 0xC6; 254 | const IINC: u8 = 0x84; 255 | const ILOAD: u8 = 0x15; 256 | const ILOAD_0: u8 = 0x1A; 257 | const ILOAD_1: u8 = 0x1B; 258 | const ILOAD_2: u8 = 0x1C; 259 | const ILOAD_3: u8 = 0x1D; 260 | const IMPDEP1: u8 = 0xFE; 261 | const IMPDEP2: u8 = 0xFF; 262 | const IMUL: u8 = 0x68; 263 | const INEG: u8 = 0x74; 264 | const INSTANCEOF: u8 = 0xC1; 265 | const INVOKEDYNAMIC: u8 = 0xBA; 266 | const INVOKEINTERFACE: u8 = 0xB9; 267 | const INVOKESPECIAL: u8 = 0xB7; 268 | const INVOKESTATIC: u8 = 0xB8; 269 | const INVOKEVIRTUAL: u8 = 0xB6; 270 | const IOR: u8 = 0x80; 271 | const IREM: u8 = 0x70; 272 | const IRETURN: u8 = 0xAC; 273 | const ISHL: u8 = 0x78; 274 | const ISHR: u8 = 0x7A; 275 | const ISTORE: u8 = 0x36; 276 | const ISTORE_0: u8 = 0x3B; 277 | const ISTORE_1: u8 = 0x3C; 278 | const ISTORE_2: u8 = 0x3D; 279 | const ISTORE_3: u8 = 0x3E; 280 | const ISUB: u8 = 0x64; 281 | const IUSHR: u8 = 0x7C; 282 | const IXOR: u8 = 0x82; 283 | const JSR: u8 = 0xA8; 284 | const JSR_W: u8 = 0xC9; 285 | const L2D: u8 = 0x8A; 286 | const L2F: u8 = 0x89; 287 | const L2I: u8 = 0x88; 288 | const LADD: u8 = 0x61; 289 | const LALOAD: u8 = 0x2F; 290 | const LAND: u8 = 0x7F; 291 | const LASTORE: u8 = 0x50; 292 | const LCMP: u8 = 0x94; 293 | const LCONST_0: u8 = 0x09; 294 | const LCONST_1: u8 = 0x0A; 295 | const LDC: u8 = 0x12; 296 | const LDC_W: u8 = 0x13; 297 | const LDC2_W: u8 = 0x14; 298 | const LDIV: u8 = 0x6D; 299 | const LLOAD: u8 = 0x16; 300 | const LLOAD_0: u8 = 0x1E; 301 | const LLOAD_1: u8 = 0x1F; 302 | const LLOAD_2: u8 = 0x20; 303 | const LLOAD_3: u8 = 0x21; 304 | const LMUL: u8 = 0x69; 305 | const LNEG: u8 = 0x75; 306 | const LOOKUPSWITCH: u8 = 0xAB; 307 | const LOR: u8 = 0x81; 308 | const LREM: u8 = 0x71; 309 | const LRETURN: u8 = 0xAD; 310 | const LSHL: u8 = 0x79; 311 | const LSHR: u8 = 0x7B; 312 | const LSTORE: u8 = 0x37; 313 | const LSTORE_0: u8 = 0x3F; 314 | const LSTORE_1: u8 = 0x40; 315 | const LSTORE_2: u8 = 0x41; 316 | const LSTORE_3: u8 = 0x42; 317 | const LSUB: u8 = 0x65; 318 | const LUSHR: u8 = 0x7D; 319 | const LXOR: u8 = 0x83; 320 | const MONITORENTER: u8 = 0xC2; 321 | const MONITOREXIT: u8 = 0xC3; 322 | const MULTIANEWARRAY: u8 = 0xC5; 323 | const NEW: u8 = 0xBB; 324 | const NEWARRAY: u8 = 0xBC; 325 | const NOP: u8 = 0x00; 326 | const POP: u8 = 0x57; 327 | const POP2: u8 = 0x58; 328 | const PUTFIELD: u8 = 0xB5; 329 | const PUTSTATIC: u8 = 0xB3; 330 | const RET: u8 = 0xA9; 331 | const RETURN: u8 = 0xB1; 332 | const SALOAD: u8 = 0x35; 333 | const SASTORE: u8 = 0x56; 334 | const SIPUSH: u8 = 0x11; 335 | const SWAP: u8 = 0x5F; 336 | const TABLESWITCH: u8 = 0xAA; 337 | const WIDE: u8 = 0xC4; 338 | 339 | /// Iterate all instructions and collect any pcs that are referenced - i.e. need to have relevant Labels 340 | fn find_insn_refs(rdr: &mut T, length: u32, pc_label_map: &mut HashMap) -> Result<()> { 341 | let mut pc: u32 = 0; 342 | while pc < length { 343 | let this_pc = pc; 344 | let opcode = rdr.read_u8()?; 345 | pc += 1; 346 | 347 | match opcode { 348 | InsnParser::GOTO => { 349 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 350 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 351 | pc += 2; 352 | } 353 | InsnParser::GOTO_W => { 354 | let to = (rdr.read_i32::()? + this_pc as i32) as u32; 355 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 356 | pc += 4; 357 | } 358 | InsnParser::IF_ACMPEQ => { 359 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 360 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 361 | pc += 2; 362 | } 363 | InsnParser::IF_ACMPNE => { 364 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 365 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 366 | pc += 2; 367 | } 368 | InsnParser::IF_ICMPEQ => { 369 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 370 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 371 | pc += 2; 372 | } 373 | InsnParser::IF_ICMPGE => { 374 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 375 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 376 | pc += 2; 377 | } 378 | InsnParser::IF_ICMPGT => { 379 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 380 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 381 | pc += 2; 382 | } 383 | InsnParser::IF_ICMPLE => { 384 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 385 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 386 | pc += 2; 387 | } 388 | InsnParser::IF_ICMPLT => { 389 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 390 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 391 | pc += 2; 392 | } 393 | InsnParser::IF_ICMPNE => { 394 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 395 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 396 | pc += 2; 397 | } 398 | InsnParser::IFEQ => { 399 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 400 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 401 | pc += 2; 402 | } 403 | InsnParser::IFGE => { 404 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 405 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 406 | pc += 2; 407 | } 408 | InsnParser::IFGT => { 409 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 410 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 411 | pc += 2; 412 | } 413 | InsnParser::IFLE => { 414 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 415 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 416 | pc += 2; 417 | } 418 | InsnParser::IFLT => { 419 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 420 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 421 | pc += 2; 422 | } 423 | InsnParser::IFNE => { 424 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 425 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 426 | pc += 2; 427 | } 428 | InsnParser::IFNONNULL => { 429 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 430 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 431 | pc += 2; 432 | } 433 | InsnParser::IFNULL => { 434 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 435 | pc_label_map.insert_if_not_present(to, LabelInsn::new(pc_label_map.len() as u32)); 436 | pc += 2; 437 | } 438 | InsnParser::LOOKUPSWITCH => { 439 | let pad = 3 - (this_pc % 4); 440 | rdr.seek(SeekFrom::Current(pad as i64))?; 441 | 442 | let default = (rdr.read_i32::()? + this_pc as i32) as u32; 443 | pc_label_map.insert_if_not_present(default, LabelInsn::new(pc_label_map.len() as u32)); 444 | let npairs = rdr.read_i32::()? as u32; 445 | 446 | for i in 0..npairs { 447 | let matc = rdr.read_i32::()?; 448 | let jump = (rdr.read_i32::()? + this_pc as i32) as u32; 449 | pc_label_map.insert_if_not_present(jump, LabelInsn::new(pc_label_map.len() as u32)); 450 | } 451 | 452 | pc += pad + (2 * 4) + (npairs * 2 * 4); 453 | } 454 | InsnParser::TABLESWITCH => { 455 | let pad = 3 - (this_pc % 4); 456 | rdr.seek(SeekFrom::Current(pad as i64))?; 457 | 458 | let default = (rdr.read_i32::()? + this_pc as i32) as u32; 459 | pc_label_map.insert_if_not_present(default, LabelInsn::new(pc_label_map.len() as u32)); 460 | 461 | let low = rdr.read_i32::()?; 462 | let high = rdr.read_i32::()?; 463 | let num_cases = (high - low + 1) as u32; 464 | for i in 0..num_cases { 465 | let case = (rdr.read_i32::()? + this_pc as i32) as u32; 466 | pc_label_map.insert_if_not_present(case, LabelInsn::new(pc_label_map.len() as u32)); 467 | } 468 | 469 | pc += pad + ((3 + num_cases) * 4); 470 | }, 471 | InsnParser::AALOAD | InsnParser::AASTORE | InsnParser::ACONST_NULL | 472 | InsnParser::ALOAD_0 | InsnParser::ALOAD_1 | InsnParser::ALOAD_2 | 473 | InsnParser::ALOAD_3 | InsnParser::ARETURN | InsnParser::ARRAYLENGTH | 474 | InsnParser::ASTORE_0 | InsnParser::ASTORE_2 | InsnParser::ASTORE_3 | 475 | InsnParser::ATHROW | InsnParser::BALOAD | InsnParser::BASTORE | 476 | InsnParser::BREAKPOINT | InsnParser::CALOAD | InsnParser::CASTORE | 477 | InsnParser::D2F | InsnParser::D2I | InsnParser::D2L | InsnParser::DADD | 478 | InsnParser::DALOAD | InsnParser::DASTORE | InsnParser::DCMPG | InsnParser::DCMPL | 479 | InsnParser::DCONST_0 | InsnParser::DCONST_1 | InsnParser::DDIV | 480 | InsnParser::DLOAD_0 | InsnParser::DLOAD_1 | InsnParser::DLOAD_2 | 481 | InsnParser::DLOAD_3 | InsnParser::DMUL | InsnParser::DNEG | InsnParser::DREM | 482 | InsnParser::DRETURN | InsnParser::DSTORE_0 | InsnParser::DSTORE_1 | 483 | InsnParser::DSTORE_2 | InsnParser::DSTORE_3 | InsnParser::DSUB | InsnParser::DUP | 484 | InsnParser::DUP_X1 | InsnParser::DUP_X2 | InsnParser::DUP2 | InsnParser::DUP2_X1 | 485 | InsnParser::DUP2_X2 | InsnParser::F2D | InsnParser::F2I | InsnParser::F2L | 486 | InsnParser::FADD | InsnParser::FALOAD | InsnParser::FASTORE | InsnParser::FCMPG | 487 | InsnParser::FCMPL | InsnParser::FCONST_0 | InsnParser::FCONST_1 | 488 | InsnParser::FCONST_2 | InsnParser::FDIV | InsnParser::FLOAD_0 | 489 | InsnParser::FLOAD_1 | InsnParser::FLOAD_2 | InsnParser::FLOAD_3 | InsnParser::FMUL | 490 | InsnParser::FNEG | InsnParser::FREM | InsnParser::FRETURN | InsnParser::FSTORE_0 | 491 | InsnParser::FSTORE_1 | InsnParser::FSTORE_2 | InsnParser::FSTORE_3 | 492 | InsnParser::FSUB | InsnParser::I2B | InsnParser::I2C | InsnParser::I2D | 493 | InsnParser::I2F | InsnParser::I2L | InsnParser::I2S | InsnParser::IADD | 494 | InsnParser::IALOAD | InsnParser::IAND | InsnParser::IASTORE | 495 | InsnParser::ICONST_M1 | InsnParser::ICONST_0 | InsnParser::ICONST_1 | 496 | InsnParser::ICONST_2 | InsnParser::ICONST_3 | InsnParser::ICONST_4 | 497 | InsnParser::ICONST_5 | InsnParser::IDIV | InsnParser::ILOAD_0 | 498 | InsnParser::ILOAD_1 | InsnParser::ILOAD_2 | InsnParser::ILOAD_3 | 499 | InsnParser::IMPDEP1 | InsnParser::IMPDEP2 | InsnParser::IMUL | InsnParser::INEG | 500 | InsnParser::IOR | InsnParser::IREM | InsnParser::IRETURN | InsnParser::ISHL | 501 | InsnParser::ISHR | InsnParser::ISTORE_0 | InsnParser::ISTORE_1 | 502 | InsnParser::ISTORE_2 | InsnParser::ISTORE_3 | InsnParser::ISUB | InsnParser::IUSHR | 503 | InsnParser::IXOR | InsnParser::L2D | InsnParser::L2F | InsnParser::L2I | 504 | InsnParser::LADD | InsnParser::LALOAD | InsnParser::LAND | InsnParser::LASTORE | 505 | InsnParser::LCMP | InsnParser::LCONST_0 | InsnParser::LCONST_1 | InsnParser::LDIV | 506 | InsnParser::LLOAD_0 | InsnParser::LLOAD_1 | InsnParser::LLOAD_2 | 507 | InsnParser::LLOAD_3 | InsnParser::LMUL | InsnParser::LNEG | InsnParser::LOR | 508 | InsnParser::LREM | InsnParser::LRETURN | InsnParser::LSHL | InsnParser::LSHR | 509 | InsnParser::LSTORE_0 | InsnParser::LSTORE_1 | InsnParser::LSTORE_2 | 510 | InsnParser::LSTORE_3 | InsnParser::LSUB | InsnParser::LUSHR | InsnParser::LXOR | 511 | InsnParser::MONITORENTER | InsnParser::MONITOREXIT | InsnParser::NOP | 512 | InsnParser::POP | InsnParser::POP2 | InsnParser::RETURN | InsnParser::SALOAD | 513 | InsnParser::SASTORE | InsnParser::SWAP => {}, 514 | InsnParser::ALOAD | InsnParser::ASTORE | InsnParser::BIPUSH | InsnParser::DLOAD | 515 | InsnParser::DSTORE | InsnParser::FLOAD | InsnParser::FSTORE | InsnParser::ILOAD | 516 | InsnParser::ISTORE | InsnParser::LDC | InsnParser::LLOAD | InsnParser::LSTORE | 517 | InsnParser::NEWARRAY => { 518 | pc += 1; 519 | rdr.seek(SeekFrom::Current(1))?; 520 | } 521 | InsnParser::ANEWARRAY | InsnParser::CHECKCAST | InsnParser::GETFIELD | 522 | InsnParser::GETSTATIC | InsnParser::IINC | InsnParser::INSTANCEOF | 523 | InsnParser::INVOKESPECIAL | InsnParser::INVOKESTATIC | InsnParser::INVOKEVIRTUAL | 524 | InsnParser::LDC_W | InsnParser::LDC2_W | InsnParser::NEW | InsnParser::PUTFIELD | 525 | InsnParser::PUTSTATIC | InsnParser::SIPUSH => { 526 | pc += 2; 527 | rdr.seek(SeekFrom::Current(2))?; 528 | } 529 | InsnParser::MULTIANEWARRAY => { 530 | pc += 3; 531 | rdr.seek(SeekFrom::Current(3))?; 532 | } 533 | InsnParser::INVOKEDYNAMIC | InsnParser::INVOKEINTERFACE => { 534 | pc += 4; 535 | rdr.seek(SeekFrom::Current(4))?; 536 | } 537 | InsnParser::WIDE => match rdr.read_u8()? { 538 | InsnParser::ILOAD | InsnParser::FLOAD | InsnParser::ALOAD | InsnParser::LLOAD | 539 | InsnParser::DLOAD | InsnParser::ISTORE | InsnParser::FSTORE | 540 | InsnParser::LSTORE | InsnParser::DSTORE => { 541 | pc += 3; 542 | rdr.seek(SeekFrom::Current(3))?; 543 | } 544 | InsnParser::IINC => { 545 | pc += 5; 546 | rdr.seek(SeekFrom::Current(5))?; 547 | } 548 | _ => return Err(ParserError::invalid_insn(this_pc, format!("Invalid wide opcode {:x}", opcode))) 549 | }, 550 | _ => return Err(ParserError::unknown_insn(opcode)) 551 | } 552 | } 553 | Ok(()) 554 | } 555 | 556 | fn parse_insns(constant_pool: &ConstantPool, mut rdr: T, length: u32, pc_label_map: &mut HashMap) -> Result { 557 | let num_insns_estimate = length as usize / 3; // estimate an average 3 bytes per insn 558 | let mut insns: Vec = Vec::with_capacity(num_insns_estimate); 559 | 560 | let mut pc: u32 = 0; 561 | while pc < length { 562 | let this_pc = pc; 563 | let opcode = rdr.read_u8()?; 564 | pc += 1; 565 | 566 | // does this pc need an associated label? 567 | if let Some(lbl) = pc_label_map.get(&this_pc) { 568 | insns.push(Insn::Label(*lbl)); 569 | } 570 | 571 | let insn = match opcode { 572 | InsnParser::AALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Reference(None))), 573 | InsnParser::AASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Reference(None))), 574 | InsnParser::ACONST_NULL => Insn::Ldc(LdcInsn::new(LdcType::Null)), 575 | InsnParser::ALOAD => { 576 | let index = rdr.read_u8()?; 577 | pc += 1; 578 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Reference, index as u16)) 579 | }, 580 | InsnParser::ALOAD_0 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Reference, 0)), 581 | InsnParser::ALOAD_1 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Reference, 1)), 582 | InsnParser::ALOAD_2 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Reference, 2)), 583 | InsnParser::ALOAD_3 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Reference, 3)), 584 | InsnParser::ANEWARRAY => { 585 | let kind = constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone(); 586 | pc += 2; 587 | Insn::NewArray(NewArrayInsn::new(Type::Reference(Some(kind)))) 588 | }, 589 | InsnParser::ARETURN => Insn::Return(ReturnInsn::new(ReturnType::Reference)), 590 | InsnParser::ARRAYLENGTH => Insn::ArrayLength(ArrayLengthInsn::new()), 591 | InsnParser::ASTORE => { 592 | let index = rdr.read_u8()?; 593 | pc += 1; 594 | Insn::LocalStore(LocalStoreInsn::new(OpType::Reference, index as u16)) 595 | }, 596 | InsnParser::ASTORE_0 => Insn::LocalStore(LocalStoreInsn::new(OpType::Reference, 0)), 597 | InsnParser::ASTORE_1 => Insn::LocalStore(LocalStoreInsn::new(OpType::Reference, 1)), 598 | InsnParser::ASTORE_2 => Insn::LocalStore(LocalStoreInsn::new(OpType::Reference, 2)), 599 | InsnParser::ASTORE_3 => Insn::LocalStore(LocalStoreInsn::new(OpType::Reference, 3)), 600 | InsnParser::ATHROW => Insn::Throw(ThrowInsn::new()), 601 | // BALOAD is both byte and boolean (they are same size on hotspot) we will assume byte 602 | InsnParser::BALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Byte)), 603 | InsnParser::BASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Byte)), 604 | InsnParser::BIPUSH => { 605 | let byte = rdr.read_i8()?; 606 | pc += 1; 607 | Insn::Ldc(LdcInsn::new(LdcType::Int(byte as i32))) 608 | }, 609 | InsnParser::BREAKPOINT => Insn::BreakPoint(BreakPointInsn::new()), 610 | InsnParser::CALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Char)), 611 | InsnParser::CASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Char)), 612 | InsnParser::CHECKCAST => { 613 | let kind = constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone(); 614 | pc += 2; 615 | Insn::CheckCast(CheckCastInsn::new(kind)) 616 | }, 617 | InsnParser::D2F => Insn::Convert(ConvertInsn::new(PrimitiveType::Double, PrimitiveType::Float)), 618 | InsnParser::D2I => Insn::Convert(ConvertInsn::new(PrimitiveType::Double, PrimitiveType::Int)), 619 | InsnParser::D2L => Insn::Convert(ConvertInsn::new(PrimitiveType::Double, PrimitiveType::Long)), 620 | InsnParser::DADD => Insn::Add(AddInsn::new(PrimitiveType::Double)), 621 | InsnParser::DALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Double)), 622 | InsnParser::DASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Double)), 623 | InsnParser::DCMPG => Insn::Compare(CompareInsn::new(PrimitiveType::Double, true)), 624 | InsnParser::DCMPL => Insn::Compare(CompareInsn::new(PrimitiveType::Double, false)), 625 | InsnParser::DCONST_0 => Insn::Ldc(LdcInsn::new(LdcType::Double(0f64))), 626 | InsnParser::DCONST_1 => Insn::Ldc(LdcInsn::new(LdcType::Double(1f64))), 627 | InsnParser::DDIV => Insn::Divide(DivideInsn::new(PrimitiveType::Double)), 628 | InsnParser::DLOAD => { 629 | let index = rdr.read_u8()?; 630 | pc += 1; 631 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, index as u16)) 632 | }, 633 | InsnParser::DLOAD_0 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, 0)), 634 | InsnParser::DLOAD_1 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, 1)), 635 | InsnParser::DLOAD_2 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, 2)), 636 | InsnParser::DLOAD_3 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, 3)), 637 | InsnParser::DMUL => Insn::Multiply(MultiplyInsn::new(PrimitiveType::Double)), 638 | InsnParser::DNEG => Insn::Negate(NegateInsn::new(PrimitiveType::Double)), 639 | InsnParser::DREM => Insn::Remainder(RemainderInsn::new(PrimitiveType::Double)), 640 | InsnParser::DRETURN => Insn::Return(ReturnInsn::new(ReturnType::Double)), 641 | InsnParser::DSTORE => { 642 | let index = rdr.read_u8()?; 643 | pc += 1; 644 | Insn::LocalStore(LocalStoreInsn::new(OpType::Double, index as u16)) 645 | }, 646 | InsnParser::DSTORE_0 => Insn::LocalStore(LocalStoreInsn::new(OpType::Double, 0)), 647 | InsnParser::DSTORE_1 => Insn::LocalStore(LocalStoreInsn::new(OpType::Double, 1)), 648 | InsnParser::DSTORE_2 => Insn::LocalStore(LocalStoreInsn::new(OpType::Double, 2)), 649 | InsnParser::DSTORE_3 => Insn::LocalStore(LocalStoreInsn::new(OpType::Double, 3)), 650 | InsnParser::DSUB => Insn::Subtract(SubtractInsn::new(PrimitiveType::Double)), 651 | InsnParser::DUP => Insn::Dup(DupInsn::new(1, 0)), 652 | InsnParser::DUP_X1 => Insn::Dup(DupInsn::new(1, 1)), 653 | InsnParser::DUP_X2 => Insn::Dup(DupInsn::new(1, 2)), 654 | InsnParser::DUP2 => Insn::Dup(DupInsn::new(2, 0)), 655 | InsnParser::DUP2_X1 => Insn::Dup(DupInsn::new(2, 1)), 656 | InsnParser::DUP2_X2 => Insn::Dup(DupInsn::new(2, 2)), 657 | InsnParser::F2D => Insn::Convert(ConvertInsn::new(PrimitiveType::Float, PrimitiveType::Double)), 658 | InsnParser::F2I => Insn::Convert(ConvertInsn::new(PrimitiveType::Float, PrimitiveType::Int)), 659 | InsnParser::F2L => Insn::Convert(ConvertInsn::new(PrimitiveType::Float, PrimitiveType::Long)), 660 | InsnParser::FADD => Insn::Add(AddInsn::new(PrimitiveType::Float)), 661 | InsnParser::FALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Float)), 662 | InsnParser::FASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Float)), 663 | InsnParser::FCMPG => Insn::Compare(CompareInsn::new(PrimitiveType::Float, true)), 664 | InsnParser::FCMPL => Insn::Compare(CompareInsn::new(PrimitiveType::Float, false)), 665 | InsnParser::FCONST_0 => Insn::Ldc(LdcInsn::new(LdcType::Float(0f32))), 666 | InsnParser::FCONST_1 => Insn::Ldc(LdcInsn::new(LdcType::Float(1f32))), 667 | InsnParser::FCONST_2 => Insn::Ldc(LdcInsn::new(LdcType::Float(2f32))), 668 | InsnParser::FDIV => Insn::Divide(DivideInsn::new(PrimitiveType::Float)), 669 | InsnParser::FLOAD => { 670 | let index = rdr.read_u8()?; 671 | pc += 1; 672 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Float, index as u16)) 673 | }, 674 | InsnParser::FLOAD_0 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Float, 0)), 675 | InsnParser::FLOAD_1 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Float, 1)), 676 | InsnParser::FLOAD_2 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Float, 2)), 677 | InsnParser::FLOAD_3 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Float, 3)), 678 | InsnParser::FMUL => Insn::Multiply(MultiplyInsn::new(PrimitiveType::Float)), 679 | InsnParser::FNEG => Insn::Negate(NegateInsn::new(PrimitiveType::Float)), 680 | InsnParser::FREM => Insn::Remainder(RemainderInsn::new(PrimitiveType::Float)), 681 | InsnParser::FRETURN => Insn::Return(ReturnInsn::new(ReturnType::Float)), 682 | InsnParser::FSTORE => { 683 | let index = rdr.read_u8()?; 684 | pc += 1; 685 | Insn::LocalStore(LocalStoreInsn::new(OpType::Float, index as u16)) 686 | }, 687 | InsnParser::FSTORE_0 => Insn::LocalStore(LocalStoreInsn::new(OpType::Float, 0)), 688 | InsnParser::FSTORE_1 => Insn::LocalStore(LocalStoreInsn::new(OpType::Float, 1)), 689 | InsnParser::FSTORE_2 => Insn::LocalStore(LocalStoreInsn::new(OpType::Float, 2)), 690 | InsnParser::FSTORE_3 => Insn::LocalStore(LocalStoreInsn::new(OpType::Float, 3)), 691 | InsnParser::FSUB => Insn::Subtract(SubtractInsn::new(PrimitiveType::Float)), 692 | InsnParser::GETFIELD => { 693 | let field_ref = constant_pool.fieldref(rdr.read_u16::()?)?; 694 | pc += 2; 695 | let class = constant_pool.utf8(constant_pool.class(field_ref.class_index)?.name_index)?.str.clone(); 696 | let name_type = constant_pool.nameandtype(field_ref.name_and_type_index)?; 697 | let name = constant_pool.utf8(name_type.name_index)?.str.clone(); 698 | let descriptor = constant_pool.utf8(name_type.descriptor_index)?.str.clone(); 699 | Insn::GetField(GetFieldInsn::new(true, class, name, descriptor)) 700 | }, 701 | InsnParser::GETSTATIC => { 702 | let field_ref = constant_pool.fieldref(rdr.read_u16::()?)?; 703 | pc += 2; 704 | let class = constant_pool.utf8(constant_pool.class(field_ref.class_index)?.name_index)?.str.clone(); 705 | let name_type = constant_pool.nameandtype(field_ref.name_and_type_index)?; 706 | let name = constant_pool.utf8(name_type.name_index)?.str.clone(); 707 | let descriptor = constant_pool.utf8(name_type.descriptor_index)?.str.clone(); 708 | Insn::GetField(GetFieldInsn::new(false, class, name, descriptor)) 709 | }, 710 | InsnParser::GOTO => { 711 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 712 | pc += 2; 713 | Insn::Jump(JumpInsn::new(*pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 714 | }, 715 | InsnParser::GOTO_W => { 716 | let to = (rdr.read_i32::()? + this_pc as i32) as u32; 717 | pc += 4; 718 | Insn::Jump(JumpInsn::new(*pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 719 | }, 720 | InsnParser::I2B => Insn::Convert(ConvertInsn::new(PrimitiveType::Int, PrimitiveType::Byte)), 721 | InsnParser::I2C => Insn::Convert(ConvertInsn::new(PrimitiveType::Int, PrimitiveType::Char)), 722 | InsnParser::I2D => Insn::Convert(ConvertInsn::new(PrimitiveType::Int, PrimitiveType::Double)), 723 | InsnParser::I2F => Insn::Convert(ConvertInsn::new(PrimitiveType::Int, PrimitiveType::Float)), 724 | InsnParser::I2L => Insn::Convert(ConvertInsn::new(PrimitiveType::Int, PrimitiveType::Long)), 725 | InsnParser::I2S => Insn::Convert(ConvertInsn::new(PrimitiveType::Int, PrimitiveType::Short)), 726 | InsnParser::IADD => Insn::Add(AddInsn::new(PrimitiveType::Int)), 727 | InsnParser::IALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Int)), 728 | InsnParser::IAND => Insn::And(AndInsn::new(IntegerType::Int)), 729 | InsnParser::IASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Int)), 730 | InsnParser::ICONST_M1 => Insn::Ldc(LdcInsn::new(LdcType::Int(-1))), 731 | InsnParser::ICONST_0 => Insn::Ldc(LdcInsn::new(LdcType::Int(0))), 732 | InsnParser::ICONST_1 => Insn::Ldc(LdcInsn::new(LdcType::Int(1))), 733 | InsnParser::ICONST_2 => Insn::Ldc(LdcInsn::new(LdcType::Int(2))), 734 | InsnParser::ICONST_3 => Insn::Ldc(LdcInsn::new(LdcType::Int(3))), 735 | InsnParser::ICONST_4 => Insn::Ldc(LdcInsn::new(LdcType::Int(4))), 736 | InsnParser::ICONST_5 => Insn::Ldc(LdcInsn::new(LdcType::Int(5))), 737 | InsnParser::IDIV => Insn::Divide(DivideInsn::new(PrimitiveType::Int)), 738 | InsnParser::IF_ACMPEQ => { 739 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 740 | pc += 2; 741 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::ReferencesEqual, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 742 | }, 743 | InsnParser::IF_ACMPNE => { 744 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 745 | pc += 2; 746 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::ReferencesNotEqual, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 747 | }, 748 | InsnParser::IF_ICMPEQ => { 749 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 750 | pc += 2; 751 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntsEq, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 752 | }, 753 | InsnParser::IF_ICMPGE => { 754 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 755 | pc += 2; 756 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntsGreaterThanOrEq, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 757 | }, 758 | InsnParser::IF_ICMPGT => { 759 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 760 | pc += 2; 761 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntsGreaterThan, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 762 | }, 763 | InsnParser::IF_ICMPLE => { 764 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 765 | pc += 2; 766 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntsLessThanOrEq, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 767 | }, 768 | InsnParser::IF_ICMPLT => { 769 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 770 | pc += 2; 771 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntsLessThan, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 772 | }, 773 | InsnParser::IF_ICMPNE => { 774 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 775 | pc += 2; 776 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntsNotEq, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 777 | }, 778 | InsnParser::IFEQ => { 779 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 780 | pc += 2; 781 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntEqZero, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 782 | }, 783 | InsnParser::IFGE => { 784 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 785 | pc += 2; 786 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntGreaterThanOrEqZero, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 787 | }, 788 | InsnParser::IFGT => { 789 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 790 | pc += 2; 791 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntGreaterThanZero, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 792 | }, 793 | InsnParser::IFLE => { 794 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 795 | pc += 2; 796 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntLessThanOrEqZero, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 797 | }, 798 | InsnParser::IFLT => { 799 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 800 | pc += 2; 801 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntLessThanZero, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 802 | }, 803 | InsnParser::IFNE => { 804 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 805 | pc += 2; 806 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IntNotEqZero, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 807 | }, 808 | InsnParser::IFNONNULL => { 809 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 810 | pc += 2; 811 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::NotNull, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 812 | }, 813 | InsnParser::IFNULL => { 814 | let to = (rdr.read_i16::()? as i32 + this_pc as i32) as u32; 815 | pc += 2; 816 | Insn::ConditionalJump(ConditionalJumpInsn::new(JumpCondition::IsNull, *pc_label_map.get(&to).ok_or_else(ParserError::unmapped_label)?)) 817 | }, 818 | InsnParser::IINC => { 819 | let index = rdr.read_u8()?; 820 | let amount = rdr.read_i8()?; 821 | pc += 2; 822 | Insn::IncrementInt(IncrementIntInsn::new(index as u16, amount as i16)) 823 | }, 824 | InsnParser::ILOAD => { 825 | let index = rdr.read_u8()?; 826 | pc += 1; 827 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Int, index as u16)) 828 | }, 829 | InsnParser::ILOAD_0 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Int, 0)), 830 | InsnParser::ILOAD_1 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Int, 1)), 831 | InsnParser::ILOAD_2 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Int, 2)), 832 | InsnParser::ILOAD_3 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Int, 3)), 833 | InsnParser::IMPDEP1 => Insn::ImpDep1(ImpDep1Insn::new()), 834 | InsnParser::IMPDEP2 => Insn::ImpDep2(ImpDep2Insn::new()), 835 | InsnParser::IMUL => Insn::Multiply(MultiplyInsn::new(PrimitiveType::Int)), 836 | InsnParser::INEG => Insn::Negate(NegateInsn::new(PrimitiveType::Int)), 837 | InsnParser::INSTANCEOF => { 838 | let class = constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone(); 839 | pc += 2; 840 | Insn::InstanceOf(InstanceOfInsn::new(class)) 841 | }, 842 | InsnParser::INVOKEDYNAMIC => { 843 | let dyn_info = constant_pool.invokedynamicinfo(rdr.read_u16::()?)?; 844 | rdr.read_u16::()?; 845 | pc += 4; 846 | // TODO: Resolve bootstrap methods 847 | 848 | let name_and_type = constant_pool.nameandtype(dyn_info.name_and_type_index)?; 849 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 850 | let descriptor = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 851 | Insn::InvokeDynamic(InvokeDynamicInsn::new(name, descriptor, BootstrapMethodType::InvokeStatic, String::from("Unimplemented"), String::from("Unimplemented"), String::from("Unimplemented"), Vec::new())) 852 | }, 853 | InsnParser::INVOKEINTERFACE => { 854 | let method = constant_pool.interfacemethodref(rdr.read_u16::()?)?; 855 | let _count = rdr.read_u8()?; // serves 0 purpose? nice one jvm 856 | rdr.read_u8()?; // well at least it serves more purpose than this 857 | pc += 4; 858 | 859 | let name_and_type = constant_pool.nameandtype(method.name_and_type_index)?; 860 | let class = constant_pool.utf8(constant_pool.class(method.class_index)?.name_index)?.str.clone(); 861 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 862 | let descriptor = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 863 | Insn::Invoke(InvokeInsn::new(InvokeType::Instance, class, name, descriptor, true)) 864 | } 865 | InsnParser::INVOKESPECIAL => { 866 | let method_index = rdr.read_u16::()?; 867 | pc += 2; 868 | 869 | let (method, interface_method) = constant_pool.any_method(method_index)?; 870 | let name_and_type = constant_pool.nameandtype(method.name_and_type_index)?; 871 | let class = constant_pool.utf8(constant_pool.class(method.class_index)?.name_index)?.str.clone(); 872 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 873 | let descriptor = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 874 | 875 | Insn::Invoke(InvokeInsn::new(InvokeType::Special, class, name, descriptor, interface_method)) 876 | }, 877 | InsnParser::INVOKESTATIC => { 878 | let method_index = rdr.read_u16::()?; 879 | pc += 2; 880 | 881 | let (method, interface_method) = constant_pool.any_method(method_index)?; 882 | let name_and_type = constant_pool.nameandtype(method.name_and_type_index)?; 883 | let class = constant_pool.utf8(constant_pool.class(method.class_index)?.name_index)?.str.clone(); 884 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 885 | let descriptor = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 886 | 887 | Insn::Invoke(InvokeInsn::new(InvokeType::Static, class, name, descriptor, interface_method)) 888 | }, 889 | InsnParser::INVOKEVIRTUAL => { 890 | let method_index = rdr.read_u16::()?; 891 | pc += 2; 892 | 893 | let (method, interface_method) = constant_pool.any_method(method_index)?; 894 | let name_and_type = constant_pool.nameandtype(method.name_and_type_index)?; 895 | let class = constant_pool.utf8(constant_pool.class(method.class_index)?.name_index)?.str.clone(); 896 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 897 | let descriptor = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 898 | 899 | Insn::Invoke(InvokeInsn::new(InvokeType::Instance, class, name, descriptor, interface_method)) 900 | }, 901 | InsnParser::IOR => Insn::Or(OrInsn::new(IntegerType::Int)), 902 | InsnParser::IREM => Insn::Remainder(RemainderInsn::new(PrimitiveType::Int)), 903 | InsnParser::IRETURN => Insn::Return(ReturnInsn::new(ReturnType::Int)), 904 | InsnParser::ISHL => Insn::ShiftLeft(ShiftLeftInsn::new(IntegerType::Int)), 905 | InsnParser::ISHR => Insn::ShiftRight(ShiftRightInsn::new(IntegerType::Int)), 906 | InsnParser::ISTORE => { 907 | let index = rdr.read_u8()?; 908 | pc += 1; 909 | Insn::LocalStore(LocalStoreInsn::new(OpType::Int, index as u16)) 910 | }, 911 | InsnParser::ISTORE_0 => Insn::LocalStore(LocalStoreInsn::new(OpType::Int, 0)), 912 | InsnParser::ISTORE_1 => Insn::LocalStore(LocalStoreInsn::new(OpType::Int, 1)), 913 | InsnParser::ISTORE_2 => Insn::LocalStore(LocalStoreInsn::new(OpType::Int, 2)), 914 | InsnParser::ISTORE_3 => Insn::LocalStore(LocalStoreInsn::new(OpType::Int, 3)), 915 | InsnParser::ISUB => Insn::Subtract(SubtractInsn::new(PrimitiveType::Int)), 916 | InsnParser::IUSHR => Insn::LogicalShiftRight(LogicalShiftRightInsn::new(IntegerType::Int)), 917 | InsnParser::IXOR => Insn::Xor(XorInsn::new(IntegerType::Int)), 918 | //InsnParser::JSR => 919 | //InsnParser::JSR_W => 920 | InsnParser::L2D => Insn::Convert(ConvertInsn::new(PrimitiveType::Long, PrimitiveType::Double)), 921 | InsnParser::L2F => Insn::Convert(ConvertInsn::new(PrimitiveType::Long, PrimitiveType::Float)), 922 | InsnParser::L2I => Insn::Convert(ConvertInsn::new(PrimitiveType::Long, PrimitiveType::Int)), 923 | InsnParser::LADD => Insn::Add(AddInsn::new(PrimitiveType::Long)), 924 | InsnParser::LALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Long)), 925 | InsnParser::LAND => Insn::And(AndInsn::new(IntegerType::Long)), 926 | InsnParser::LASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Long)), 927 | InsnParser::LCMP => Insn::Compare(CompareInsn::new(PrimitiveType::Long, false)), 928 | InsnParser::LCONST_0 => Insn::Ldc(LdcInsn::new(LdcType::Long(0))), 929 | InsnParser::LCONST_1 => Insn::Ldc(LdcInsn::new(LdcType::Long(1))), 930 | InsnParser::LDC => { 931 | let index = rdr.read_u8()? as u16; 932 | pc += 1; 933 | InsnParser::parse_ldc(index, constant_pool)? 934 | }, 935 | InsnParser::LDC_W => { 936 | let index = rdr.read_u16::()?; 937 | pc += 2; 938 | InsnParser::parse_ldc(index, constant_pool)? 939 | }, 940 | InsnParser::LDC2_W => { 941 | let index = rdr.read_u16::()?; 942 | pc += 2; 943 | InsnParser::parse_ldc(index, constant_pool)? 944 | }, 945 | InsnParser::LDIV => Insn::Divide(DivideInsn::new(PrimitiveType::Long)), 946 | InsnParser::LLOAD => { 947 | let index = rdr.read_u8()?; 948 | pc += 1; 949 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, index as u16)) 950 | }, 951 | InsnParser::LLOAD_0 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Long, 0)), 952 | InsnParser::LLOAD_1 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Long, 1)), 953 | InsnParser::LLOAD_2 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Long, 2)), 954 | InsnParser::LLOAD_3 => Insn::LocalLoad(LocalLoadInsn::new(OpType::Long, 3)), 955 | InsnParser::LMUL => Insn::Multiply(MultiplyInsn::new(PrimitiveType::Long)), 956 | InsnParser::LNEG => Insn::Negate(NegateInsn::new(PrimitiveType::Long)), 957 | InsnParser::LOOKUPSWITCH => { 958 | let pad = 3 - (this_pc % 4); 959 | rdr.read_nbytes(pad as usize)?; 960 | 961 | let default = (rdr.read_i32::()? + this_pc as i32) as u32; 962 | let npairs = rdr.read_i32::()? as u32; 963 | 964 | let mut insn = LookupSwitchInsn::new(*pc_label_map.get(&default).ok_or_else(ParserError::unmapped_label)?); 965 | 966 | for i in 0..npairs { 967 | let matc = rdr.read_i32::()?; 968 | let jump = (rdr.read_i32::()? + this_pc as i32) as u32; 969 | insn.cases.insert(matc, *pc_label_map.get(&jump).ok_or_else(ParserError::unmapped_label)?); 970 | } 971 | 972 | pc += pad + (2 * 4) + (npairs * 2 * 4); 973 | 974 | Insn::LookupSwitch(insn) 975 | } 976 | InsnParser::LOR => Insn::Or(OrInsn::new(IntegerType::Long)), 977 | InsnParser::LREM => Insn::Remainder(RemainderInsn::new(PrimitiveType::Long)), 978 | InsnParser::LRETURN => Insn::Return(ReturnInsn::new(ReturnType::Long)), 979 | InsnParser::LSHL => Insn::ShiftLeft(ShiftLeftInsn::new(IntegerType::Long)), 980 | InsnParser::LSHR => Insn::ShiftRight(ShiftRightInsn::new(IntegerType::Long)), 981 | InsnParser::LSTORE => { 982 | let index = rdr.read_u8()?; 983 | pc += 1; 984 | Insn::LocalStore(LocalStoreInsn::new(OpType::Long, index as u16)) 985 | }, 986 | InsnParser::LSTORE_0 => Insn::LocalStore(LocalStoreInsn::new(OpType::Long, 0)), 987 | InsnParser::LSTORE_1 => Insn::LocalStore(LocalStoreInsn::new(OpType::Long, 1)), 988 | InsnParser::LSTORE_2 => Insn::LocalStore(LocalStoreInsn::new(OpType::Long, 2)), 989 | InsnParser::LSTORE_3 => Insn::LocalStore(LocalStoreInsn::new(OpType::Long, 3)), 990 | InsnParser::LSUB => Insn::Subtract(SubtractInsn::new(PrimitiveType::Long)), 991 | InsnParser::LUSHR => Insn::LogicalShiftRight(LogicalShiftRightInsn::new(IntegerType::Long)), 992 | InsnParser::LXOR => Insn::Xor(XorInsn::new(IntegerType::Long)), 993 | InsnParser::MONITORENTER => Insn::MonitorEnter(MonitorEnterInsn::new()), 994 | InsnParser::MONITOREXIT => Insn::MonitorExit(MonitorExitInsn::new()), 995 | InsnParser::MULTIANEWARRAY => { 996 | let kind = constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone(); 997 | let dimensions = rdr.read_u8()?; 998 | pc += 3; 999 | Insn::MultiNewArray(MultiNewArrayInsn::new(kind, dimensions)) 1000 | }, 1001 | InsnParser::NEW => { 1002 | let kind = constant_pool.utf8(constant_pool.class(rdr.read_u16::()?)?.name_index)?.str.clone(); 1003 | pc += 2; 1004 | Insn::NewObject(NewObjectInsn::new(kind)) 1005 | }, 1006 | InsnParser::NEWARRAY => { 1007 | let atype = rdr.read_u8()?; 1008 | pc += 1; 1009 | let kind = match atype { 1010 | 4 => Type::Boolean, 1011 | 5 => Type::Char, 1012 | 6 => Type::Float, 1013 | 7 => Type::Double, 1014 | 8 => Type::Byte, 1015 | 9 => Type::Short, 1016 | 10 => Type::Int, 1017 | 11 => Type::Long, 1018 | _ => return Err(ParserError::other("Unknown Primitive Type")) 1019 | }; 1020 | Insn::NewArray(NewArrayInsn::new(kind)) 1021 | }, 1022 | InsnParser::NOP => Insn::Nop(NopInsn::new()), 1023 | InsnParser::POP => Insn::Pop(PopInsn::new(false)), 1024 | InsnParser::POP2 => Insn::Pop(PopInsn::new(true)), 1025 | InsnParser::PUTFIELD => { 1026 | let field_ref = constant_pool.fieldref(rdr.read_u16::()?)?; 1027 | pc += 2; 1028 | let name_and_type = constant_pool.nameandtype(field_ref.name_and_type_index)?; 1029 | let class = constant_pool.utf8(constant_pool.class(field_ref.class_index)?.name_index)?.str.clone(); 1030 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 1031 | let desc = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 1032 | Insn::PutField(PutFieldInsn::new(true, class, name, desc)) 1033 | }, 1034 | InsnParser::PUTSTATIC => { 1035 | let field_ref = constant_pool.fieldref(rdr.read_u16::()?)?; 1036 | pc += 2; 1037 | let name_and_type = constant_pool.nameandtype(field_ref.name_and_type_index)?; 1038 | let class = constant_pool.utf8(constant_pool.class(field_ref.class_index)?.name_index)?.str.clone(); 1039 | let name = constant_pool.utf8(name_and_type.name_index)?.str.clone(); 1040 | let desc = constant_pool.utf8(name_and_type.descriptor_index)?.str.clone(); 1041 | Insn::PutField(PutFieldInsn::new(false, class, name, desc)) 1042 | }, 1043 | //InsnParser::RET => 1044 | InsnParser::RETURN => Insn::Return(ReturnInsn::new(ReturnType::Void)), 1045 | InsnParser::SALOAD => Insn::ArrayLoad(ArrayLoadInsn::new(Type::Short)), 1046 | InsnParser::SASTORE => Insn::ArrayStore(ArrayStoreInsn::new(Type::Short)), 1047 | InsnParser::SIPUSH => { 1048 | let short = rdr.read_i16::()?; 1049 | pc += 2; 1050 | Insn::Ldc(LdcInsn::new(LdcType::Int(short as i32))) 1051 | }, 1052 | InsnParser::SWAP => Insn::Swap(SwapInsn::new()), 1053 | InsnParser::TABLESWITCH => { 1054 | let pad = 3 - (this_pc % 4); 1055 | rdr.read_nbytes(pad as usize)?; 1056 | 1057 | let default = (rdr.read_i32::()? + this_pc as i32) as u32; 1058 | 1059 | let low = rdr.read_i32::()?; 1060 | let high = rdr.read_i32::()?; 1061 | let num_cases = (high - low + 1) as u32; 1062 | let mut cases: Vec = Vec::with_capacity(num_cases as usize); 1063 | for i in 0..num_cases { 1064 | let case = (rdr.read_i32::()? + this_pc as i32) as u32; 1065 | cases.push(*pc_label_map.get(&case).ok_or_else(ParserError::unmapped_label)?); 1066 | } 1067 | 1068 | pc += pad + ((3 + num_cases) * 4); 1069 | 1070 | Insn::TableSwitch(TableSwitchInsn { 1071 | default: *pc_label_map.get(&default).ok_or_else(ParserError::unmapped_label)?, 1072 | low, 1073 | cases 1074 | }) 1075 | }, 1076 | InsnParser::WIDE => { 1077 | let opcode = rdr.read_u8()?; 1078 | pc += 1; 1079 | match opcode { 1080 | InsnParser::ILOAD => { 1081 | let index = rdr.read_u16::()?; 1082 | pc += 2; 1083 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Int, index)) 1084 | }, 1085 | InsnParser::FLOAD => { 1086 | let index = rdr.read_u16::()?; 1087 | pc += 2; 1088 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Float, index)) 1089 | }, 1090 | InsnParser::ALOAD => { 1091 | let index = rdr.read_u16::()?; 1092 | pc += 2; 1093 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Reference, index)) 1094 | }, 1095 | InsnParser::LLOAD => { 1096 | let index = rdr.read_u16::()?; 1097 | pc += 2; 1098 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Long, index)) 1099 | }, 1100 | InsnParser::DLOAD => { 1101 | let index = rdr.read_u16::()?; 1102 | pc += 2; 1103 | Insn::LocalLoad(LocalLoadInsn::new(OpType::Double, index)) 1104 | }, 1105 | InsnParser::ISTORE => { 1106 | let index = rdr.read_u16::()?; 1107 | pc += 2; 1108 | Insn::LocalStore(LocalStoreInsn::new(OpType::Int, index)) 1109 | }, 1110 | InsnParser::FSTORE => { 1111 | let index = rdr.read_u16::()?; 1112 | pc += 2; 1113 | Insn::LocalStore(LocalStoreInsn::new(OpType::Float, index)) 1114 | }, 1115 | InsnParser::LSTORE => { 1116 | let index = rdr.read_u16::()?; 1117 | pc += 2; 1118 | Insn::LocalStore(LocalStoreInsn::new(OpType::Long, index)) 1119 | }, 1120 | InsnParser::DSTORE => { 1121 | let index = rdr.read_u16::()?; 1122 | pc += 2; 1123 | Insn::LocalStore(LocalStoreInsn::new(OpType::Double, index)) 1124 | }, 1125 | InsnParser::IINC => { 1126 | let index = rdr.read_u16::()?; 1127 | let amount = rdr.read_i16::()?; 1128 | pc += 4; 1129 | Insn::IncrementInt(IncrementIntInsn::new(index, amount)) 1130 | } 1131 | InsnParser::RET => unimplemented!("Wide Ret instructions are not implemented"), 1132 | _ => return Err(ParserError::invalid_insn(this_pc, format!("Invalid wide opcode {:x}", opcode))) 1133 | } 1134 | } 1135 | _ => return Err(ParserError::unknown_insn(opcode)) 1136 | }; 1137 | insns.push(insn); 1138 | } 1139 | 1140 | // there can be a label at the end of the code space, e.g. for an end exception handler 1141 | if let Some(lbl) = pc_label_map.get(&pc) { 1142 | insns.push(Insn::Label(*lbl)); 1143 | } 1144 | 1145 | let list = InsnList { 1146 | insns, 1147 | labels: pc_label_map.len() as u32 1148 | }; 1149 | 1150 | Ok(list) 1151 | } 1152 | 1153 | fn remap_label_nodes(x: &mut LabelInsn, list: &mut InsnList, pc_index_map: &HashMap, insert: &mut HashMap>) -> Result<()> { 1154 | let mut insert_into = *match pc_index_map.get(&x.id) { 1155 | Some(x) => x, 1156 | _ => return Err(ParserError::out_of_bounds_jump(x.id as i32)) 1157 | }; 1158 | 1159 | for (i, insns) in insert.iter() { 1160 | for _ in 0..insns.len() { 1161 | if insert_into as usize > *i { 1162 | insert_into += 1; 1163 | } 1164 | } 1165 | } 1166 | 1167 | let jump_to = list.new_label(); 1168 | x.id = jump_to.id; 1169 | 1170 | insert.entry(insert_into as usize) 1171 | .or_insert_with(|| Vec::with_capacity(1)) 1172 | .push(Insn::Label(jump_to)); 1173 | Ok(()) 1174 | } 1175 | 1176 | fn parse_ldc(index: CPIndex, constant_pool: &ConstantPool) -> Result { 1177 | let constant = constant_pool.get(index)?; 1178 | let ldc_type = match constant { 1179 | ConstantType::String(x) => LdcType::String(constant_pool.utf8(x.utf_index)?.str.clone()), 1180 | ConstantType::Integer(x) => LdcType::Int(x.inner()), 1181 | ConstantType::Float(x) => LdcType::Float(x.inner()), 1182 | ConstantType::Double(x) => LdcType::Double(x.inner()), 1183 | ConstantType::Long(x) => LdcType::Long(x.inner()), 1184 | ConstantType::Class(x) => LdcType::Class(constant_pool.utf8(x.name_index)?.str.clone()), 1185 | ConstantType::MethodType(x) => LdcType::MethodType(constant_pool.utf8(x.descriptor_index)?.str.clone()), 1186 | ConstantType::MethodHandle(x) => return Err(ParserError::unimplemented("MethodHandle LDC")), 1187 | ConstantType::Dynamic(x) => return Err(ParserError::unimplemented("Dynamic LDC")), 1188 | x => return Err(ParserError::incomp_cp( 1189 | "LDC Constant Type", 1190 | constant, 1191 | index as usize 1192 | )) 1193 | }; 1194 | Ok(Insn::Ldc(LdcInsn::new(ldc_type))) 1195 | } 1196 | 1197 | fn write_insns(code: &CodeAttribute, constant_pool: &mut ConstantPoolWriter) -> Result<(Vec, HashMap)> { 1198 | let mut wtr: Cursor> = Cursor::new(Vec::with_capacity(code.insns.len())); 1199 | 1200 | let mut label_pc_map: HashMap = HashMap::new(); 1201 | 1202 | enum ReferenceType { 1203 | /// 0: GOTO 1204 | /// 1: indexbyte_1 1205 | /// 2: indexbyte_2 1206 | /// 3: NOP 1207 | /// 4: NOP 1208 | Jump(u32), 1209 | /// 0: OPCODE (IF_IMPEQ, IFEQ...) 1210 | /// 1: indexbyte_1 1211 | /// 2: indexbyte_2 1212 | /// 3: NOP 1213 | /// 4: NOP 1214 | /// 5: NOP 1215 | /// 6: NOP 1216 | /// 7: NOP 1217 | Conditional(u32), 1218 | /// 0: indexbyte_1 1219 | /// 1: indexbyte_2 1220 | /// 2: indexbyte_3 1221 | /// 3: indexbyte_4 1222 | Direct(u32) 1223 | } 1224 | 1225 | let mut forward_references: HashMap> = HashMap::new(); 1226 | 1227 | let mut pc = 0u32; 1228 | for insn in code.insns.iter() { 1229 | match insn { 1230 | Insn::Label(x) => { 1231 | label_pc_map.insert(*x, pc); 1232 | if let Some(refs) = forward_references.get(x) { 1233 | let vec_mut = wtr.get_mut(); 1234 | for ref_t in refs.iter() { 1235 | match ref_t { 1236 | ReferenceType::Jump(at) => { 1237 | let i = *at as usize; 1238 | let offset: i32 = pc as i32 - i as i32; 1239 | let off_bytes = offset.to_be_bytes(); 1240 | if off_bytes[0] == 0 && off_bytes[1] == 0 { 1241 | vec_mut[i + 1] = off_bytes[2]; 1242 | vec_mut[i + 2] = off_bytes[3]; 1243 | } else { 1244 | // need to replace with a GOTO_W 1245 | vec_mut[i] = InsnParser::GOTO_W; 1246 | vec_mut[i + 1] = off_bytes[0]; 1247 | vec_mut[i + 2] = off_bytes[1]; 1248 | vec_mut[i + 3] = off_bytes[2]; 1249 | vec_mut[i + 4] = off_bytes[3]; 1250 | } 1251 | } 1252 | ReferenceType::Conditional(at) => { 1253 | let i = *at as usize; 1254 | let offset_1: i32 = pc as i32 - i as i32; 1255 | let off_bytes = offset_1.to_be_bytes(); 1256 | if off_bytes[0] == 0 && off_bytes[1] == 0 { 1257 | vec_mut[i + 1] = off_bytes[2]; 1258 | vec_mut[i + 2] = off_bytes[3]; 1259 | } else { 1260 | // need to add a a GOTO_W 1261 | let off_bytes_1 = 3i32.to_be_bytes(); 1262 | vec_mut[i + 1] = off_bytes_1[2]; 1263 | vec_mut[i + 2] = off_bytes_1[3]; 1264 | let offset_2: i32 = pc as i32 - i as i32 - 3; 1265 | let off_bytes_2 = offset_2.to_be_bytes(); 1266 | vec_mut[i + 3] = InsnParser::GOTO_W; 1267 | vec_mut[i + 4] = off_bytes_2[0]; 1268 | vec_mut[i + 5] = off_bytes_2[1]; 1269 | vec_mut[i + 6] = off_bytes_2[2]; 1270 | vec_mut[i + 7] = off_bytes_2[3]; 1271 | } 1272 | } 1273 | ReferenceType::Direct(at) => { 1274 | let i = *at as usize; 1275 | let offset: i32 = pc as i32 - i as i32; 1276 | let off_bytes = offset.to_be_bytes(); 1277 | vec_mut[i] = off_bytes[0]; 1278 | vec_mut[i + 1] = off_bytes[1]; 1279 | vec_mut[i + 2] = off_bytes[2]; 1280 | vec_mut[i + 3] = off_bytes[3]; 1281 | } 1282 | } 1283 | } 1284 | } 1285 | } 1286 | Insn::ArrayLoad(x) => { 1287 | wtr.write_u8(match &x.kind { 1288 | Type::Reference(x) => InsnParser::AALOAD, 1289 | Type::Byte | Type::Boolean => InsnParser::BALOAD, 1290 | Type::Char => InsnParser::CALOAD, 1291 | Type::Short => InsnParser::SALOAD, 1292 | Type::Int => InsnParser::IALOAD, 1293 | Type::Long => InsnParser::LALOAD, 1294 | Type::Float => InsnParser::FALOAD, 1295 | Type::Double => InsnParser::DALOAD, 1296 | Type::Void => return Err(ParserError::invalid_insn(pc, "Cannot use type Void in array load")) 1297 | })?; 1298 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1299 | } 1300 | Insn::ArrayStore(x) => { 1301 | wtr.write_u8(match &x.kind { 1302 | Type::Reference(x) => InsnParser::AASTORE, 1303 | Type::Byte | Type::Boolean => InsnParser::BASTORE, 1304 | Type::Char => InsnParser::CASTORE, 1305 | Type::Short => InsnParser::SASTORE, 1306 | Type::Int => InsnParser::IASTORE, 1307 | Type::Long => InsnParser::LASTORE, 1308 | Type::Float => InsnParser::FASTORE, 1309 | Type::Double => InsnParser::DASTORE, 1310 | Type::Void => return Err(ParserError::invalid_insn(pc, "Cannot use type Void in array store")) 1311 | })?; 1312 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1313 | } 1314 | Insn::Ldc(x) => { 1315 | pc = pc.checked_add(match &x.constant { 1316 | LdcType::Null => { 1317 | wtr.write_u8(InsnParser::ACONST_NULL)?; 1318 | 1 1319 | } 1320 | LdcType::String(x) => InsnParser::write_ldc(&mut wtr, constant_pool.string_utf(x.clone()), false)?, 1321 | LdcType::Int(x) => InsnParser::write_ldc(&mut wtr, constant_pool.integer(*x), false)?, 1322 | LdcType::Float(x) => InsnParser::write_ldc(&mut wtr, constant_pool.float(*x), false)?, 1323 | LdcType::Long(x) => InsnParser::write_ldc(&mut wtr, constant_pool.long(*x), false)?, 1324 | LdcType::Double(x) => InsnParser::write_ldc(&mut wtr, constant_pool.double(*x), false)?, 1325 | LdcType::Class(x) => InsnParser::write_ldc(&mut wtr, constant_pool.class_utf8(x.clone()), false)?, 1326 | LdcType::MethodType(x) => InsnParser::write_ldc(&mut wtr, constant_pool.methodtype_utf8(x.clone()), false)?, 1327 | LdcType::MethodHandle() => return Err(ParserError::invalid_insn(pc, "MethodHandle LDC")), 1328 | LdcType::Dynamic() => return Err(ParserError::invalid_insn(pc, "Dynamic LDC")), 1329 | }).ok_or_else(ParserError::too_many_instructions)?; 1330 | } 1331 | Insn::LocalLoad(x) => { 1332 | let (op0, op1, op2, op3, opx) = match &x.kind { 1333 | OpType::Reference => (InsnParser::ALOAD_0, InsnParser::ALOAD_1, InsnParser::ALOAD_2, InsnParser::ALOAD_3, InsnParser::ALOAD), 1334 | OpType::Short | OpType::Char | OpType::Byte | OpType::Boolean | OpType::Int => (InsnParser::ILOAD_0, InsnParser::ILOAD_1, InsnParser::ILOAD_2, InsnParser::ILOAD_3, InsnParser::ILOAD), 1335 | OpType::Float => (InsnParser::FLOAD_0, InsnParser::FLOAD_1, InsnParser::FLOAD_2, InsnParser::FLOAD_3, InsnParser::FLOAD), 1336 | OpType::Double => (InsnParser::DLOAD_0, InsnParser::DLOAD_1, InsnParser::DLOAD_2, InsnParser::DLOAD_3, InsnParser::DLOAD), 1337 | OpType::Long => (InsnParser::LLOAD_0, InsnParser::LLOAD_1, InsnParser::LLOAD_2, InsnParser::LLOAD_3, InsnParser::LLOAD), 1338 | }; 1339 | match x.index { 1340 | 0 => { 1341 | wtr.write_u8(op0)?; 1342 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1343 | } 1344 | 1 => { 1345 | wtr.write_u8(op1)?; 1346 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1347 | } 1348 | 2 => { 1349 | wtr.write_u8(op2)?; 1350 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1351 | } 1352 | 3 => { 1353 | wtr.write_u8(op3)?; 1354 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1355 | } 1356 | index => { 1357 | if index <= 0xFF { 1358 | wtr.write_u8(opx)?; 1359 | wtr.write_u8(index as u8)?; 1360 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1361 | } else { 1362 | wtr.write_u8(InsnParser::WIDE)?; 1363 | wtr.write_u8(opx)?; 1364 | wtr.write_u16::(index)?; 1365 | pc = pc.checked_add(4).ok_or_else(ParserError::too_many_instructions)?; 1366 | } 1367 | } 1368 | } 1369 | } 1370 | Insn::LocalStore(x) => { 1371 | let (op0, op1, op2, op3, opx) = match &x.kind { 1372 | OpType::Reference => (InsnParser::ASTORE_0, InsnParser::ASTORE_1, InsnParser::ASTORE_2, InsnParser::ASTORE_3, InsnParser::ASTORE), 1373 | OpType::Boolean | OpType::Byte | OpType::Char | OpType::Short | OpType::Int => (InsnParser::ISTORE_0, InsnParser::ISTORE_1, InsnParser::ISTORE_2, InsnParser::ISTORE_3, InsnParser::ISTORE), 1374 | OpType::Float => (InsnParser::FSTORE_0, InsnParser::FSTORE_1, InsnParser::FSTORE_2, InsnParser::FSTORE_3, InsnParser::FSTORE), 1375 | OpType::Double => (InsnParser::DSTORE_0, InsnParser::DSTORE_1, InsnParser::DSTORE_2, InsnParser::DSTORE_3, InsnParser::DSTORE), 1376 | OpType::Long => (InsnParser::LSTORE_0, InsnParser::LSTORE_1, InsnParser::LSTORE_2, InsnParser::LSTORE_3, InsnParser::LSTORE) 1377 | }; 1378 | match x.index { 1379 | 0 => { 1380 | wtr.write_u8(op0)?; 1381 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1382 | } 1383 | 1 => { 1384 | wtr.write_u8(op1)?; 1385 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1386 | } 1387 | 2 => { 1388 | wtr.write_u8(op2)?; 1389 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1390 | } 1391 | 3 => { 1392 | wtr.write_u8(op3)?; 1393 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1394 | } 1395 | index => { 1396 | if index <= 0xFF { 1397 | wtr.write_u8(opx)?; 1398 | wtr.write_u8(index as u8)?; 1399 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1400 | } else { 1401 | wtr.write_u8(InsnParser::WIDE)?; 1402 | wtr.write_u8(opx)?; 1403 | wtr.write_u16::(index)?; 1404 | pc = pc.checked_add(4).ok_or_else(ParserError::too_many_instructions)?; 1405 | } 1406 | } 1407 | } 1408 | } 1409 | Insn::NewArray(x) => { 1410 | match &x.kind { 1411 | Type::Reference(x) => { 1412 | let cls = if let Some(cls) = x { 1413 | cls.clone() 1414 | } else { 1415 | // technically this should be invalid and we could throw an error 1416 | // but it's better to just assume the user wants an Object 1417 | String::from("java/lang/Object") 1418 | }; 1419 | wtr.write_u8(InsnParser::ANEWARRAY)?; 1420 | wtr.write_u16::(constant_pool.class_utf8(cls))?; 1421 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1422 | } 1423 | Type::Boolean => { 1424 | wtr.write_u8(InsnParser::NEWARRAY)?; 1425 | wtr.write_u8(4)?; 1426 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1427 | } 1428 | Type::Byte => { 1429 | wtr.write_u8(InsnParser::NEWARRAY)?; 1430 | wtr.write_u8(8)?; 1431 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1432 | } 1433 | Type::Char => { 1434 | wtr.write_u8(InsnParser::NEWARRAY)?; 1435 | wtr.write_u8(5)?; 1436 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1437 | } 1438 | Type::Short => { 1439 | wtr.write_u8(InsnParser::NEWARRAY)?; 1440 | wtr.write_u8(9)?; 1441 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1442 | } 1443 | Type::Int => { 1444 | wtr.write_u8(InsnParser::NEWARRAY)?; 1445 | wtr.write_u8(10)?; 1446 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1447 | } 1448 | Type::Long => { 1449 | wtr.write_u8(InsnParser::NEWARRAY)?; 1450 | wtr.write_u8(11)?; 1451 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1452 | } 1453 | Type::Float => { 1454 | wtr.write_u8(InsnParser::NEWARRAY)?; 1455 | wtr.write_u8(6)?; 1456 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1457 | } 1458 | Type::Double => { 1459 | wtr.write_u8(InsnParser::NEWARRAY)?; 1460 | wtr.write_u8(7)?; 1461 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1462 | }, 1463 | Type::Void => return Err(ParserError::invalid_insn(pc, "Cannot use type Void in newarray")) 1464 | } 1465 | } 1466 | Insn::Return(x) => { 1467 | match &x.kind { 1468 | ReturnType::Void => wtr.write_u8(InsnParser::RETURN)?, 1469 | ReturnType::Reference => wtr.write_u8(InsnParser::ARETURN)?, 1470 | // boolean, byte, char and short all use the int return (same size) 1471 | ReturnType::Boolean => wtr.write_u8(InsnParser::IRETURN)?, 1472 | ReturnType::Byte => wtr.write_u8(InsnParser::IRETURN)?, 1473 | ReturnType::Char => wtr.write_u8(InsnParser::IRETURN)?, 1474 | ReturnType::Short => wtr.write_u8(InsnParser::IRETURN)?, 1475 | ReturnType::Int => wtr.write_u8(InsnParser::IRETURN)?, 1476 | ReturnType::Long => wtr.write_u8(InsnParser::LRETURN)?, 1477 | ReturnType::Float => wtr.write_u8(InsnParser::FRETURN)?, 1478 | ReturnType::Double => wtr.write_u8(InsnParser::DRETURN)?, 1479 | } 1480 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1481 | } 1482 | Insn::ArrayLength(x) => { 1483 | wtr.write_u8(InsnParser::ARRAYLENGTH)?; 1484 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1485 | } 1486 | Insn::Throw(x) => { 1487 | wtr.write_u8(InsnParser::ATHROW)?; 1488 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1489 | } 1490 | Insn::CheckCast(x) => { 1491 | wtr.write_u8(InsnParser::CHECKCAST)?; 1492 | wtr.write_u16::(constant_pool.class_utf8(x.kind.clone()))?; 1493 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1494 | } 1495 | Insn::Convert(x) => { 1496 | match &x.from { 1497 | PrimitiveType::Short | PrimitiveType::Char | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Int => { 1498 | wtr.write_u8(match &x.to { 1499 | PrimitiveType::Boolean | PrimitiveType::Byte => InsnParser::I2B, 1500 | PrimitiveType::Char => InsnParser::I2C, 1501 | PrimitiveType::Short => InsnParser::I2S, 1502 | PrimitiveType::Int => InsnParser::NOP, 1503 | PrimitiveType::Long => InsnParser::I2L, 1504 | PrimitiveType::Float => InsnParser::I2F, 1505 | PrimitiveType::Double => InsnParser::I2D 1506 | })?; 1507 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1508 | } 1509 | PrimitiveType::Long => { 1510 | wtr.write_u8(match &x.to { 1511 | PrimitiveType::Short | PrimitiveType::Char | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Int => InsnParser::L2I, 1512 | PrimitiveType::Long => InsnParser::NOP, 1513 | PrimitiveType::Float => InsnParser::L2F, 1514 | PrimitiveType::Double => InsnParser::L2D 1515 | })?; 1516 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1517 | } 1518 | PrimitiveType::Float => { 1519 | wtr.write_u8(match &x.to { 1520 | PrimitiveType::Short | PrimitiveType::Char | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Int => InsnParser::F2I, 1521 | PrimitiveType::Long => InsnParser::F2L, 1522 | PrimitiveType::Float => InsnParser::NOP, 1523 | PrimitiveType::Double => InsnParser::F2D 1524 | })?; 1525 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1526 | } 1527 | PrimitiveType::Double => { 1528 | wtr.write_u8(match &x.to { 1529 | PrimitiveType::Short | PrimitiveType::Char | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Int => InsnParser::D2I, 1530 | PrimitiveType::Long => InsnParser::D2L, 1531 | PrimitiveType::Float => InsnParser::D2F, 1532 | PrimitiveType::Double => InsnParser::NOP 1533 | })?; 1534 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1535 | } 1536 | } 1537 | } 1538 | Insn::Add(x) => { 1539 | wtr.write_u8(match &x.kind { 1540 | PrimitiveType::Boolean => InsnParser::IADD, 1541 | PrimitiveType::Byte => InsnParser::IADD, 1542 | PrimitiveType::Char => InsnParser::IADD, 1543 | PrimitiveType::Short => InsnParser::IADD, 1544 | PrimitiveType::Int => InsnParser::IADD, 1545 | PrimitiveType::Long => InsnParser::LADD, 1546 | PrimitiveType::Float => InsnParser::FADD, 1547 | PrimitiveType::Double => InsnParser::DADD 1548 | })?; 1549 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1550 | } 1551 | Insn::Compare(x) => { 1552 | match &x.kind { 1553 | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Char | PrimitiveType::Short | PrimitiveType::Int => { 1554 | // there's no int comparison opcode, but we can use long comparison 1555 | wtr.write_u8(InsnParser::I2L)?; 1556 | wtr.write_u8(InsnParser::LCMP)?; 1557 | pc = pc.checked_add(2).ok_or_else(ParserError::too_many_instructions)?; 1558 | } 1559 | PrimitiveType::Long => { 1560 | wtr.write_u8(InsnParser::LCMP)?; 1561 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1562 | } 1563 | PrimitiveType::Float => { 1564 | wtr.write_u8(if x.pos_on_nan { InsnParser::FCMPG } else { InsnParser::FCMPL })?; 1565 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1566 | } 1567 | PrimitiveType::Double => { 1568 | wtr.write_u8(if x.pos_on_nan { InsnParser::DCMPG } else { InsnParser::DCMPL })?; 1569 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1570 | } 1571 | } 1572 | } 1573 | Insn::Divide(x) => { 1574 | wtr.write_u8(match &x.kind { 1575 | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Char | PrimitiveType::Short | PrimitiveType::Int => InsnParser::IDIV, 1576 | PrimitiveType::Long => InsnParser::LDIV, 1577 | PrimitiveType::Float => InsnParser::FDIV, 1578 | PrimitiveType::Double => InsnParser::DDIV 1579 | })?; 1580 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1581 | } 1582 | Insn::Multiply(x) => { 1583 | wtr.write_u8(match &x.kind { 1584 | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Char | PrimitiveType::Short | PrimitiveType::Int => InsnParser::IMUL, 1585 | PrimitiveType::Long => InsnParser::LMUL, 1586 | PrimitiveType::Float => InsnParser::FMUL, 1587 | PrimitiveType::Double => InsnParser::DMUL 1588 | })?; 1589 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1590 | } 1591 | Insn::Negate(x) => { 1592 | wtr.write_u8(match &x.kind { 1593 | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Char | PrimitiveType::Short | PrimitiveType::Int => InsnParser::INEG, 1594 | PrimitiveType::Long => InsnParser::LNEG, 1595 | PrimitiveType::Float => InsnParser::FNEG, 1596 | PrimitiveType::Double => InsnParser::DNEG 1597 | })?; 1598 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1599 | } 1600 | Insn::Remainder(x) => { 1601 | wtr.write_u8(match &x.kind { 1602 | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Char | PrimitiveType::Short | PrimitiveType::Int => InsnParser::IREM, 1603 | PrimitiveType::Long => InsnParser::LREM, 1604 | PrimitiveType::Float => InsnParser::FREM, 1605 | PrimitiveType::Double => InsnParser::DREM 1606 | })?; 1607 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1608 | } 1609 | Insn::Subtract(x) => { 1610 | wtr.write_u8(match &x.kind { 1611 | PrimitiveType::Boolean | PrimitiveType::Byte | PrimitiveType::Char | PrimitiveType::Short | PrimitiveType::Int => InsnParser::ISUB, 1612 | PrimitiveType::Long => InsnParser::LSUB, 1613 | PrimitiveType::Float => InsnParser::FSUB, 1614 | PrimitiveType::Double => InsnParser::DSUB 1615 | })?; 1616 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1617 | } 1618 | Insn::And(x) => { 1619 | wtr.write_u8(match &x.kind { 1620 | IntegerType::Int => InsnParser::IAND, 1621 | IntegerType::Long => InsnParser::LAND 1622 | })?; 1623 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1624 | } 1625 | Insn::Or(x) => { 1626 | wtr.write_u8(match &x.kind { 1627 | IntegerType::Int => InsnParser::IOR, 1628 | IntegerType::Long => InsnParser::LOR 1629 | })?; 1630 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1631 | } 1632 | Insn::Xor(x) => { 1633 | wtr.write_u8(match &x.kind { 1634 | IntegerType::Int => InsnParser::IXOR, 1635 | IntegerType::Long => InsnParser::LXOR 1636 | })?; 1637 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1638 | } 1639 | Insn::ShiftLeft(x) => { 1640 | wtr.write_u8(match &x.kind { 1641 | IntegerType::Int => InsnParser::ISHL, 1642 | IntegerType::Long => InsnParser::LSHL 1643 | })?; 1644 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1645 | } 1646 | Insn::ShiftRight(x) => { 1647 | wtr.write_u8(match &x.kind { 1648 | IntegerType::Int => InsnParser::ISHR, 1649 | IntegerType::Long => InsnParser::LSHR 1650 | })?; 1651 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1652 | } 1653 | Insn::LogicalShiftRight(x) => { 1654 | wtr.write_u8(match &x.kind { 1655 | IntegerType::Int => InsnParser::IUSHR, 1656 | IntegerType::Long => InsnParser::LUSHR 1657 | })?; 1658 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1659 | } 1660 | Insn::Dup(x) => { 1661 | wtr.write_u8(match x.num { 1662 | 1 => { 1663 | match x.down { 1664 | 0 => InsnParser::DUP, 1665 | 1 => InsnParser::DUP_X1, 1666 | 2 => InsnParser::DUP_X2, 1667 | _ => return Err(ParserError::invalid_insn(pc, "DupInsn::down must not be larger than 2")) 1668 | } 1669 | } 1670 | 2 => { 1671 | match x.down { 1672 | 0 => InsnParser::DUP2, 1673 | 1 => InsnParser::DUP2_X1, 1674 | 2 => InsnParser::DUP2_X2, 1675 | _ => return Err(ParserError::invalid_insn(pc, "DupInsn::down must not be larger than 2")) 1676 | } 1677 | } 1678 | _ => return Err(ParserError::invalid_insn(pc, "DupInsn::num must be in the range 1-2")) 1679 | })?; 1680 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1681 | } 1682 | Insn::Pop(x) => { 1683 | wtr.write_u8(match x.pop_two { 1684 | false => InsnParser::POP, 1685 | true => InsnParser::POP2, 1686 | })?; 1687 | pc = pc.checked_add(1).ok_or_else(ParserError::too_many_instructions)?; 1688 | } 1689 | Insn::GetField(x) => { 1690 | wtr.write_u8(if x.instance { InsnParser::GETFIELD } else { InsnParser::GETSTATIC })?; 1691 | let class_ref = constant_pool.class_utf8(x.class.clone()); 1692 | let name_ref = constant_pool.utf8(x.name.clone()); 1693 | let desc_ref = constant_pool.utf8(x.descriptor.clone()); 1694 | let nametype_ref = constant_pool.nameandtype(name_ref, desc_ref); 1695 | wtr.write_u16::(constant_pool.fieldref(class_ref, nametype_ref))?; 1696 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1697 | } 1698 | Insn::PutField(x) => { 1699 | wtr.write_u8(if x.instance { InsnParser::PUTFIELD } else { InsnParser::PUTSTATIC })?; 1700 | let class_ref = constant_pool.class_utf8(x.class.clone()); 1701 | let name_ref = constant_pool.utf8(x.name.clone()); 1702 | let desc_ref = constant_pool.utf8(x.descriptor.clone()); 1703 | let nametype_ref = constant_pool.nameandtype(name_ref, desc_ref); 1704 | wtr.write_u16::(constant_pool.fieldref(class_ref, nametype_ref))?; 1705 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1706 | } 1707 | Insn::Jump(x) => { 1708 | if let Some(to) = label_pc_map.get(&x.jump_to) { 1709 | let offset: i32 = pc as i32 - (*to) as i32; 1710 | let off_bytes = offset.to_be_bytes(); 1711 | // backwards reference 1712 | if off_bytes[0] == 0 && off_bytes[1] == 0 { 1713 | wtr.write_u8(InsnParser::GOTO)?; 1714 | wtr.write_i16::(offset as i16)?; 1715 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1716 | } else { 1717 | wtr.write_u8(InsnParser::GOTO_W)?; 1718 | wtr.write_i32::(offset)?; 1719 | pc = pc.checked_add(5).ok_or_else(ParserError::too_many_instructions)?; 1720 | } 1721 | } else { 1722 | if let Some(vec) = forward_references.get_mut(&x.jump_to) { 1723 | vec.push(ReferenceType::Jump(pc)); 1724 | } else { 1725 | let vec = vec![ReferenceType::Jump(pc)]; 1726 | forward_references.insert(x.jump_to, vec); 1727 | } 1728 | wtr.write_u8(InsnParser::GOTO)?; 1729 | wtr.write_u16::(0)?; 1730 | wtr.write_u8(InsnParser::NOP)?; 1731 | wtr.write_u8(InsnParser::NOP)?; 1732 | pc = pc.checked_add(8).ok_or_else(ParserError::too_many_instructions)?; 1733 | } 1734 | } 1735 | Insn::ConditionalJump(x) => { 1736 | let opcode = match x.condition { 1737 | JumpCondition::IsNull => InsnParser::IFNULL, 1738 | JumpCondition::NotNull => InsnParser::IFNONNULL, 1739 | JumpCondition::ReferencesEqual => InsnParser::IF_ACMPEQ, 1740 | JumpCondition::ReferencesNotEqual => InsnParser::IF_ACMPNE, 1741 | JumpCondition::IntsEq => InsnParser::IF_ICMPEQ, 1742 | JumpCondition::IntsNotEq => InsnParser::IF_ICMPNE, 1743 | JumpCondition::IntsLessThan => InsnParser::IF_ICMPLT, 1744 | JumpCondition::IntsLessThanOrEq => InsnParser::IF_ICMPLE, 1745 | JumpCondition::IntsGreaterThan => InsnParser::IF_ICMPGT, 1746 | JumpCondition::IntsGreaterThanOrEq => InsnParser::IF_ICMPGE, 1747 | JumpCondition::IntEqZero => InsnParser::IFEQ, 1748 | JumpCondition::IntNotEqZero => InsnParser::IFNE, 1749 | JumpCondition::IntLessThanZero => InsnParser::IFLT, 1750 | JumpCondition::IntLessThanOrEqZero => InsnParser::IFLE, 1751 | JumpCondition::IntGreaterThanZero => InsnParser::IFGT, 1752 | JumpCondition::IntGreaterThanOrEqZero => InsnParser::IFGE 1753 | }; 1754 | 1755 | if let Some(to) = label_pc_map.get(&x.jump_to) { 1756 | let offset: i32 = pc as i32 - (*to) as i32; 1757 | let off_bytes = offset.to_be_bytes(); 1758 | // backwards reference 1759 | if off_bytes[0] == 0 && off_bytes[1] == 0 { 1760 | wtr.write_u8(opcode)?; 1761 | wtr.write_i16::(offset as i16)?; 1762 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1763 | } else { 1764 | wtr.write_u8(opcode)?; 1765 | wtr.write_u16::(3)?; 1766 | wtr.write_u8(InsnParser::GOTO_W)?; 1767 | wtr.write_i32::(offset - 3)?; 1768 | pc = pc.checked_add(8).ok_or_else(ParserError::too_many_instructions)?; 1769 | } 1770 | } else { 1771 | if let Some(vec) = forward_references.get_mut(&x.jump_to) { 1772 | vec.push(ReferenceType::Conditional(pc)); 1773 | } else { 1774 | let vec = vec![ReferenceType::Conditional(pc)]; 1775 | forward_references.insert(x.jump_to, vec); 1776 | } 1777 | wtr.write_u8(opcode)?; 1778 | wtr.write_u16::(0)?; 1779 | wtr.write_u8(InsnParser::NOP)?; 1780 | wtr.write_u8(InsnParser::NOP)?; 1781 | wtr.write_u8(InsnParser::NOP)?; 1782 | wtr.write_u8(InsnParser::NOP)?; 1783 | wtr.write_u8(InsnParser::NOP)?; 1784 | pc = pc.checked_add(8).ok_or_else(ParserError::too_many_instructions)?; 1785 | } 1786 | } 1787 | Insn::IncrementInt(x) => { 1788 | let index = x.index; 1789 | let amount = x.amount; 1790 | // need to check if we can fit the amount into 1 byte 1791 | if let (Ok(index), Ok(amount)) = (u8::try_from(index), i8::try_from(amount)) { 1792 | wtr.write_u8(InsnParser::IINC)?; 1793 | wtr.write_u8(index)?; 1794 | wtr.write_i8(amount)?; 1795 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1796 | } else { 1797 | wtr.write_u8(InsnParser::WIDE)?; 1798 | wtr.write_u8(InsnParser::IINC)?; 1799 | wtr.write_u16::(index)?; 1800 | wtr.write_i16::(amount)?; 1801 | pc = pc.checked_add(6).ok_or_else(ParserError::too_many_instructions)?; 1802 | } 1803 | } 1804 | Insn::InstanceOf(x) => { 1805 | wtr.write_u8(InsnParser::INSTANCEOF)?; 1806 | wtr.write_u16::(constant_pool.class_utf8(x.class.clone()))?; 1807 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1808 | } 1809 | Insn::InvokeDynamic(x) => { 1810 | return Err(ParserError::unimplemented("Invokedynamic writing unimplemented")); 1811 | } 1812 | Insn::Invoke(x) => { 1813 | let opcode = match x.kind { 1814 | InvokeType::Instance => InsnParser::INVOKEVIRTUAL, 1815 | InvokeType::Static => InsnParser::INVOKESTATIC, 1816 | InvokeType::Interface => InsnParser::INVOKEINTERFACE, 1817 | InvokeType::Special => InsnParser::INVOKESPECIAL 1818 | }; 1819 | wtr.write_u8(opcode)?; 1820 | if opcode == InsnParser::INVOKEINTERFACE { 1821 | let class = constant_pool.class_utf8(x.class.clone()); 1822 | let name = constant_pool.utf8(x.name.clone()); 1823 | let desc = constant_pool.utf8(x.descriptor.clone()); 1824 | let nandt = constant_pool.nameandtype(name, desc); 1825 | wtr.write_u16::(constant_pool.interfacemethodref(class, nandt))?; 1826 | // The count operand of an invokeinterface instruction is valid if it is 1827 | // the difference between the size of the operand stack before and after the instruction 1828 | // executes. 1829 | let mut count = 1; // interface methods are virtual so there is always at least one 1830 | let (args, _) = parse_method_desc(&x.descriptor)?; 1831 | for arg in args.iter() { 1832 | count += arg.size(); 1833 | } 1834 | wtr.write_u8(count)?; 1835 | wtr.write_u8(0)?; 1836 | pc = pc.checked_add(5).ok_or_else(ParserError::too_many_instructions)?; 1837 | } else { 1838 | let class = constant_pool.class_utf8(x.class.clone()); 1839 | let name = constant_pool.utf8(x.name.clone()); 1840 | let desc = constant_pool.utf8(x.descriptor.clone()); 1841 | let nandt = constant_pool.nameandtype(name, desc); 1842 | wtr.write_u16::(constant_pool.methodref(class, nandt))?; 1843 | pc = pc.checked_add(3).ok_or_else(ParserError::too_many_instructions)?; 1844 | } 1845 | } 1846 | Insn::LookupSwitch(x) => { 1847 | wtr.write_u8(InsnParser::LOOKUPSWITCH)?; 1848 | let pad = (4 - (pc % 4)) % 4; 1849 | for i in 0..pad { 1850 | wtr.write_u8(0)?; 1851 | } 1852 | 1853 | if let Some(at) = label_pc_map.get(&x.default) { 1854 | let offset: i32 = pc as i32 - (*at) as i32; 1855 | wtr.write_i32::(offset)?; 1856 | } else { 1857 | if let Some(vec) = forward_references.get_mut(&x.default) { 1858 | vec.push(ReferenceType::Direct(pc + 2)); 1859 | } else { 1860 | let vec = vec![ReferenceType::Direct(pc + 2)]; 1861 | forward_references.insert(x.default, vec); 1862 | } 1863 | wtr.write_i32::(0)?; 1864 | } 1865 | 1866 | wtr.write_i32::(x.cases.len() as i32)?; 1867 | 1868 | pc = pc.checked_add(10).ok_or_else(ParserError::too_many_instructions)?; 1869 | 1870 | for (case, to) in x.cases.iter() { 1871 | wtr.write_i32::(*case)?; 1872 | if let Some(at) = label_pc_map.get(to) { 1873 | let offset: i32 = (pc + 4) as i32 - (*at) as i32; 1874 | wtr.write_i32::(offset)?; 1875 | } else { 1876 | if let Some(vec) = forward_references.get_mut(to) { 1877 | vec.push(ReferenceType::Direct(pc + 4)); 1878 | } else { 1879 | let vec = vec![ReferenceType::Direct(pc + 4)]; 1880 | forward_references.insert(*to, vec); 1881 | } 1882 | wtr.write_i32::(0)?; 1883 | } 1884 | pc = pc.checked_add(8).ok_or_else(ParserError::too_many_instructions)?; 1885 | } 1886 | } 1887 | Insn::TableSwitch(x) => { 1888 | wtr.write_u8(InsnParser::TABLESWITCH)?; 1889 | let pad = (4 - (pc % 4)) % 4; 1890 | for i in 0..pad { 1891 | wtr.write_u8(0)?; 1892 | } 1893 | } 1894 | Insn::MonitorEnter(_) => {} 1895 | Insn::MonitorExit(_) => {} 1896 | Insn::MultiNewArray(_) => {} 1897 | Insn::NewObject(_) => {} 1898 | Insn::Nop(_) => {} 1899 | Insn::Swap(_) => {} 1900 | Insn::ImpDep1(_) => {} 1901 | Insn::ImpDep2(_) => {} 1902 | Insn::BreakPoint(_) => {} 1903 | } 1904 | } 1905 | 1906 | Ok((wtr.into_inner(), label_pc_map)) 1907 | } 1908 | 1909 | fn write_ldc(wtr: &mut T, constant: u16, double_size: bool) -> Result { 1910 | // double sized constants must use LDC2 (only wide variant exists) 1911 | if double_size { 1912 | wtr.write_u8(InsnParser::LDC2_W)?; 1913 | wtr.write_u16::(constant)?; 1914 | Ok(5) 1915 | } else { 1916 | // If we can fit the constant index into a u8 then use LDC otherwise use LDC_W 1917 | if constant <= 0xFF { 1918 | wtr.write_u8(InsnParser::LDC)?; 1919 | wtr.write_u8(constant as u8)?; 1920 | Ok(3) 1921 | } else { 1922 | wtr.write_u8(InsnParser::LDC_W)?; 1923 | wtr.write_u16::(constant)?; 1924 | Ok(5) 1925 | } 1926 | } 1927 | } 1928 | } 1929 | -------------------------------------------------------------------------------- /src/constantpool.rs: -------------------------------------------------------------------------------- 1 | use crate::Serializable; 2 | use crate::utils::ReadUtils; 3 | use crate::error::{Result, ParserError}; 4 | use std::io::{Read, Write}; 5 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 6 | use std::borrow::{Cow}; 7 | use derive_more::Constructor; 8 | use enum_display_derive::DisplayDebug; 9 | use std::fmt::{Debug, Formatter}; 10 | use linked_hash_map::LinkedHashMap; 11 | use std::hash::{Hash}; 12 | 13 | pub type CPIndex = u16; 14 | 15 | #[derive(Clone, PartialEq)] 16 | pub struct ConstantPool { 17 | inner: Vec> 18 | } 19 | 20 | impl Debug for ConstantPool { 21 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 22 | let mut list = f.debug_list(); 23 | for x in self.inner.iter() { 24 | match x { 25 | Some(x) => list.entry(x), 26 | None => list.entry(x) 27 | }; 28 | } 29 | list.finish() 30 | } 31 | } 32 | 33 | impl Default for ConstantPool { 34 | fn default() -> Self { 35 | ConstantPool { 36 | inner: Vec::with_capacity(12) 37 | } 38 | } 39 | } 40 | 41 | #[allow(dead_code)] 42 | impl ConstantPool { 43 | pub fn new() -> Self { 44 | ConstantPool::default() 45 | } 46 | 47 | pub fn get(&self, index: CPIndex) -> Result<&ConstantType> { 48 | match self.inner.get(index as usize) { 49 | Some(Some(x)) => { 50 | Ok(x) 51 | } 52 | _ => Err(ParserError::bad_cp_index(index)) 53 | } 54 | } 55 | 56 | pub fn set(&mut self, index: CPIndex, value: Option) { 57 | let index = index as usize; 58 | if index > self.inner.len() - 1 { 59 | self.inner.resize(index + 1, None); 60 | } 61 | self.inner[index] = value 62 | } 63 | 64 | pub fn class(&self, index: CPIndex) -> Result<&ClassInfo> { 65 | match self.get(index)? { 66 | ConstantType::Class(t) => Ok(t), 67 | x => Err(ParserError::incomp_cp( 68 | "Class", 69 | x, 70 | index as usize 71 | )), 72 | } 73 | } 74 | 75 | pub fn fieldref(&self, index: CPIndex) -> Result<&FieldRefInfo> { 76 | match self.get(index)? { 77 | ConstantType::Fieldref(t) => Ok(t), 78 | x => Err(ParserError::incomp_cp( 79 | "FieldRef", 80 | x, 81 | index as usize 82 | )), 83 | } 84 | } 85 | 86 | pub fn any_method(&self, index: CPIndex) -> Result<(&MethodRefInfo, bool)> { 87 | match self.get(index)? { 88 | ConstantType::Methodref(method) => Ok((method, false)), 89 | ConstantType::InterfaceMethodref(method) => Ok((method, true)), 90 | x => Err(ParserError::incomp_cp( 91 | "AnyMethodRef", 92 | x, 93 | index as usize 94 | )), 95 | } 96 | } 97 | 98 | pub fn methodref(&self, index: CPIndex) -> Result<&MethodRefInfo> { 99 | match self.get(index)? { 100 | ConstantType::Methodref(t) => Ok(t), 101 | x => Err(ParserError::incomp_cp( 102 | "MethodRef", 103 | x, 104 | index as usize 105 | )), 106 | } 107 | } 108 | 109 | pub fn interfacemethodref(&self, index: CPIndex) -> Result<&MethodRefInfo> { 110 | match self.get(index)? { 111 | ConstantType::InterfaceMethodref(t) => Ok(t), 112 | x => Err(ParserError::incomp_cp( 113 | "InterfaceMethodRef", 114 | x, 115 | index as usize 116 | )), 117 | } 118 | } 119 | 120 | pub fn string(&self, index: CPIndex) -> Result<&StringInfo> { 121 | match self.get(index)? { 122 | ConstantType::String(t) => Ok(t), 123 | x => Err(ParserError::incomp_cp( 124 | "String", 125 | x, 126 | index as usize 127 | )), 128 | } 129 | } 130 | 131 | pub fn integer(&self, index: CPIndex) -> Result<&IntegerInfo> { 132 | match self.get(index)? { 133 | ConstantType::Integer(t) => Ok(t), 134 | x => Err(ParserError::incomp_cp( 135 | "Integer", 136 | x, 137 | index as usize 138 | )), 139 | } 140 | } 141 | 142 | pub fn float(&self, index: CPIndex) -> Result<&FloatInfo> { 143 | match self.get(index)? { 144 | ConstantType::Float(t) => Ok(t), 145 | x => Err(ParserError::incomp_cp( 146 | "Float", 147 | x, 148 | index as usize 149 | )), 150 | } 151 | } 152 | 153 | pub fn long(&self, index: CPIndex) -> Result<&LongInfo> { 154 | match self.get(index)? { 155 | ConstantType::Long(t) => Ok(t), 156 | x => Err(ParserError::incomp_cp( 157 | "Long", 158 | x, 159 | index as usize 160 | )), 161 | } 162 | } 163 | 164 | pub fn double(&self, index: CPIndex) -> Result<&DoubleInfo> { 165 | match self.get(index)? { 166 | ConstantType::Double(t) => Ok(t), 167 | x => Err(ParserError::incomp_cp( 168 | "Double", 169 | x, 170 | index as usize 171 | )), 172 | } 173 | } 174 | 175 | pub fn nameandtype(&self, index: CPIndex) -> Result<&NameAndTypeInfo> { 176 | match self.get(index)? { 177 | ConstantType::NameAndType(t) => Ok(t), 178 | x => Err(ParserError::incomp_cp( 179 | "NameAndType", 180 | x, 181 | index as usize 182 | )), 183 | } 184 | } 185 | 186 | pub fn utf8(&self, index: CPIndex) -> Result<&Utf8Info> { 187 | match self.get(index)? { 188 | ConstantType::Utf8(t) => Ok(t), 189 | x => Err(ParserError::incomp_cp( 190 | "Utf8", 191 | x, 192 | index as usize 193 | )), 194 | } 195 | } 196 | 197 | pub fn utf8_inner(&self, index: CPIndex) -> Result { 198 | let utf8_info = self.utf8(index)?; 199 | Ok(utf8_info.str.clone()) 200 | } 201 | 202 | pub fn methodhandle(&self, index: CPIndex) -> Result<&MethodHandleInfo> { 203 | match self.get(index)? { 204 | ConstantType::MethodHandle(t) => Ok(t), 205 | x => Err(ParserError::incomp_cp( 206 | "MethodHandle", 207 | x, 208 | index as usize 209 | )), 210 | } 211 | } 212 | 213 | pub fn methodtype(&self, index: CPIndex) -> Result<&MethodTypeInfo> { 214 | match self.get(index)? { 215 | ConstantType::MethodType(t) => Ok(t), 216 | x => Err(ParserError::incomp_cp( 217 | "MethodType", 218 | x, 219 | index as usize 220 | )), 221 | } 222 | } 223 | 224 | pub fn dynamicinfo(&self, index: CPIndex) -> Result<&DynamicInfo> { 225 | match self.get(index)? { 226 | ConstantType::Dynamic(t) => Ok(t), 227 | x => Err(ParserError::incomp_cp( 228 | "Dynamic", 229 | x, 230 | index as usize 231 | )), 232 | } 233 | } 234 | 235 | pub fn invokedynamicinfo(&self, index: CPIndex) -> Result<&InvokeDynamicInfo> { 236 | match self.get(index)? { 237 | ConstantType::InvokeDynamic(t) => Ok(t), 238 | x => Err(ParserError::incomp_cp( 239 | "InvokeDynamic", 240 | x, 241 | index as usize 242 | )), 243 | } 244 | } 245 | 246 | pub fn module(&self, index: CPIndex) -> Result<&ModuleInfo> { 247 | match self.get(index)? { 248 | ConstantType::Module(t) => Ok(t), 249 | x => Err(ParserError::incomp_cp( 250 | "Module", 251 | x, 252 | index as usize 253 | )), 254 | } 255 | } 256 | 257 | pub fn package(&self, index: CPIndex) -> Result<&PackageInfo> { 258 | match self.get(index)? { 259 | ConstantType::Package(t) => Ok(t), 260 | x => Err(ParserError::incomp_cp( 261 | "Package", 262 | x, 263 | index as usize 264 | )), 265 | } 266 | } 267 | } 268 | 269 | impl Serializable for ConstantPool { 270 | fn parse(rdr: &mut R) -> Result { 271 | let size = rdr.read_u16::()? as usize; 272 | let mut cp = ConstantPool { 273 | inner: vec![None; size] 274 | }; 275 | let mut skip = false; 276 | for i in 1..size { 277 | if skip { 278 | skip = false; 279 | continue 280 | } 281 | let constant = ConstantType::parse(rdr)?; 282 | if constant.double_size() { 283 | skip = true; 284 | } 285 | cp.set(i as CPIndex, Some(constant)); 286 | } 287 | 288 | Ok(cp) 289 | } 290 | 291 | fn write(&self, wtr: &mut W) -> Result<()> { 292 | wtr.write_u16::(self.inner.len() as u16)?; 293 | Ok(()) 294 | } 295 | } 296 | 297 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 298 | pub struct ClassInfo { 299 | pub name_index: CPIndex 300 | } 301 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 302 | pub struct FieldRefInfo { 303 | pub class_index: CPIndex, 304 | pub name_and_type_index: CPIndex 305 | } 306 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 307 | pub struct MethodRefInfo { 308 | pub class_index: CPIndex, 309 | pub name_and_type_index: CPIndex 310 | } 311 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 312 | pub struct StringInfo { 313 | pub utf_index: CPIndex 314 | } 315 | 316 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 317 | pub struct IntegerInfo { 318 | inner: i32 319 | } 320 | impl IntegerInfo { 321 | pub fn inner(&self) -> i32 { 322 | self.inner 323 | } 324 | } 325 | 326 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 327 | pub struct LongInfo { 328 | inner: i64 329 | } 330 | impl LongInfo { 331 | pub fn inner(&self) -> i64 { 332 | self.inner 333 | } 334 | } 335 | 336 | /// Rust floats do not support Eq and Hash 337 | /// This is because its just too hard to correctly compare floats 338 | /// For our purpose however we dont care too much about equality and more about not (?) equality 339 | /// In the end if two of the same floats are not compared equal to each other then we just make 340 | /// two constant pool entries and who cares 341 | /// Because of this we will store the float as an integer and let rust do integer comparisons on it 342 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 343 | pub struct FloatInfo { 344 | inner: u32 345 | } 346 | impl FloatInfo { 347 | pub fn new(inner: f32) -> Self { 348 | FloatInfo { 349 | inner: inner.to_bits() 350 | } 351 | } 352 | pub fn inner(&self) -> f32 { 353 | f32::from_bits(self.inner) 354 | } 355 | } 356 | 357 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 358 | pub struct DoubleInfo { 359 | inner: u64 360 | } 361 | impl DoubleInfo { 362 | pub fn new(inner: f64) -> Self { 363 | DoubleInfo { 364 | inner: inner.to_bits() 365 | } 366 | } 367 | pub fn inner(&self) -> f64 { 368 | f64::from_bits(self.inner) 369 | } 370 | } 371 | 372 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 373 | pub struct NameAndTypeInfo { 374 | pub name_index: CPIndex, 375 | pub descriptor_index: CPIndex 376 | } 377 | #[derive(Constructor, Clone, Debug, PartialEq, Eq, Hash)] 378 | pub struct Utf8Info { 379 | pub str: String 380 | } 381 | 382 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 383 | pub struct MethodHandleInfo { 384 | pub kind: MethodHandleKind, 385 | pub reference: CPIndex 386 | } 387 | 388 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 389 | pub enum MethodHandleKind { 390 | GetField, 391 | GetStatic, 392 | PutField, 393 | PutStatic, 394 | InvokeVirtual, 395 | InvokeStatic, 396 | InvokeSpecial, 397 | NewInvokeSpecial, 398 | InvokeInterface 399 | } 400 | 401 | #[allow(non_upper_case_globals)] 402 | impl MethodHandleKind { 403 | const REF_getField: u8 = 1; 404 | const REF_getStatic: u8 = 2; 405 | const REF_putField: u8 = 3; 406 | const REF_putStatic: u8 = 4; 407 | const REF_invokeVirtual: u8 = 5; 408 | const REF_invokeStatic: u8 = 6; 409 | const REF_invokeSpecial: u8 = 7; 410 | const REF_newInvokeSpecial: u8 = 8; 411 | const REF_invokeInterface: u8 = 9; 412 | } 413 | 414 | 415 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 416 | pub struct MethodTypeInfo { 417 | pub descriptor_index: CPIndex 418 | } 419 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 420 | pub struct DynamicInfo { 421 | pub bootstrap_method_attr_index: CPIndex, 422 | pub name_and_type_index: CPIndex 423 | } 424 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 425 | pub struct InvokeDynamicInfo { 426 | pub bootstrap_method_attr_index: CPIndex, 427 | pub name_and_type_index: CPIndex 428 | } 429 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 430 | pub struct ModuleInfo { 431 | pub name_index: CPIndex 432 | } 433 | #[derive(Constructor, Copy, Clone, Debug, PartialEq, Eq, Hash)] 434 | pub struct PackageInfo { 435 | pub name_index: CPIndex 436 | } 437 | 438 | #[derive(Clone, PartialEq, Eq, Hash, DisplayDebug)] 439 | pub enum ConstantType { 440 | Class (ClassInfo), 441 | Fieldref (FieldRefInfo), 442 | Methodref (MethodRefInfo), 443 | InterfaceMethodref (MethodRefInfo), 444 | String (StringInfo), 445 | Integer (IntegerInfo), 446 | Float (FloatInfo), 447 | Long (LongInfo), 448 | Double (DoubleInfo), 449 | NameAndType (NameAndTypeInfo), 450 | Utf8 (Utf8Info), 451 | MethodHandle (MethodHandleInfo), 452 | MethodType (MethodTypeInfo), 453 | Dynamic (DynamicInfo), 454 | InvokeDynamic (InvokeDynamicInfo), 455 | Module (ModuleInfo), 456 | Package (PackageInfo) 457 | } 458 | 459 | #[allow(non_upper_case_globals)] 460 | impl ConstantType { 461 | const CONSTANT_Utf8: u8 = 1; 462 | const CONSTANT_Integer: u8 = 3; 463 | const CONSTANT_Float: u8 = 4; 464 | const CONSTANT_Long: u8 = 5; 465 | const CONSTANT_Double: u8 = 6; 466 | const CONSTANT_Class: u8 = 7; 467 | const CONSTANT_String: u8 = 8; 468 | const CONSTANT_Fieldref: u8 = 9; 469 | const CONSTANT_Methodref: u8 = 10; 470 | const CONSTANT_InterfaceMethodref: u8 = 11; 471 | const CONSTANT_NameAndType: u8 = 12; 472 | const CONSTANT_MethodHandle: u8 = 15; 473 | const CONSTANT_MethodType: u8 = 16; 474 | const CONSTANT_Dynamic: u8 = 17; 475 | const CONSTANT_InvokeDynamic: u8 = 18; 476 | const CONSTANT_Module: u8 = 19; 477 | const CONSTANT_Package: u8 = 20; 478 | 479 | pub fn parse(rdr: &mut R) -> Result { 480 | let tag = rdr.read_u8()?; 481 | Ok(match tag { 482 | ConstantType::CONSTANT_Class => ConstantType::Class ( 483 | ClassInfo { 484 | name_index: rdr.read_u16::()? 485 | }, 486 | ), 487 | ConstantType::CONSTANT_Fieldref => ConstantType::Fieldref ( 488 | FieldRefInfo { 489 | class_index: rdr.read_u16::()?, 490 | name_and_type_index: rdr.read_u16::()? 491 | }, 492 | ), 493 | ConstantType::CONSTANT_Methodref => ConstantType::Methodref ( 494 | MethodRefInfo { 495 | class_index: rdr.read_u16::()?, 496 | name_and_type_index: rdr.read_u16::()? 497 | }, 498 | ), 499 | ConstantType::CONSTANT_InterfaceMethodref => ConstantType::InterfaceMethodref ( 500 | MethodRefInfo { 501 | class_index: rdr.read_u16::()?, 502 | name_and_type_index: rdr.read_u16::()? 503 | }, 504 | ), 505 | ConstantType::CONSTANT_String => ConstantType::String ( 506 | StringInfo { 507 | utf_index: rdr.read_u16::()? 508 | }, 509 | ), 510 | ConstantType::CONSTANT_Integer => ConstantType::Integer ( 511 | IntegerInfo::new(rdr.read_i32::()?), 512 | ), 513 | ConstantType::CONSTANT_Float => ConstantType::Float ( 514 | FloatInfo::new(rdr.read_f32::()?), 515 | ), 516 | ConstantType::CONSTANT_Long => ConstantType::Long ( 517 | LongInfo::new(rdr.read_i64::()?), 518 | ), 519 | ConstantType::CONSTANT_Double => ConstantType::Double ( 520 | DoubleInfo::new(rdr.read_f64::()?), 521 | ), 522 | ConstantType::CONSTANT_NameAndType => ConstantType::NameAndType ( 523 | NameAndTypeInfo { 524 | name_index: rdr.read_u16::()?, 525 | descriptor_index: rdr.read_u16::()? 526 | }, 527 | ), 528 | ConstantType::CONSTANT_Utf8 => { 529 | let length = rdr.read_u16::()? as usize; 530 | let bytes = rdr.read_nbytes(length)?; 531 | let utf = match mutf8::mutf8_to_utf8(bytes.as_slice()) { 532 | Cow::Borrowed(_data) => bytes.into(), 533 | Cow::Owned(data) => data.into_boxed_slice(), 534 | }; 535 | 536 | let str = String::from_utf8_lossy(&utf); 537 | let str = String::from(&*str); 538 | ConstantType::Utf8 ( Utf8Info { str } ) 539 | }, 540 | ConstantType::CONSTANT_MethodHandle => { 541 | let kind = match rdr.read_u8()? { 542 | MethodHandleKind::REF_getField => MethodHandleKind::GetField, 543 | MethodHandleKind::REF_getStatic => MethodHandleKind::GetStatic, 544 | MethodHandleKind::REF_putField => MethodHandleKind::PutField, 545 | MethodHandleKind::REF_putStatic => MethodHandleKind::PutStatic, 546 | MethodHandleKind::REF_invokeVirtual => MethodHandleKind::InvokeVirtual, 547 | MethodHandleKind::REF_invokeStatic => MethodHandleKind::InvokeStatic, 548 | MethodHandleKind::REF_invokeSpecial => MethodHandleKind::InvokeSpecial, 549 | MethodHandleKind::REF_newInvokeSpecial => MethodHandleKind::NewInvokeSpecial, 550 | MethodHandleKind::REF_invokeInterface => MethodHandleKind::InvokeInterface, 551 | x => return Err(ParserError::other(format!("Unknown method handle type {}", x))) 552 | }; 553 | let reference = rdr.read_u16::()?; 554 | ConstantType::MethodHandle(MethodHandleInfo::new(kind, reference)) 555 | }, 556 | ConstantType::CONSTANT_MethodType => ConstantType::MethodType ( 557 | MethodTypeInfo { 558 | descriptor_index: rdr.read_u16::()? 559 | }, 560 | ), 561 | ConstantType::CONSTANT_Dynamic => ConstantType::Dynamic ( 562 | DynamicInfo { 563 | bootstrap_method_attr_index: rdr.read_u16::()?, 564 | name_and_type_index: rdr.read_u16::()? 565 | }, 566 | ), 567 | ConstantType::CONSTANT_InvokeDynamic => ConstantType::InvokeDynamic ( 568 | InvokeDynamicInfo { 569 | bootstrap_method_attr_index: rdr.read_u16::()?, 570 | name_and_type_index: rdr.read_u16::()? 571 | }, 572 | ), 573 | ConstantType::CONSTANT_Module => ConstantType::Module ( 574 | ModuleInfo { 575 | name_index: rdr.read_u16::()? 576 | }, 577 | ), 578 | ConstantType::CONSTANT_Package => ConstantType::Package ( 579 | PackageInfo { 580 | name_index: rdr.read_u16::()? 581 | }, 582 | ), 583 | _ => return Err(ParserError::unrecognised("constant tag", tag.to_string())) 584 | }) 585 | } 586 | 587 | pub fn write(&self, wtr: &mut W) -> Result<()> { 588 | match self { 589 | ConstantType::Class(x) => { 590 | wtr.write_u8(ConstantType::CONSTANT_Class)?; 591 | wtr.write_u16::(x.name_index)?; 592 | }, 593 | ConstantType::Fieldref(x) => { 594 | wtr.write_u8(ConstantType::CONSTANT_Fieldref)?; 595 | wtr.write_u16::(x.class_index)?; 596 | wtr.write_u16::(x.name_and_type_index)?; 597 | } 598 | ConstantType::Methodref(x) => { 599 | wtr.write_u8(ConstantType::CONSTANT_Methodref)?; 600 | wtr.write_u16::(x.class_index)?; 601 | wtr.write_u16::(x.name_and_type_index)?; 602 | } 603 | ConstantType::InterfaceMethodref(x) => { 604 | wtr.write_u8(ConstantType::CONSTANT_InterfaceMethodref)?; 605 | wtr.write_u16::(x.class_index)?; 606 | wtr.write_u16::(x.name_and_type_index)?; 607 | } 608 | ConstantType::String(x) => { 609 | wtr.write_u8(ConstantType::CONSTANT_String)?; 610 | wtr.write_u16::(x.utf_index)?; 611 | } 612 | ConstantType::Integer(x) => { 613 | wtr.write_u8(ConstantType::CONSTANT_Integer)?; 614 | wtr.write_i32::(x.inner())?; 615 | } 616 | ConstantType::Float(x) => { 617 | wtr.write_u8(ConstantType::CONSTANT_Float)?; 618 | wtr.write_f32::(x.inner())?; 619 | } 620 | ConstantType::Long(x) => { 621 | wtr.write_u8(ConstantType::CONSTANT_Long)?; 622 | wtr.write_i64::(x.inner())?; 623 | } 624 | ConstantType::Double(x) => { 625 | wtr.write_u8(ConstantType::CONSTANT_Double)?; 626 | wtr.write_f64::(x.inner())?; 627 | } 628 | ConstantType::NameAndType(x) => { 629 | wtr.write_u8(ConstantType::CONSTANT_NameAndType)?; 630 | wtr.write_u16::(x.name_index)?; 631 | wtr.write_u16::(x.descriptor_index)?; 632 | } 633 | ConstantType::Utf8(x) => { 634 | wtr.write_u8(ConstantType::CONSTANT_Utf8)?; 635 | let bytes = x.str.as_bytes(); 636 | let mutf = match mutf8::utf8_to_mutf8(bytes) { 637 | Cow::Borrowed(_data) => bytes.into(), 638 | Cow::Owned(data) => data.into_boxed_slice(), 639 | }; 640 | wtr.write_u16::(mutf.len() as u16)?; 641 | wtr.write_all(&*mutf)?; 642 | } 643 | ConstantType::MethodHandle(x) => { 644 | wtr.write_u8(ConstantType::CONSTANT_MethodHandle)?; 645 | 646 | let reference_kind = match x.kind { 647 | MethodHandleKind::GetField => MethodHandleKind::REF_getField, 648 | MethodHandleKind::GetStatic => MethodHandleKind::REF_getStatic, 649 | MethodHandleKind::PutField => MethodHandleKind::REF_putField, 650 | MethodHandleKind::PutStatic => MethodHandleKind::REF_putStatic, 651 | MethodHandleKind::InvokeVirtual => MethodHandleKind::REF_invokeVirtual, 652 | MethodHandleKind::InvokeStatic => MethodHandleKind::REF_invokeStatic, 653 | MethodHandleKind::InvokeSpecial => MethodHandleKind::REF_invokeSpecial, 654 | MethodHandleKind::NewInvokeSpecial => MethodHandleKind::REF_newInvokeSpecial, 655 | MethodHandleKind::InvokeInterface => MethodHandleKind::REF_invokeInterface, 656 | }; 657 | 658 | wtr.write_u8(reference_kind)?; 659 | wtr.write_u16::(x.reference)?; 660 | } 661 | ConstantType::MethodType(x) => { 662 | wtr.write_u8(ConstantType::CONSTANT_MethodType)?; 663 | wtr.write_u16::(x.descriptor_index)?; 664 | }, 665 | ConstantType::Dynamic(x) => { 666 | wtr.write_u8(ConstantType::CONSTANT_Dynamic)?; 667 | wtr.write_u16::(x.bootstrap_method_attr_index)?; 668 | wtr.write_u16::(x.name_and_type_index)?; 669 | }, 670 | ConstantType::InvokeDynamic(x) => { 671 | wtr.write_u8(ConstantType::CONSTANT_InvokeDynamic)?; 672 | wtr.write_u16::(x.bootstrap_method_attr_index)?; 673 | wtr.write_u16::(x.name_and_type_index)?; 674 | }, 675 | ConstantType::Module(x) => { 676 | wtr.write_u8(ConstantType::CONSTANT_Module)?; 677 | wtr.write_u16::(x.name_index)?; 678 | }, 679 | ConstantType::Package(x) => { 680 | wtr.write_u8(ConstantType::CONSTANT_Package)?; 681 | wtr.write_u16::(x.name_index)?; 682 | }, 683 | } 684 | Ok(()) 685 | } 686 | 687 | pub fn double_size(&self) -> bool { 688 | matches!(self, ConstantType::Double(..) | ConstantType::Long(..)) 689 | } 690 | } 691 | 692 | pub struct ConstantPoolWriter { 693 | inner: LinkedHashMap, 694 | index: CPIndex 695 | } 696 | 697 | impl Default for ConstantPoolWriter { 698 | fn default() -> Self { 699 | ConstantPoolWriter { 700 | inner: LinkedHashMap::with_capacity(5), 701 | index: 1 702 | } 703 | } 704 | } 705 | 706 | impl ConstantPoolWriter { 707 | pub fn new() -> Self { 708 | ConstantPoolWriter::default() 709 | } 710 | 711 | pub fn put(&mut self, constant: ConstantType) -> CPIndex { 712 | match self.inner.get(&constant) { 713 | Some(x) => *x, 714 | None => { 715 | let this_index = self.index; 716 | self.index += if constant.double_size() { 2 } else { 1 }; 717 | self.inner.insert(constant, this_index); 718 | this_index 719 | } 720 | } 721 | } 722 | 723 | pub fn len(&self) -> u16 { 724 | self.index 725 | } 726 | 727 | pub fn is_empty(&self) -> bool { 728 | self.index == 0 729 | } 730 | 731 | pub fn class(&mut self, name_index: CPIndex) -> CPIndex { 732 | self.put(ConstantType::Class(ClassInfo::new(name_index))) 733 | } 734 | 735 | pub fn class_utf8>(&mut self, str: T) -> CPIndex { 736 | let utf = self.utf8(str); 737 | self.class(utf) 738 | } 739 | 740 | pub fn fieldref(&mut self, class_index: CPIndex, name_and_type_index: CPIndex) -> CPIndex { 741 | self.put(ConstantType::Fieldref(FieldRefInfo::new(class_index, name_and_type_index))) 742 | } 743 | 744 | pub fn methodref(&mut self, class_index: CPIndex, name_and_type_index: CPIndex) -> CPIndex { 745 | self.put(ConstantType::Methodref(MethodRefInfo::new(class_index, name_and_type_index))) 746 | } 747 | 748 | pub fn interfacemethodref(&mut self, class_index: CPIndex, name_and_type_index: CPIndex) -> CPIndex { 749 | self.put(ConstantType::InterfaceMethodref(MethodRefInfo::new(class_index, name_and_type_index))) 750 | } 751 | 752 | pub fn string(&mut self, string_index: CPIndex) -> CPIndex { 753 | self.put(ConstantType::String(StringInfo::new(string_index))) 754 | } 755 | 756 | pub fn string_utf>(&mut self, str: T) -> CPIndex { 757 | let utf = self.utf8(str); 758 | self.string(utf) 759 | } 760 | 761 | pub fn integer(&mut self, bytes: i32) -> CPIndex { 762 | self.put(ConstantType::Integer(IntegerInfo::new(bytes))) 763 | } 764 | 765 | pub fn float(&mut self, bytes: f32) -> CPIndex { 766 | self.put(ConstantType::Float(FloatInfo::new(bytes))) 767 | } 768 | 769 | pub fn long(&mut self, bytes: i64) -> CPIndex { 770 | self.put(ConstantType::Long(LongInfo::new(bytes))) 771 | } 772 | 773 | pub fn double(&mut self, bytes: f64) -> CPIndex { 774 | self.put(ConstantType::Double(DoubleInfo::new(bytes))) 775 | } 776 | 777 | pub fn nameandtype(&mut self, name_index: CPIndex, descriptor_index: CPIndex) -> CPIndex { 778 | self.put(ConstantType::NameAndType(NameAndTypeInfo::new(name_index, descriptor_index))) 779 | } 780 | 781 | pub fn utf8>(&mut self, str: T) -> CPIndex { 782 | self.put(ConstantType::Utf8(Utf8Info::new(str.into()))) 783 | } 784 | 785 | pub fn methodhandle(&mut self, kind: MethodHandleKind, reference: CPIndex) -> CPIndex { 786 | self.put(ConstantType::MethodHandle(MethodHandleInfo::new(kind, reference))) 787 | } 788 | 789 | pub fn methodtype(&mut self, descriptor_index: CPIndex) -> CPIndex { 790 | self.put(ConstantType::MethodType(MethodTypeInfo::new(descriptor_index))) 791 | } 792 | 793 | pub fn methodtype_utf8>(&mut self, str: T) -> CPIndex { 794 | let utf = self.utf8(str); 795 | self.methodtype(utf) 796 | } 797 | 798 | pub fn dynamicinfo(&mut self, bootstrap_method_attr_index: CPIndex, name_and_type_index: CPIndex) -> CPIndex { 799 | self.put(ConstantType::Dynamic(DynamicInfo::new(bootstrap_method_attr_index, name_and_type_index))) 800 | } 801 | 802 | pub fn invokedynamicinfo(&mut self, bootstrap_method_attr_index: CPIndex, name_and_type_index: CPIndex) -> CPIndex { 803 | self.put(ConstantType::InvokeDynamic(InvokeDynamicInfo::new(bootstrap_method_attr_index, name_and_type_index))) 804 | } 805 | 806 | pub fn module(&mut self, name_index: CPIndex) -> CPIndex { 807 | self.put(ConstantType::Module(ModuleInfo::new(name_index))) 808 | } 809 | 810 | pub fn package(&mut self, name_index: CPIndex) -> CPIndex { 811 | self.put(ConstantType::Package(PackageInfo::new(name_index))) 812 | } 813 | 814 | pub fn write(&mut self, wtr: &mut W) -> Result<()> { 815 | wtr.write_u16::(self.index as u16)?; 816 | for (constant, _index) in self.inner.iter() { 817 | constant.write(wtr)?; 818 | } 819 | 820 | Ok(()) 821 | } 822 | } 823 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | use std::{io, result}; 3 | use std::fmt::{Debug}; 4 | use crate::constantpool::ConstantType; 5 | use std::str::Utf8Error; 6 | use std::string::FromUtf8Error; 7 | 8 | #[derive(Error, Debug)] 9 | pub enum ParserError { 10 | #[error("Error reading/writing")] 11 | IO(io::Error), 12 | #[error("Incompatible Constant Entry (expected {expected:#?} at {index:?})")] 13 | IncompatibleCPEntry { 14 | expected: &'static str, 15 | found: ConstantType, 16 | index: usize 17 | }, 18 | #[error("Unrecognized {0}: {1}")] 19 | Unrecognized(&'static str, String), 20 | #[error("Invalid constant pool index: {0}")] 21 | BadCpIndex(u16), 22 | #[error("{0} was none!")] 23 | None(&'static str), 24 | #[error("Unknown Instruction {opcode:X}")] 25 | UnknownInstruction { 26 | opcode: u8 27 | }, 28 | #[error("Invalid Instruction {pc} {msg}")] 29 | InvalidInstruction { 30 | pc: u32, 31 | msg: String 32 | }, 33 | #[error("Unimplemented {0}")] 34 | Unimplemented(&'static str), 35 | #[error("Out of bounds jump index {0}")] 36 | OutOfBoundsJumpIndex(i32), 37 | #[error("Invalid Utf8 {0}")] 38 | InvalidUtf8(Utf8Error), 39 | #[error("Too many instructions in method")] 40 | TooManyInstructions(), 41 | #[error("Invalid Descriptor: {0}")] 42 | InvalidDescriptor(String), 43 | #[error("{0}")] 44 | Other(String) 45 | } 46 | 47 | impl ParserError { 48 | fn check_panic(self) -> Self { 49 | if let Ok(x) = std::env::var("PANIC_ON_ERR") { 50 | if x == "1" || x == "true" { 51 | panic!("{:#x?}", self) 52 | } 53 | } 54 | self 55 | } 56 | 57 | pub fn io(inner: io::Error) -> Self { 58 | ParserError::IO(inner).check_panic() 59 | } 60 | 61 | pub fn incomp_cp(expected: &'static str, found: &ConstantType, index: usize) -> Self { 62 | ParserError::IncompatibleCPEntry { 63 | expected, 64 | found: found.clone(), 65 | index 66 | }.check_panic() 67 | } 68 | 69 | pub fn unrecognised(first: &'static str, second: String) -> Self { 70 | ParserError::Unrecognized(first, second).check_panic() 71 | } 72 | 73 | pub fn bad_cp_index(index: T) -> Self 74 | where T: Into { 75 | ParserError::BadCpIndex(index.into()).check_panic() 76 | } 77 | 78 | pub fn none(name: &'static str) -> Self { 79 | ParserError::None(name).check_panic() 80 | } 81 | 82 | pub fn unknown_insn(opcode: u8) -> Self { 83 | ParserError::UnknownInstruction { opcode }.check_panic() 84 | } 85 | 86 | pub fn invalid_insn(pc: u32, msg: T) -> Self 87 | where T: Into { 88 | ParserError::InvalidInstruction { 89 | pc, 90 | msg: msg.into() 91 | } 92 | } 93 | 94 | pub fn unimplemented(name: &'static str) -> Self { 95 | ParserError::Unimplemented(name).check_panic() 96 | } 97 | 98 | pub fn out_of_bounds_jump(index: i32) -> Self { 99 | ParserError::OutOfBoundsJumpIndex(index).check_panic() 100 | } 101 | 102 | pub fn invalid_utf8(err: Utf8Error) -> Self { 103 | ParserError::InvalidUtf8(err).check_panic() 104 | } 105 | 106 | pub fn too_many_instructions() -> Self { 107 | ParserError::TooManyInstructions().check_panic() 108 | } 109 | 110 | pub fn invalid_descriptor>(msg: T) -> Self { 111 | ParserError::InvalidDescriptor(msg.into()).check_panic() 112 | } 113 | 114 | #[inline] 115 | pub fn other(name: T) -> Self 116 | where T: Into { 117 | ParserError::Other(name.into()).check_panic() 118 | } 119 | 120 | 121 | 122 | 123 | pub fn unmapped_label() -> Self { 124 | ParserError::other("No mapping found for label") 125 | } 126 | } 127 | 128 | impl From for ParserError { 129 | fn from(inner: io::Error) -> Self { 130 | ParserError::io(inner) 131 | } 132 | } 133 | 134 | impl From for ParserError { 135 | fn from(err: FromUtf8Error) -> Self { 136 | ParserError::invalid_utf8(err.utf8_error()) 137 | } 138 | } 139 | 140 | pub type Result = result::Result; 141 | -------------------------------------------------------------------------------- /src/field.rs: -------------------------------------------------------------------------------- 1 | use crate::Serializable; 2 | use crate::access::FieldAccessFlags; 3 | use crate::constantpool::{ConstantPool, ConstantPoolWriter}; 4 | use crate::attributes::{Attributes, Attribute, AttributeSource, SignatureAttribute}; 5 | use crate::version::ClassVersion; 6 | use crate::error::Result; 7 | use crate::utils::{VecUtils}; 8 | use std::io::{Read, Write}; 9 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 10 | 11 | #[allow(non_snake_case)] 12 | pub mod Fields { 13 | use std::io::{Read, Write}; 14 | use crate::field::Field; 15 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 16 | use crate::version::ClassVersion; 17 | use crate::constantpool::{ConstantPool, ConstantPoolWriter}; 18 | 19 | pub fn parse(rdr: &mut T, version: &ClassVersion, constant_pool: &ConstantPool) -> crate::Result> { 20 | let num_fields = rdr.read_u16::()? as usize; 21 | let mut fields: Vec = Vec::with_capacity(num_fields); 22 | for _ in 0..num_fields { 23 | fields.push(Field::parse(rdr, version, constant_pool)?); 24 | } 25 | Ok(fields) 26 | } 27 | 28 | pub fn write(wtr: &mut T, fields: &[Field], constant_pool: &mut ConstantPoolWriter) -> crate::Result<()> { 29 | wtr.write_u16::(fields.len() as u16)?; 30 | for field in fields.iter() { 31 | field.write(wtr, constant_pool)?; 32 | } 33 | Ok(()) 34 | } 35 | } 36 | 37 | #[derive(Clone, Debug, PartialEq)] 38 | pub struct Field { 39 | pub access_flags: FieldAccessFlags, 40 | pub name: String, 41 | pub descriptor: String, 42 | pub attributes: Vec 43 | } 44 | 45 | impl Field { 46 | pub fn parse(rdr: &mut R, version: &ClassVersion, constant_pool: &ConstantPool) -> Result { 47 | let access_flags = FieldAccessFlags::parse(rdr)?; 48 | let name = constant_pool.utf8(rdr.read_u16::()?)?.str.clone(); 49 | let descriptor = constant_pool.utf8(rdr.read_u16::()?)?.str.clone(); 50 | let attributes = Attributes::parse(rdr, AttributeSource::Field, version, constant_pool, &mut None)?; 51 | 52 | Ok(Field { 53 | access_flags, 54 | name, 55 | descriptor, 56 | attributes 57 | }) 58 | } 59 | 60 | pub fn signature(&mut self) -> Option<&mut String> { 61 | for attr in self.attributes.iter_mut() { 62 | if let Attribute::Signature(sig) = attr { 63 | return Some(&mut sig.signature) 64 | } 65 | } 66 | None 67 | } 68 | 69 | pub fn set_signature(&mut self, sig: Option) { 70 | // According to the JVM spec there must be at most one signature attribute in the attributes table 71 | // first find the index of the existing sig 72 | let index = self.attributes.find_first(|attr| { 73 | matches!(attr, Attribute::Signature(_)) 74 | }); 75 | if let Some(sig) = sig { 76 | let attr = Attribute::Signature(SignatureAttribute::new(sig)); 77 | if let Some(index) = index { 78 | self.attributes.replace(index, attr); 79 | } else { 80 | self.attributes.push(attr); 81 | } 82 | } else if let Some(index) = index { 83 | self.attributes.remove(index); 84 | } 85 | } 86 | 87 | pub fn write(&self, wtr: &mut W, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 88 | self.access_flags.write(wtr)?; 89 | wtr.write_u16::(constant_pool.utf8(self.name.clone()))?; 90 | wtr.write_u16::(constant_pool.utf8(self.descriptor.clone()))?; 91 | Attributes::write(wtr, &self.attributes, constant_pool, None)?; 92 | Ok(()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/insnlist.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::{Insn, LabelInsn}; 2 | use std::fmt::{Debug, Formatter,}; 3 | use std::slice::Iter; 4 | 5 | #[derive(Clone, PartialEq)] 6 | pub struct InsnList { 7 | pub insns: Vec, 8 | pub(crate) labels: u32 9 | } 10 | 11 | impl Default for InsnList { 12 | fn default() -> Self { 13 | InsnList { 14 | insns: Vec::new(), 15 | labels: 0 16 | } 17 | } 18 | } 19 | 20 | #[allow(dead_code)] 21 | impl InsnList { 22 | pub fn new() -> Self { 23 | InsnList::default() 24 | } 25 | 26 | pub fn with_capacity(capacity: usize) -> Self { 27 | InsnList { 28 | insns: Vec::with_capacity(capacity), 29 | labels: 0 30 | } 31 | } 32 | 33 | /// The given label will be valid for the lifetime of this list 34 | pub fn new_label(&mut self) -> LabelInsn { 35 | let id = self.labels; 36 | self.labels += 1; 37 | LabelInsn::new(id) 38 | } 39 | 40 | pub fn iter(&self) -> Iter<'_, Insn> { 41 | self.insns.iter() 42 | } 43 | 44 | pub fn len(&self) -> usize { 45 | self.insns.len() 46 | } 47 | 48 | pub fn is_empty(&self) -> bool { 49 | self.insns.is_empty() 50 | } 51 | } 52 | 53 | 54 | impl Debug for InsnList { 55 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 56 | f.debug_list() 57 | .entries(&self.insns) 58 | .finish() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate derive_more; 2 | extern crate bitflags; 3 | use std::io::{Read, Write}; 4 | use error::Result; 5 | 6 | pub mod classfile; 7 | pub mod constantpool; 8 | pub mod version; 9 | pub mod access; 10 | pub mod attributes; 11 | pub mod field; 12 | pub mod method; 13 | pub mod code; 14 | pub mod ast; 15 | pub mod insnlist; 16 | pub mod error; 17 | pub mod types; 18 | mod utils; 19 | 20 | 21 | pub trait Serializable : Sized { 22 | fn parse(rdr: &mut R) -> Result; 23 | fn write(&self, wtr: &mut W) -> Result<()>; 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use crate::classfile::ClassFile; 29 | use crate::error::Result; 30 | use std::fs::{self, File, DirEntry, OpenOptions}; 31 | use std::io::{BufReader, BufWriter}; 32 | use std::process::Command; 33 | 34 | fn read(dir: &str) -> Result { 35 | // Read 36 | let f = File::open(dir).unwrap(); 37 | let mut reader = BufReader::new(f); 38 | ClassFile::parse(&mut reader) 39 | } 40 | 41 | fn write(class: ClassFile, dir: &String) -> Result<()> { 42 | let f = OpenOptions::new().write(true).open(dir).unwrap(); 43 | let mut writer = BufWriter::new(f); 44 | class.write(&mut writer) 45 | } 46 | 47 | fn print_read(dir: &String) -> Result { 48 | let class = read(dir)?; 49 | println!("{:#x?}", class); 50 | Ok(class) 51 | } 52 | 53 | fn walk(dir: &str, op: &dyn Fn(DirEntry) -> Result<()>) -> Result<()> { 54 | for entry in fs::read_dir(dir)? { 55 | let entry = entry?; 56 | op(entry)?; 57 | } 58 | Ok(()) 59 | } 60 | 61 | #[test] 62 | fn test_classes() -> Result<()> { 63 | /*walk("classes/benchmarking/", &|entry| { 64 | let path = entry.path(); 65 | if path.is_file() { 66 | let extension = path.extension().unwrap().to_str().unwrap(); 67 | if extension == "class" { 68 | read(path.into_os_string().to_str().unwrap()).unwrap(); 69 | } 70 | } 71 | Ok(()) 72 | })?;*/ 73 | walk("classes/testing/", &|entry| { 74 | let path = entry.path(); 75 | if path.is_file() { 76 | let extension = path.extension().unwrap().to_str().unwrap(); 77 | if extension == "class" { 78 | fs::remove_file(path)?; 79 | } 80 | } 81 | Ok(()) 82 | })?; 83 | walk("classes/testing/", &|entry| { 84 | let path = entry.path(); 85 | if path.is_file() { 86 | let extension = path.extension().unwrap().to_str().unwrap(); 87 | if extension == "java" { 88 | let output = Command::new("javac") 89 | .args(&[path.into_os_string().to_str().unwrap()]) 90 | .output() 91 | .unwrap(); 92 | if !output.stderr.is_empty() { 93 | panic!("{}", String::from_utf8(output.stderr).unwrap()); 94 | } 95 | } 96 | } 97 | Ok(()) 98 | })?; 99 | walk("classes/testing/", &|entry| { 100 | let path = entry.path(); 101 | if path.is_file() { 102 | let extension = path.extension().unwrap().to_str().unwrap(); 103 | if extension == "class" { 104 | let dir = path.into_os_string().into_string().unwrap(); 105 | let class = print_read(&dir).unwrap(); 106 | write(class, &dir)?; 107 | } 108 | } 109 | Ok(()) 110 | })?; 111 | Ok(()) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/method.rs: -------------------------------------------------------------------------------- 1 | use crate::access::MethodAccessFlags; 2 | use crate::attributes::{Attribute, Attributes, AttributeSource, SignatureAttribute, ExceptionsAttribute}; 3 | use crate::version::ClassVersion; 4 | use crate::constantpool::{ConstantPool, ConstantPoolWriter}; 5 | use crate::Serializable; 6 | use crate::error::Result; 7 | use crate::utils::{VecUtils}; 8 | use crate::code::CodeAttribute; 9 | use std::io::{Read, Write}; 10 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 11 | 12 | #[allow(non_snake_case)] 13 | pub mod Methods { 14 | use std::io::{Read, Write}; 15 | use crate::method::Method; 16 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 17 | use crate::version::ClassVersion; 18 | use crate::constantpool::{ConstantPool, ConstantPoolWriter}; 19 | 20 | pub fn parse(rdr: &mut T, version: &ClassVersion, constant_pool: &ConstantPool) -> crate::Result> { 21 | let num_fields = rdr.read_u16::()? as usize; 22 | let mut fields: Vec = Vec::with_capacity(num_fields); 23 | for _ in 0..num_fields { 24 | fields.push(Method::parse(rdr, version, constant_pool)?); 25 | } 26 | Ok(fields) 27 | } 28 | 29 | pub fn write(wtr: &mut T, fields: &[Method], constant_pool: &mut ConstantPoolWriter) -> crate::Result<()> { 30 | wtr.write_u16::(fields.len() as u16)?; 31 | for field in fields.iter() { 32 | field.write(wtr, constant_pool)?; 33 | } 34 | Ok(()) 35 | } 36 | } 37 | 38 | #[derive(Clone, Debug, PartialEq)] 39 | pub struct Method { 40 | pub access_flags: MethodAccessFlags, 41 | pub name: String, 42 | pub descriptor: String, 43 | pub attributes: Vec 44 | } 45 | 46 | impl Method { 47 | pub fn parse(rdr: &mut R, version: &ClassVersion, constant_pool: &ConstantPool) -> Result { 48 | let access_flags = MethodAccessFlags::parse(rdr)?; 49 | let name = constant_pool.utf8(rdr.read_u16::()?)?.str.clone(); 50 | let descriptor = constant_pool.utf8(rdr.read_u16::()?)?.str.clone(); 51 | 52 | let attributes = Attributes::parse(rdr, AttributeSource::Method, version, constant_pool, &mut None)?; 53 | 54 | Ok(Method { 55 | access_flags, 56 | name, 57 | descriptor, 58 | attributes 59 | }) 60 | } 61 | 62 | pub fn signature(&mut self) -> Option<&mut String> { 63 | for attr in self.attributes.iter_mut() { 64 | if let Attribute::Signature(sig) = attr { 65 | return Some(&mut sig.signature) 66 | } 67 | } 68 | None 69 | } 70 | 71 | pub fn set_signature(&mut self, sig: Option) { 72 | let index = self.attributes.find_first(|attr| { 73 | matches!(attr, Attribute::Signature(_)) 74 | }); 75 | if let Some(sig) = sig { 76 | let attr = Attribute::Signature(SignatureAttribute::new(sig)); 77 | if let Some(index) = index { 78 | self.attributes.replace(index, attr); 79 | } else { 80 | self.attributes.push(attr); 81 | } 82 | } else if let Some(index) = index { 83 | self.attributes.remove(index); 84 | } 85 | } 86 | 87 | pub fn exceptions(&mut self) -> Option<&mut Vec> { 88 | for attr in self.attributes.iter_mut() { 89 | if let Attribute::Exceptions(x) = attr { 90 | return Some(&mut x.exceptions) 91 | } 92 | } 93 | None 94 | } 95 | 96 | pub fn set_exceptions(&mut self, exc: Option>) { 97 | let index = self.attributes.find_first(|attr| { 98 | matches!(attr, Attribute::Exceptions(_)) 99 | }); 100 | if let Some(exc) = exc { 101 | let attr = Attribute::Exceptions(ExceptionsAttribute::new(exc)); 102 | if let Some(index) = index { 103 | self.attributes.replace(index, attr); 104 | } else { 105 | self.attributes.push(attr); 106 | } 107 | } else if let Some(index) = index { 108 | self.attributes.remove(index); 109 | } 110 | } 111 | 112 | pub fn code(&mut self) -> Option<&mut CodeAttribute> { 113 | for attr in self.attributes.iter_mut() { 114 | if let Attribute::Code(x) = attr { 115 | return Some(x) 116 | } 117 | } 118 | None 119 | } 120 | 121 | pub fn set_code(&mut self, code: Option) { 122 | let index = self.attributes.find_first(|attr| { 123 | matches!(attr, Attribute::Code(_)) 124 | }); 125 | if let Some(code) = code { 126 | let attr = Attribute::Code(code); 127 | if let Some(index) = index { 128 | self.attributes.replace(index, attr); 129 | } else { 130 | self.attributes.push(attr); 131 | } 132 | } else if let Some(index) = index { 133 | self.attributes.remove(index); 134 | } 135 | } 136 | 137 | pub fn write(&self, wtr: &mut W, constant_pool: &mut ConstantPoolWriter) -> Result<()> { 138 | self.access_flags.write(wtr)?; 139 | wtr.write_u16::(constant_pool.utf8(self.name.clone()))?; 140 | wtr.write_u16::(constant_pool.utf8(self.descriptor.clone()))?; 141 | Attributes::write(wtr, &self.attributes, constant_pool, None)?; 142 | Ok(()) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Result, ParserError}; 2 | 3 | const VOID: char = 'V'; 4 | const BYTE: char = 'B'; 5 | const CHAR: char = 'C'; 6 | const DOUBLE: char = 'D'; 7 | const FLOAT: char = 'F'; 8 | const INT: char = 'I'; 9 | const LONG: char = 'J'; 10 | const SHORT: char = 'S'; 11 | const BOOLEAN: char = 'Z'; 12 | 13 | #[derive(Clone, Debug, PartialEq, Eq)] 14 | pub enum Type { 15 | Reference(Option), // If None then the reference refers to no particular class 16 | Boolean, 17 | Byte, 18 | Char, 19 | Short, 20 | Int, 21 | Long, 22 | Float, 23 | Double, 24 | Void 25 | } 26 | 27 | impl Type { 28 | /// returns the size of the type as a multiple of a dword 29 | pub fn size(&self) -> u8 { 30 | match self { 31 | Type::Reference(_) => 1, 32 | Type::Boolean => 1, 33 | Type::Byte => 1, 34 | Type::Char => 1, 35 | Type::Short => 1, 36 | Type::Int => 1, 37 | Type::Long => 2, 38 | Type::Float => 1, 39 | Type::Double => 2, 40 | Type::Void => 0, 41 | } 42 | } 43 | } 44 | 45 | pub fn parse_method_desc(desc: &str) -> Result<(Vec, Type)> { 46 | parse_method_desc_chars(&desc.as_bytes()) 47 | } 48 | 49 | fn parse_method_desc_chars(desc: &[u8]) -> Result<(Vec, Type)> { 50 | if desc[0] != b'(' { 51 | return Err(ParserError::invalid_descriptor("Method desc must start with '('")); 52 | } 53 | let mut args: Vec = Vec::new(); 54 | let mut i = 1usize; 55 | while desc[i] != b')' { 56 | let (typ, i2) = parse_type_chars(desc, i)?; 57 | args.push(typ); 58 | i = i2; 59 | 60 | if i >= desc.len() { 61 | return Err(ParserError::invalid_descriptor("Method desc must have ')'")); 62 | } 63 | } 64 | let (ret, _) = parse_type_chars(desc, i + 1)?; 65 | Ok((args, ret)) 66 | } 67 | 68 | pub fn parse_type(desc: &str) -> Result<(Type, usize)> { 69 | parse_type_chars(&desc.as_bytes(), 0) 70 | } 71 | 72 | fn parse_type_chars(desc: &[u8], mut index: usize) -> Result<(Type, usize)> { 73 | if index == desc.len() { 74 | return Err(ParserError::invalid_descriptor("Empty type string")); 75 | } 76 | Ok(match desc[index] as char { 77 | VOID => (Type::Void, index + 1), 78 | BYTE => (Type::Byte, index + 1), 79 | CHAR => (Type::Char, index + 1), 80 | DOUBLE => (Type::Double, index + 1), 81 | FLOAT => (Type::Float, index + 1), 82 | INT => (Type::Int, index + 1), 83 | LONG => (Type::Long, index + 1), 84 | SHORT => (Type::Short, index + 1), 85 | BOOLEAN => (Type::Boolean, index + 1), 86 | 'L' => { 87 | let mut buf = String::new(); 88 | while desc[index] != b';' { 89 | index += 1; 90 | if index >= desc.len() { 91 | return Err(ParserError::invalid_descriptor("Type missing ';'")) 92 | } 93 | buf.push(desc[index] as char); 94 | } 95 | (Type::Reference(Some(buf)), index + 1) 96 | } 97 | x => return Err(ParserError::invalid_descriptor(format!("Unknown type '{}'", x))) 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::io::Read; 2 | use std::collections::HashMap; 3 | use std::hash::Hash; 4 | 5 | pub trait VecUtils { 6 | /// Overwrites the given index with the given item and returns the previous item if successful 7 | /// May return null if the index is out of bounds 8 | fn replace(&mut self, index: usize, item: T) -> Option; 9 | 10 | fn find_first(&self, op: F) -> Option 11 | where 12 | F: FnMut(&T) -> bool; 13 | } 14 | 15 | impl VecUtils for Vec { 16 | fn replace(&mut self, index: usize, item: T) -> Option { 17 | if index >= self.len() { 18 | return None; 19 | } 20 | let v = &mut **self; 21 | Some(std::mem::replace(&mut v[index], item)) 22 | } 23 | 24 | fn find_first(&self, mut op: F) -> Option where 25 | F: FnMut(&T) -> bool { 26 | for (i, attr) in self.iter().enumerate() { 27 | if op(attr) { 28 | return Some(i); 29 | } 30 | } 31 | None 32 | } 33 | } 34 | 35 | pub trait ReadUtils: Read { 36 | #[inline] 37 | fn read_nbytes(&mut self, nbytes: usize) -> std::io::Result> { 38 | let mut buf = vec![0u8; nbytes]; 39 | self.read_exact(&mut buf)?; 40 | Ok(buf) 41 | } 42 | } 43 | impl ReadUtils for W {} 44 | 45 | pub trait MapUtils { 46 | /// returns true if inserted 47 | fn insert_if_not_present(&mut self, key: K, value: V) -> bool; 48 | 49 | fn insert_if_not_present_lazy(&mut self, key: K, value: F) -> bool 50 | where F: FnOnce() -> V; 51 | } 52 | 53 | impl MapUtils for HashMap { 54 | #[inline] 55 | fn insert_if_not_present(&mut self, key: K, value: V) -> bool { 56 | if self.get(&key).is_none() { 57 | self.insert(key, value); 58 | true 59 | } else { 60 | false 61 | } 62 | } 63 | 64 | #[inline] 65 | fn insert_if_not_present_lazy(&mut self, key: K, value: F) -> bool where F: FnOnce() -> V { 66 | if self.get(&key).is_none() { 67 | self.insert(key, value()); 68 | true 69 | } else { 70 | false 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | use crate::Serializable; 2 | use std::io::{Read, Write}; 3 | use std::cmp::{PartialOrd, Ordering}; 4 | use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt}; 5 | use crate::error::{Result, ParserError}; 6 | use std::convert::{TryFrom, TryInto}; 7 | 8 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 9 | pub struct ClassVersion { 10 | pub major: MajorVersion, 11 | pub minor: u16 12 | } 13 | 14 | impl Ord for ClassVersion { 15 | fn cmp(&self, other: &Self) -> Ordering { 16 | let major = self.major.cmp(&other.major); 17 | if major == Ordering::Equal { 18 | self.minor.cmp(&other.minor) 19 | } else { 20 | major 21 | } 22 | } 23 | } 24 | 25 | impl PartialOrd for ClassVersion { 26 | fn partial_cmp(&self, other: &Self) -> Option { 27 | Some(self.cmp(other)) 28 | } 29 | } 30 | 31 | impl Serializable for ClassVersion { 32 | fn parse(rdr: &mut R) -> Result { 33 | let minor = rdr.read_u16::()?; 34 | let major = rdr.read_u16::()?; 35 | Ok(ClassVersion::new(major.try_into()?, minor)) 36 | } 37 | 38 | fn write(&self, wtr: &mut W) -> Result<()> { 39 | wtr.write_u16::(self.minor)?; 40 | wtr.write_u16::(self.major.into())?; 41 | Ok(()) 42 | } 43 | } 44 | 45 | #[allow(dead_code)] 46 | impl ClassVersion { 47 | fn new_major(major: MajorVersion) -> Self { 48 | ClassVersion::new(major, 0) 49 | } 50 | fn new(major: MajorVersion, minor: u16) -> Self { 51 | ClassVersion { 52 | major, minor 53 | } 54 | } 55 | } 56 | 57 | #[allow(non_camel_case_types)] 58 | #[repr(u16)] 59 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 60 | pub enum MajorVersion { 61 | JDK_1_1 = 45, 62 | JDK_1_2 = 46, 63 | JDK_1_3 = 47, 64 | JDK_1_4 = 48, 65 | JAVA_5 = 49, 66 | JAVA_6 = 50, 67 | JAVA_7 = 51, 68 | JAVA_8 = 52, 69 | JAVA_9 = 53, 70 | JAVA_10 = 54, 71 | JAVA_11 = 55, 72 | JAVA_12 = 56, 73 | JAVA_13 = 57, 74 | JAVA_14 = 58, 75 | JAVA_15 = 59 76 | } 77 | 78 | impl From for u16 { 79 | fn from(version_enum: MajorVersion) -> u16 { 80 | version_enum as u16 81 | } 82 | } 83 | 84 | impl TryFrom for MajorVersion { 85 | type Error = ParserError; 86 | fn try_from(version: u16) -> Result { 87 | Ok(match version { 88 | 45 => MajorVersion::JDK_1_1, 89 | 46 => MajorVersion::JDK_1_2, 90 | 47 => MajorVersion::JDK_1_3, 91 | 48 => MajorVersion::JDK_1_4, 92 | 49 => MajorVersion::JAVA_5, 93 | 50 => MajorVersion::JAVA_6, 94 | 51 => MajorVersion::JAVA_7, 95 | 52 => MajorVersion::JAVA_8, 96 | 53 => MajorVersion::JAVA_9, 97 | 54 => MajorVersion::JAVA_10, 98 | 55 => MajorVersion::JAVA_11, 99 | 56 => MajorVersion::JAVA_12, 100 | 57 => MajorVersion::JAVA_13, 101 | 58 => MajorVersion::JAVA_14, 102 | 59 => MajorVersion::JAVA_15, 103 | _ => return Err(ParserError::Unrecognized("major version", version.to_string())) 104 | }) 105 | } 106 | } 107 | --------------------------------------------------------------------------------