├── src ├── shell │ ├── mod.rs │ └── command.rs ├── classpath │ ├── mod.rs │ └── classpath.rs ├── instruction │ ├── control │ │ ├── mod.rs │ │ └── goto.rs │ ├── load │ │ ├── mod.rs │ │ └── iload.rs │ ├── store │ │ ├── mod.rs │ │ └── istore.rs │ ├── constant │ │ ├── mod.rs │ │ ├── nop.rs │ │ ├── xipush.rs │ │ └── ldc.rs │ ├── math │ │ ├── mod.rs │ │ ├── inc.rs │ │ ├── and.rs │ │ ├── add.rs │ │ ├── mul.rs │ │ └── neg.rs │ ├── comparison │ │ ├── mod.rs │ │ ├── lcmp.rs │ │ ├── dcmp.rs │ │ ├── fcmp.rs │ │ ├── ifcond.rs │ │ └── if_icmp.rs │ ├── mod.rs │ └── instruction.rs ├── rtda │ ├── slot.rs │ ├── mod.rs │ ├── heap │ │ ├── mod.rs │ │ ├── object.rs │ │ ├── class_ref.rs │ │ ├── symbol_ref.rs │ │ ├── access_flags.rs │ │ ├── class_member.rs │ │ ├── field.rs │ │ ├── class.rs │ │ ├── method.rs │ │ └── class_loader.rs │ ├── stack.rs │ ├── thread.rs │ ├── vars.rs │ ├── operand_stack.rs │ └── frame.rs ├── util │ ├── mod.rs │ ├── converter.rs │ ├── code_reader.rs │ └── modified_utf8.rs ├── test_data │ ├── MyObject.class │ ├── Object.class │ ├── GaussTest.class │ ├── FibonacciTest.class │ ├── GaussTest.java │ └── MyObject.java ├── lib.rs ├── classfile │ ├── mod.rs │ ├── constant_info.rs │ ├── member_info.rs │ ├── class_file.rs │ ├── constant_pool.rs │ ├── attribute_info.rs │ └── class_reader.rs └── bin │ └── main.rs ├── .gitignore ├── Cargo.toml ├── .github └── dependabot.yml ├── LICENSE ├── README.md ├── .travis.yml └── Cargo.lock /src/shell/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod command; 2 | -------------------------------------------------------------------------------- /src/classpath/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod classpath; 2 | -------------------------------------------------------------------------------- /src/instruction/control/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod goto; 2 | -------------------------------------------------------------------------------- /src/instruction/load/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod iload; 2 | -------------------------------------------------------------------------------- /src/instruction/store/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod istore; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | .idea 5 | jvm-rs.iml -------------------------------------------------------------------------------- /src/rtda/slot.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum Slot { 3 | Num(i32), 4 | } 5 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod code_reader; 2 | pub mod converter; 3 | pub mod modified_utf8; 4 | -------------------------------------------------------------------------------- /src/instruction/constant/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ldc; 2 | pub mod nop; 3 | pub mod xconst; 4 | pub mod xipush; 5 | -------------------------------------------------------------------------------- /src/test_data/MyObject.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/standbyme/jvm-rs/HEAD/src/test_data/MyObject.class -------------------------------------------------------------------------------- /src/test_data/Object.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/standbyme/jvm-rs/HEAD/src/test_data/Object.class -------------------------------------------------------------------------------- /src/instruction/math/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod add; 2 | pub mod and; 3 | pub mod inc; 4 | pub mod mul; 5 | pub mod neg; 6 | -------------------------------------------------------------------------------- /src/test_data/GaussTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/standbyme/jvm-rs/HEAD/src/test_data/GaussTest.class -------------------------------------------------------------------------------- /src/test_data/FibonacciTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/standbyme/jvm-rs/HEAD/src/test_data/FibonacciTest.class -------------------------------------------------------------------------------- /src/instruction/comparison/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dcmp; 2 | pub mod fcmp; 3 | pub mod if_icmp; 4 | pub mod ifcond; 5 | pub mod lcmp; 6 | -------------------------------------------------------------------------------- /src/instruction/mod.rs: -------------------------------------------------------------------------------- 1 | mod comparison; 2 | mod constant; 3 | mod control; 4 | pub mod instruction; 5 | mod load; 6 | mod math; 7 | mod store; 8 | -------------------------------------------------------------------------------- /src/rtda/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod frame; 2 | pub mod heap; 3 | pub mod operand_stack; 4 | mod slot; 5 | mod stack; 6 | pub mod thread; 7 | pub mod vars; 8 | -------------------------------------------------------------------------------- /src/rtda/heap/mod.rs: -------------------------------------------------------------------------------- 1 | mod access_flags; 2 | pub mod class; 3 | pub mod class_loader; 4 | mod class_member; 5 | mod field; 6 | pub mod method; 7 | mod object; 8 | -------------------------------------------------------------------------------- /src/rtda/heap/object.rs: -------------------------------------------------------------------------------- 1 | use rtda::heap::class::Class; 2 | use rtda::slot::Slot; 3 | 4 | struct Object { 5 | class: Class, 6 | fields: Vec, 7 | } 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate vec_map; 2 | 3 | pub mod classfile; 4 | pub mod classpath; 5 | pub mod instruction; 6 | pub mod rtda; 7 | pub mod shell; 8 | pub mod util; 9 | -------------------------------------------------------------------------------- /src/shell/command.rs: -------------------------------------------------------------------------------- 1 | pub struct Command<'a> { 2 | pub class_name: String, 3 | pub cp_opt: Option, 4 | pub jre_opt: Option, 5 | pub args: Vec<&'a str>, 6 | } 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jvm" 3 | version = "0.1.0" 4 | authors = ["StandbyMe "] 5 | 6 | [dependencies] 7 | byteorder = "1" 8 | vec_map = "0.8.1" 9 | zip = "0.5.13" -------------------------------------------------------------------------------- /src/classfile/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate vec_map; 2 | 3 | pub mod attribute_info; 4 | pub mod class_file; 5 | pub mod class_reader; 6 | pub mod constant_info; 7 | pub mod constant_pool; 8 | pub mod member_info; 9 | -------------------------------------------------------------------------------- /src/test_data/GaussTest.java: -------------------------------------------------------------------------------- 1 | package test_data; 2 | 3 | public class GaussTest { 4 | 5 | public static void main(String[] args) { 6 | int sum = 0; 7 | for (int i = 1; i <= 100; i++) { 8 | sum += i; 9 | } 10 | System.out.println(sum); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "21:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: zip 11 | versions: 12 | - 0.5.10 13 | - 0.5.11 14 | - 0.5.9 15 | -------------------------------------------------------------------------------- /src/instruction/constant/nop.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::thread::Thread; 3 | use util::code_reader::CodeReader; 4 | 5 | #[allow(non_snake_case)] 6 | pub fn NOP(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 7 | let execute_result = ExecuteResult { thread, offset: 0 }; 8 | (execute_result, code_reader) 9 | } 10 | -------------------------------------------------------------------------------- /src/rtda/heap/class_ref.rs: -------------------------------------------------------------------------------- 1 | use rtda::heap::symbol_ref::SymbolRef; 2 | use std::rc::Rc; 3 | use classfile::constant_pool::ConstantPool; 4 | use classfile::constant_info::ConstantInfo; 5 | 6 | struct ClassRef { 7 | symbol_ref: SymbolRef, 8 | } 9 | 10 | impl ClassRef { 11 | fn new(constant_pool: Rc, constant_class_info: ConstantInfo) -> ClassRef { 12 | let symbol_ref = SymbolRef{ 13 | constant_pool, 14 | 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/rtda/heap/symbol_ref.rs: -------------------------------------------------------------------------------- 1 | use classfile::constant_pool::ConstantPool; 2 | use rtda::heap::class::Class; 3 | use std::rc::Rc; 4 | use classfile::constant_info::ConstantInfo; 5 | 6 | pub struct SymbolRef { 7 | constant_pool: Rc, 8 | class_name: String, 9 | class: Rc, 10 | } 11 | 12 | impl SymbolRef { 13 | fn new(constant_pool: Rc, constant_class_info: ConstantInfo) -> ClassRef { 14 | let class_name = 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/instruction/control/goto.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::thread::Thread; 3 | use util::code_reader::CodeReader; 4 | 5 | #[allow(non_snake_case)] 6 | pub fn GOTO(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 7 | println!("GOTO"); 8 | 9 | let (offset, code_reader) = code_reader.read_i16(); 10 | let offset = offset as isize; 11 | 12 | let execute_result = ExecuteResult { thread, offset }; 13 | (execute_result, code_reader) 14 | } 15 | -------------------------------------------------------------------------------- /src/classfile/constant_info.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum ConstantInfo { 3 | Integer(i32), 4 | Float(f32), 5 | Long(i64), 6 | Double(f64), 7 | UTF8(String), 8 | String(u16), 9 | Class { 10 | name_index: u16, 11 | }, 12 | NameAndType { 13 | name_index: u16, 14 | descriptor_index: u16, 15 | }, 16 | FieldRef { 17 | class_index: u16, 18 | name_and_type_index: u16, 19 | }, 20 | MethodRef { 21 | class_index: u16, 22 | name_and_type_index: u16, 23 | }, 24 | InterfaceMethodRef { 25 | class_index: u16, 26 | name_and_type_index: u16, 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /src/test_data/MyObject.java: -------------------------------------------------------------------------------- 1 | public class MyObject { 2 | 3 | public static int staticVar; 4 | public int instanceVar; 5 | 6 | public static void main(String[] args) { 7 | int x = 32768; // ldc 8 | MyObject myObj = new MyObject(); // new 9 | MyObject.staticVar = x; // putstatic 10 | x = MyObject.staticVar; // getstatic 11 | myObj.instanceVar = x; // putfield 12 | x = myObj.instanceVar; // getfield 13 | Object obj = myObj; 14 | if (obj instanceof MyObject) { // instanceof 15 | myObj = (MyObject) obj; // checkcast 16 | System.out.println(myObj.instanceVar); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/util/converter.rs: -------------------------------------------------------------------------------- 1 | use std; 2 | 3 | pub fn i32_to_f32(val: i32) -> f32 { 4 | unsafe { std::mem::transmute::(val) } 5 | } 6 | 7 | pub fn f32_to_i32(val: f32) -> i32 { 8 | unsafe { std::mem::transmute::(val) } 9 | } 10 | 11 | pub fn i64_to_i32seq(val: i64) -> [i32; 2] { 12 | unsafe { std::mem::transmute::(val) } 13 | } 14 | 15 | pub fn f64_to_i32seq(val: f64) -> [i32; 2] { 16 | unsafe { std::mem::transmute::(val) } 17 | } 18 | 19 | pub fn i32seq_to_i64(val: [i32; 2]) -> i64 { 20 | unsafe { std::mem::transmute::<[i32; 2], i64>(val) } 21 | } 22 | 23 | pub fn i32seq_to_f64(val: [i32; 2]) -> f64 { 24 | unsafe { std::mem::transmute::<[i32; 2], f64>(val) } 25 | } 26 | -------------------------------------------------------------------------------- /src/rtda/heap/access_flags.rs: -------------------------------------------------------------------------------- 1 | pub const ACC_PUBLIC: u16 = 0x0001; 2 | pub const ACC_PRIVATE: u16 = 0x0002; 3 | pub const ACC_PROTECTED: u16 = 0x0004; 4 | pub const ACC_STATIC: u16 = 0x0008; 5 | pub const ACC_FINAL: u16 = 0x0010; 6 | pub const ACC_SUPER: u16 = 0x0020; 7 | pub const ACC_SYNCHRONIZED: u16 = 0x0020; 8 | pub const ACC_VOLATILE: u16 = 0x0040; 9 | pub const ACC_BRIDGE: u16 = 0x0040; 10 | pub const ACC_TRANSIENT: u16 = 0x0080; 11 | pub const ACC_VARARGS: u16 = 0x0080; 12 | pub const ACC_NATIVE: u16 = 0x0100; 13 | pub const ACC_INTERFACE: u16 = 0x0200; 14 | pub const ACC_ABSTRACT: u16 = 0x0400; 15 | pub const ACC_STRICT: u16 = 0x0800; 16 | pub const ACC_SYNTHETIC: u16 = 0x1000; 17 | pub const ACC_ANNOTATION: u16 = 0x2000; 18 | pub const ACC_ENUM: u16 = 0x4000; 19 | -------------------------------------------------------------------------------- /src/rtda/stack.rs: -------------------------------------------------------------------------------- 1 | use rtda::frame::Frame; 2 | 3 | pub struct Stack { 4 | max_size: usize, 5 | vec: Vec, 6 | } 7 | 8 | impl Stack { 9 | pub fn new(max_size: usize) -> Stack { 10 | let vec = Vec::with_capacity(max_size); 11 | Stack { max_size, vec } 12 | } 13 | 14 | pub fn push(mut self, frame: Frame) -> Stack { 15 | if self.vec.len() == self.max_size { 16 | panic!("java.lang.StackOverflowError") 17 | } else { 18 | self.vec.push(frame); 19 | self 20 | } 21 | } 22 | 23 | pub fn pop(mut self) -> (Frame, Stack) { 24 | let frame = self.vec.pop().unwrap(); 25 | (frame, self) 26 | } 27 | 28 | pub fn is_empty(&self) -> bool { 29 | self.vec.is_empty() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/classfile/member_info.rs: -------------------------------------------------------------------------------- 1 | use classfile::attribute_info::AttributeInfo; 2 | 3 | #[derive(Debug)] 4 | pub struct MemberInfo { 5 | pub access_flags: u16, 6 | pub name: String, 7 | pub name_index: u16, 8 | pub descriptor_index: u16, 9 | pub descriptor: String, 10 | pub attributes: Vec, 11 | } 12 | 13 | impl MemberInfo { 14 | pub fn code_attribute(&self) -> Option<&AttributeInfo> { 15 | self.attributes.iter().find(|x| match x { 16 | AttributeInfo::Code { .. } => true, 17 | _ => false, 18 | }) 19 | } 20 | 21 | pub fn constant_value_attribute(&self) -> Option<&AttributeInfo> { 22 | self.attributes.iter().find(|x| match x { 23 | AttributeInfo::ConstantValue { .. } => true, 24 | _ => false, 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/rtda/thread.rs: -------------------------------------------------------------------------------- 1 | use rtda::frame::Frame; 2 | use rtda::stack::Stack; 3 | 4 | const STACK_SIZE: usize = 1024; 5 | 6 | pub struct Thread { 7 | stack: Stack, 8 | } 9 | 10 | impl Thread { 11 | pub fn new() -> Thread { 12 | Thread { 13 | stack: Stack::new(STACK_SIZE), 14 | } 15 | } 16 | 17 | pub fn push_frame(self, frame: Frame) -> Thread { 18 | let Thread { stack } = self; 19 | Thread { 20 | stack: stack.push(frame), 21 | } 22 | } 23 | 24 | pub fn pop_frame(self) -> (Frame, Thread) { 25 | let Thread { stack } = self; 26 | let (frame, stack) = stack.pop(); 27 | let thread = Thread { stack }; 28 | (frame, thread) 29 | } 30 | 31 | pub fn is_stack_empty(&self) -> bool { 32 | self.stack.is_empty() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/rtda/vars.rs: -------------------------------------------------------------------------------- 1 | extern crate vec_map; 2 | 3 | use self::vec_map::VecMap; 4 | 5 | use rtda::slot::Slot; 6 | 7 | #[derive(Debug)] 8 | pub struct Vars { 9 | vec_map: VecMap, 10 | } 11 | 12 | impl Vars { 13 | pub fn new(max_locals: usize) -> Vars { 14 | if max_locals > 0 { 15 | let vec_map = VecMap::with_capacity(max_locals); 16 | Vars { vec_map } 17 | } else { 18 | panic!("max_locals < 0") 19 | } 20 | } 21 | 22 | pub fn set_int(mut self, index: usize, val: i32) -> Vars { 23 | self.vec_map.insert(index, Slot::Num(val)); 24 | self 25 | } 26 | 27 | pub fn get_int(&self, index: usize) -> i32 { 28 | match self.vec_map[index] { 29 | Slot::Num(val) => val, 30 | _ => panic!("get_int from wrong place"), 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/rtda/heap/class_member.rs: -------------------------------------------------------------------------------- 1 | use classfile::member_info::MemberInfo; 2 | use rtda::heap::access_flags::*; 3 | 4 | #[derive(Debug)] 5 | pub struct ClassMember { 6 | pub access_flags: u16, 7 | pub name: String, 8 | pub descriptor: String, 9 | } 10 | 11 | impl ClassMember { 12 | pub fn new(member_info: &MemberInfo) -> ClassMember { 13 | let MemberInfo { 14 | access_flags, 15 | name, 16 | descriptor, 17 | .. 18 | } = member_info; 19 | ClassMember { 20 | access_flags: *access_flags, 21 | name: name.to_string(), 22 | descriptor: descriptor.to_string(), 23 | } 24 | } 25 | pub fn is_static(&self) -> bool { 26 | self.access_flags & ACC_STATIC != 0 27 | } 28 | 29 | pub fn is_final(&self) -> bool { 30 | self.access_flags & ACC_FINAL != 0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/instruction/constant/xipush.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn BIPUSH(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | println!("BIPUSH"); 9 | let (frame, thread) = thread.pop_frame(); 10 | 11 | let Frame { 12 | operand_stack, 13 | local_vars, 14 | method, 15 | class, 16 | } = frame; 17 | 18 | let (val, code_reader) = code_reader.read_i8(); 19 | let i = val; 20 | let operand_stack = operand_stack.push_int(i as i32); 21 | 22 | let frame = Frame { 23 | class, 24 | operand_stack, 25 | local_vars, 26 | method, 27 | }; 28 | let thread = thread.push_frame(frame); 29 | let execute_result = ExecuteResult { thread, offset: 0 }; 30 | (execute_result, code_reader) 31 | } 32 | -------------------------------------------------------------------------------- /src/instruction/math/inc.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn IINC(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | println!("IINC"); 9 | 10 | let (frame, thread) = thread.pop_frame(); 11 | 12 | let Frame { 13 | operand_stack, 14 | local_vars, 15 | method, 16 | class, 17 | } = frame; 18 | 19 | let (index, code_reader) = code_reader.read_u8(); 20 | let (val_1, code_reader) = code_reader.read_u8(); 21 | let index = index as usize; 22 | let val_1 = val_1 as i32; 23 | 24 | let val_2 = local_vars.get_int(index); 25 | let val = val_1 + val_2; 26 | let local_vars = local_vars.set_int(index, val); 27 | 28 | let frame = Frame { 29 | class, 30 | operand_stack, 31 | local_vars, 32 | method, 33 | }; 34 | let thread = thread.push_frame(frame); 35 | let execute_result = ExecuteResult { thread, offset: 0 }; 36 | (execute_result, code_reader) 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 StandbyMe 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 | -------------------------------------------------------------------------------- /src/rtda/heap/field.rs: -------------------------------------------------------------------------------- 1 | use classfile::attribute_info::AttributeInfo; 2 | use classfile::member_info::MemberInfo; 3 | use rtda::heap::class_member::ClassMember; 4 | 5 | #[derive(Debug)] 6 | pub struct Field { 7 | pub class_member: ClassMember, 8 | pub constant_value_index: Option, 9 | } 10 | 11 | impl Field { 12 | pub fn new(member_info: MemberInfo) -> Field { 13 | let class_member = ClassMember::new(&member_info); 14 | let constant_value_index = member_info.constant_value_attribute().map(|x| match x { 15 | AttributeInfo::ConstantValue { 16 | constant_value_index, 17 | } => *constant_value_index as usize, 18 | _ => panic!("Not ConstantValue"), 19 | }); 20 | Field { 21 | class_member, 22 | constant_value_index, 23 | } 24 | } 25 | 26 | pub fn is_static(&self) -> bool { 27 | self.class_member.is_static() 28 | } 29 | 30 | pub fn is_long_or_double(&self) -> bool { 31 | let descriptor = &self.class_member.descriptor; 32 | descriptor == "J" || descriptor == "D" 33 | } 34 | 35 | pub fn is_final(&self) -> bool { 36 | self.class_member.is_final() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/classfile/class_file.rs: -------------------------------------------------------------------------------- 1 | use classfile::attribute_info::AttributeInfo; 2 | use classfile::constant_pool::ConstantPool; 3 | use classfile::member_info::MemberInfo; 4 | 5 | #[derive(Debug)] 6 | pub struct ClassFile { 7 | pub major_version: u16, 8 | pub minor_version: u16, 9 | pub constant_pool: ConstantPool, 10 | pub access_flags: u16, 11 | pub this_class: u16, 12 | pub super_class: u16, 13 | pub interfaces: Vec, 14 | pub fields: Vec, 15 | pub methods: Vec, 16 | pub attributes: Vec, 17 | } 18 | 19 | impl ClassFile { 20 | pub fn main_method<'a>(&'a self) -> &'a MemberInfo { 21 | self.methods 22 | .iter() 23 | .find(|x| x.name == "main" && x.descriptor == "([Ljava/lang/String;)V") 24 | .expect("Main method not found") 25 | } 26 | 27 | pub fn class_name(&self) -> &str { 28 | self.constant_pool.get_class_name(self.this_class as usize) 29 | } 30 | 31 | pub fn super_class_name(&self) -> &str { 32 | let super_class = self.super_class as usize; 33 | if super_class > 0 { 34 | self.constant_pool.get_class_name(super_class) 35 | } else { 36 | "" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/classfile/constant_pool.rs: -------------------------------------------------------------------------------- 1 | use classfile::constant_info::ConstantInfo; 2 | use vec_map::VecMap; 3 | 4 | #[derive(Debug)] 5 | pub struct ConstantPool { 6 | pub vec_map: VecMap, 7 | } 8 | 9 | impl ConstantPool { 10 | pub fn insert(&mut self, index: usize, constant_info: ConstantInfo) { 11 | self.vec_map.insert(index, constant_info); 12 | } 13 | 14 | pub fn get(&self, index: usize) -> &ConstantInfo { 15 | self.vec_map.get(index).expect("Bad constant pool index") 16 | } 17 | 18 | pub fn capacity(&self) -> usize { 19 | self.vec_map.capacity() 20 | } 21 | 22 | fn get_utf8(&self, index: usize) -> &str { 23 | match self.get(index) { 24 | ConstantInfo::UTF8(ref name) => name, 25 | _ => panic!("index isn't to UTF8"), 26 | } 27 | } 28 | 29 | pub fn get_class_name(&self, index: usize) -> &str { 30 | let constant_info = self.get(index); 31 | let name_index = match constant_info { 32 | ConstantInfo::Class { name_index } => name_index, 33 | _ => panic!("index isn't to Class"), 34 | }; 35 | self.get_utf8(*name_index as usize) 36 | } 37 | 38 | // fn get_class_ref(&self, index: usize) -> &str { 39 | 40 | // } 41 | } 42 | -------------------------------------------------------------------------------- /src/rtda/heap/class.rs: -------------------------------------------------------------------------------- 1 | use classfile::constant_pool::ConstantPool; 2 | use rtda::heap::field::Field; 3 | use rtda::heap::method::Method; 4 | use rtda::vars::Vars; 5 | use std::rc::Rc; 6 | 7 | #[derive(Debug)] 8 | pub struct Class { 9 | pub access_flags: u16, 10 | pub name: String, 11 | // pub super_class_name: String, 12 | // interface_names: Vec, 13 | pub constant_pool: ConstantPool, 14 | pub fields: Vec, 15 | pub methods: Vec>, 16 | // loader * ClassLoader 17 | pub super_class: Option>, 18 | // interfaces [] * Class 19 | pub instance_slot_count: usize, 20 | pub static_slot_count: usize, 21 | pub static_vars: Vars, 22 | } 23 | 24 | impl Class { 25 | pub fn main_method(&self) -> Rc { 26 | self.get_method("main", "([Ljava/lang/String;)V", true) 27 | } 28 | fn get_method(&self, name: &str, descriptor: &str, is_static: bool) -> Rc { 29 | println!( 30 | "name {} descriptor {} is_static {}", 31 | name, descriptor, is_static 32 | ); 33 | let reference = self.methods 34 | .iter() 35 | .find(|x| { 36 | x.is_static() == is_static && x.name() == name && x.descriptor() == descriptor 37 | }) 38 | .expect("Method not found"); 39 | Rc::clone(reference) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/classfile/attribute_info.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | #[derive(Debug)] 4 | pub struct ExceptionTableEntry { 5 | pub start_pc: u16, 6 | pub end_pc: u16, 7 | pub handler_pc: u16, 8 | pub catch_type: u16, 9 | } 10 | 11 | #[derive(Debug)] 12 | pub struct LineNumberTableEntry { 13 | pub start_pc: u16, 14 | pub line_number: u16, 15 | } 16 | 17 | #[derive(Debug)] 18 | pub struct LocalVariableTableEntry { 19 | pub start_pc: u16, 20 | pub length: u16, 21 | pub name_index: u16, 22 | pub descriptor_index: u16, 23 | pub index: u16, 24 | } 25 | 26 | #[derive(Debug)] 27 | pub enum AttributeInfo { 28 | Code { 29 | max_stack: u16, 30 | max_locals: u16, 31 | code: Rc>, 32 | exception_table: Vec, 33 | attributes: Vec, 34 | }, 35 | ConstantValue { 36 | constant_value_index: u16, 37 | }, 38 | Deprecated, 39 | Exceptions { 40 | exception_index_table: Vec, 41 | }, 42 | SourceFile { 43 | sourcefile_index: u16, 44 | }, 45 | Synthetic, 46 | Unparsed { 47 | attribute_name: String, 48 | attribute_length: u32, 49 | }, 50 | LineNumberTable { 51 | line_number_table: Vec, 52 | }, 53 | LocalVariableTable { 54 | local_variable_table: Vec, 55 | }, 56 | } 57 | -------------------------------------------------------------------------------- /src/instruction/store/istore.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | fn _istore(frame: Frame, index: usize) -> Frame { 7 | let Frame { 8 | operand_stack, 9 | local_vars, 10 | method, 11 | class, 12 | } = frame; 13 | let (val, operand_stack) = operand_stack.pop_int(); 14 | let local_vars = local_vars.set_int(index, val); 15 | Frame { 16 | class, 17 | operand_stack, 18 | local_vars, 19 | method, 20 | } 21 | } 22 | 23 | #[allow(non_snake_case)] 24 | pub fn ISTORE_1(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 25 | println!("ISTORE_1"); 26 | let (frame, thread) = thread.pop_frame(); 27 | 28 | let frame = _istore(frame, 1); 29 | let thread = thread.push_frame(frame); 30 | let execute_result = ExecuteResult { thread, offset: 0 }; 31 | (execute_result, code_reader) 32 | } 33 | 34 | #[allow(non_snake_case)] 35 | pub fn ISTORE_2(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 36 | println!("ISTORE_2"); 37 | let (frame, thread) = thread.pop_frame(); 38 | 39 | let frame = _istore(frame, 2); 40 | let thread = thread.push_frame(frame); 41 | let execute_result = ExecuteResult { thread, offset: 0 }; 42 | (execute_result, code_reader) 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jvm-rs 2 | [![Codecov](https://img.shields.io/codecov/c/github/standbyme/jvm-rs.svg?style=flat-square)](https://codecov.io/gh/standbyme/jvm-rs) 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/standbyme/jvm-rs/) 4 | [![](https://img.shields.io/gitter/room/jvm-rs/main.js.svg)](https://gitter.im/jvm-rs/main) 5 | 6 | A JVM on Rust under development. 7 | 8 | Strive to achieve 9 | - **Clear** structure. 10 | - Well **tested** 11 | - Minimal Unsafe(**Only** [src/util/converter.rs](https://github.com/standbyme/jvm-rs/blob/master/src/util/converter.rs) has unsafe code) 12 | - Minimal Mutable 13 | - Functional Programming 14 | 15 | Now it support **Arithmetic Operation, Control Flow and println function call**. It's very easy to understand and follow. 16 | 17 | Welcome Star : ) 18 | 19 | # Introduction 20 | jvm-rs is a toy JVM (which is far from complete) programmed in Rust inspired by [jvm.go](https://github.com/zxh0/jvm.go). The main purpose of this project is learning Rust and the JVM. So the number one goal of the project is readability of code. The basic idea is to just implement the core JVM, and use `rt.jar` (from OpenJDK) as its class library. 21 | 22 | # My dev environment 23 | * Ubuntu 18.04 24 | * Java 1.8.0_172 25 | * Rust 1.27.0 26 | 27 | # Get and Build jvm-rs 28 | Ensure your Java version is 1.8.0_172 and JAVA_HOME env was set 29 | ```sh 30 | git clone https://github.com/standbyme/jvm-rs.git 31 | cd jvm-rs 32 | cargo test 33 | ``` 34 | -------------------------------------------------------------------------------- /src/rtda/heap/method.rs: -------------------------------------------------------------------------------- 1 | use classfile::attribute_info::AttributeInfo; 2 | use classfile::member_info::MemberInfo; 3 | use rtda::heap::class_member::ClassMember; 4 | use std::rc::Rc; 5 | 6 | #[derive(Debug)] 7 | pub struct Method { 8 | class_member: ClassMember, 9 | pub max_locals: usize, 10 | pub max_stack: usize, 11 | pub code: Rc>, 12 | } 13 | 14 | impl Method { 15 | pub fn new(member_info: MemberInfo) -> Method { 16 | let class_member = ClassMember::new(&member_info); 17 | let code_attribute = member_info.code_attribute(); 18 | match code_attribute { 19 | Some(AttributeInfo::Code { 20 | max_stack, 21 | max_locals, 22 | code, 23 | .. 24 | }) => Method { 25 | class_member, 26 | max_stack: *max_stack as usize, 27 | max_locals: *max_locals as usize, 28 | code: Rc::clone(code), 29 | }, 30 | None => Method { 31 | class_member, 32 | max_stack: 0, 33 | max_locals: 1, 34 | code: Rc::new(Vec::new()), 35 | }, 36 | _ => panic!(), 37 | } 38 | } 39 | pub fn is_static(&self) -> bool { 40 | self.class_member.is_static() 41 | } 42 | 43 | pub fn name(&self) -> &str { 44 | &self.class_member.name 45 | } 46 | 47 | pub fn descriptor(&self) -> &str { 48 | &self.class_member.descriptor 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | language: rust 3 | 4 | rust: 5 | - stable 6 | 7 | os: 8 | - osx 9 | - linux 10 | 11 | 12 | cache: 13 | cargo: true 14 | apt: true 15 | 16 | addons: 17 | apt: 18 | packages: 19 | - libcurl4-openssl-dev 20 | - libelf-dev 21 | - libdw-dev 22 | - cmake 23 | - gcc 24 | - binutils-dev 25 | - libiberty-dev 26 | - xvfb 27 | 28 | # Start Xvfb so that we could have a virtual graphics driver. 29 | before_script: 30 | - | 31 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 32 | export DISPLAY=:99.0 && 33 | sh -e /etc/init.d/xvfb start && 34 | sleep 3; # give xvfb some time to start 35 | fi 36 | 37 | script: 38 | - cargo build --all --verbose 39 | - cargo test --all --verbose 40 | - cargo build --all --verbose --no-default-features 41 | - cargo test --all --verbose --no-default-features 42 | 43 | after_success: | 44 | if [[ $TRAVIS_OS_NAME == 'linux' ]]; then 45 | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && 46 | tar xzf master.tar.gz && 47 | cd kcov-master && 48 | mkdir build && 49 | cd build && 50 | cmake .. && 51 | make && 52 | make install DESTDIR=../../kcov-build && 53 | cd ../.. && 54 | rm -rf kcov-master && 55 | for file in target/debug/*-*[^\.d]; do mkdir -p "target/cov/tests_$(basename $file)"; ./kcov-build/usr/local/bin/kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/tests_$(basename $file)" "$file"; done && 56 | bash <(curl -s https://codecov.io/bash); 57 | fi -------------------------------------------------------------------------------- /src/instruction/load/iload.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | fn _iload(frame: Frame, index: usize) -> Frame { 7 | let Frame { 8 | operand_stack, 9 | local_vars, 10 | method, 11 | class, 12 | } = frame; 13 | let val = local_vars.get_int(index); 14 | let operand_stack = operand_stack.push_int(val); 15 | Frame { 16 | class, 17 | operand_stack, 18 | local_vars, 19 | method, 20 | } 21 | } 22 | 23 | #[allow(non_snake_case)] 24 | pub fn ILOAD_0(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 25 | println!("ILOAD_0"); 26 | let (frame, thread) = thread.pop_frame(); 27 | 28 | let frame = _iload(frame, 0); 29 | let thread = thread.push_frame(frame); 30 | let execute_result = ExecuteResult { thread, offset: 0 }; 31 | (execute_result, code_reader) 32 | } 33 | 34 | #[allow(non_snake_case)] 35 | pub fn ILOAD_1(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 36 | println!("ILOAD_1"); 37 | let (frame, thread) = thread.pop_frame(); 38 | 39 | let frame = _iload(frame, 1); 40 | let thread = thread.push_frame(frame); 41 | let execute_result = ExecuteResult { thread, offset: 0 }; 42 | (execute_result, code_reader) 43 | } 44 | 45 | #[allow(non_snake_case)] 46 | pub fn ILOAD_2(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 47 | println!("ILOAD_2"); 48 | let (frame, thread) = thread.pop_frame(); 49 | 50 | let frame = _iload(frame, 2); 51 | let thread = thread.push_frame(frame); 52 | let execute_result = ExecuteResult { thread, offset: 0 }; 53 | (execute_result, code_reader) 54 | } 55 | -------------------------------------------------------------------------------- /src/util/code_reader.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | 3 | use self::byteorder::{BigEndian, ByteOrder}; 4 | use std; 5 | use std::rc::Rc; 6 | 7 | pub struct CodeReader { 8 | code: Rc>, 9 | pub pc: usize, 10 | } 11 | 12 | impl CodeReader { 13 | pub fn new(code: Rc>) -> CodeReader { 14 | CodeReader { code, pc: 0 } 15 | } 16 | 17 | pub fn set_pc(self, pc: usize) -> CodeReader { 18 | let CodeReader { pc: _, code } = self; 19 | CodeReader { code, pc } 20 | } 21 | 22 | pub fn read_u8(self) -> (u8, CodeReader) { 23 | let CodeReader { pc, code } = self; 24 | let val = code[pc]; 25 | let pc = pc + 1; 26 | let code_reader = CodeReader { pc, code }; 27 | (val, code_reader) 28 | } 29 | 30 | pub fn read_i8(self) -> (i8, CodeReader) { 31 | let CodeReader { pc, code } = self; 32 | let v = code[pc]; 33 | let val = unsafe { std::mem::transmute::(v) }; 34 | let pc = pc + 1; 35 | let code_reader = CodeReader { pc, code }; 36 | (val, code_reader) 37 | } 38 | 39 | pub fn read_u16(self) -> (u16, CodeReader) { 40 | let CodeReader { pc, code } = self; 41 | let val = { 42 | let seq = &code[pc..(pc + 2)]; 43 | BigEndian::read_u16(&seq) 44 | }; 45 | let pc = pc + 2; 46 | let code_reader = CodeReader { pc, code }; 47 | (val, code_reader) 48 | } 49 | 50 | pub fn read_i16(self) -> (i16, CodeReader) { 51 | let CodeReader { pc, code } = self; 52 | 53 | let val = { 54 | let seq = &code[pc..(pc + 2)]; 55 | BigEndian::read_i16(&seq) 56 | }; 57 | let pc = pc + 2; 58 | let code_reader = CodeReader { pc, code }; 59 | (val, code_reader) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate jvm; 2 | 3 | use jvm::classpath::classpath::parse; 4 | use jvm::instruction::instruction; 5 | use jvm::instruction::instruction::ExecuteResult; 6 | use jvm::rtda::frame::Frame; 7 | use jvm::rtda::heap::class::Class; 8 | use jvm::rtda::heap::class_loader::ClassLoader; 9 | use jvm::rtda::heap::method::Method; 10 | use jvm::rtda::thread::Thread; 11 | use jvm::shell::command::Command; 12 | use std::rc::Rc; 13 | 14 | fn main() { 15 | let class_name = "src.test_data.FibonacciTest"; 16 | let class_name = class_name.replace('.', "/"); 17 | let command = Command { 18 | class_name, 19 | cp_opt: None, 20 | jre_opt: None, 21 | args: vec![], 22 | }; 23 | start_jvm(command); 24 | } 25 | 26 | fn start_jvm(command: Command) { 27 | let class_path = parse(command.jre_opt, command.cp_opt); 28 | let class_loader = ClassLoader::new(class_path); 29 | let (main_class, _) = class_loader.load(command.class_name); 30 | let main_method = main_class.main_method(); 31 | interpret(main_class, main_method) 32 | } 33 | 34 | fn interpret(class: Rc, method: Rc) { 35 | let thread = Thread::new(); 36 | let frame = Frame::new(class, method); 37 | let thread = thread.push_frame(frame); 38 | execute(thread); 39 | } 40 | 41 | fn execute(thread: Thread) { 42 | let mut mut_pc = 0usize; 43 | let mut mut_thread = thread; 44 | while !mut_thread.is_stack_empty() { 45 | let thread = mut_thread; 46 | let pc = mut_pc; 47 | 48 | let (execute_result, after_execute) = instruction::execute(mut_pc, thread); 49 | let ExecuteResult { thread, offset } = execute_result; 50 | 51 | mut_pc = match offset { 52 | 0 => after_execute.pc, 53 | i => (pc as isize + i) as usize, 54 | }; 55 | mut_thread = thread; 56 | 57 | println!("pc: {}", pc); 58 | println!("offset: {}", offset); 59 | println!("mut_pc: {}", mut_pc); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/instruction/constant/ldc.rs: -------------------------------------------------------------------------------- 1 | use classfile::constant_info::ConstantInfo; 2 | use instruction::instruction::ExecuteResult; 3 | use rtda::frame::Frame; 4 | use rtda::thread::Thread; 5 | use util::code_reader::CodeReader; 6 | 7 | #[allow(non_snake_case)] 8 | pub fn LDC(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 9 | println!("LDC"); 10 | let (index, code_reader) = code_reader.read_u8(); 11 | let (frame, thread) = thread.pop_frame(); 12 | let Frame { 13 | operand_stack, 14 | local_vars, 15 | method, 16 | class, 17 | } = frame; 18 | let class_copy = class.clone(); 19 | let constant_info = class_copy.constant_pool.get(index as usize); 20 | let operand_stack = match constant_info { 21 | ConstantInfo::Integer(val) => operand_stack.push_int(*val), 22 | ConstantInfo::Float(val) => operand_stack.push_float(*val), 23 | _ => panic!("TODO: LDC"), 24 | }; 25 | let frame = Frame { 26 | class, 27 | operand_stack, 28 | local_vars, 29 | method, 30 | }; 31 | let thread = thread.push_frame(frame); 32 | let execute_result = ExecuteResult { thread, offset: 0 }; 33 | (execute_result, code_reader) 34 | } 35 | 36 | #[allow(non_snake_case)] 37 | pub fn LDC2_W(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 38 | println!("LDC2_W"); 39 | let (index, code_reader) = code_reader.read_u16(); 40 | let (frame, thread) = thread.pop_frame(); 41 | let Frame { 42 | operand_stack, 43 | local_vars, 44 | method, 45 | class, 46 | } = frame; 47 | let class_copy = class.clone(); 48 | let constant_info = class_copy.constant_pool.get(index as usize); 49 | let operand_stack = match constant_info { 50 | ConstantInfo::Long(val) => operand_stack.push_long(*val), 51 | ConstantInfo::Double(val) => operand_stack.push_double(*val), 52 | _ => panic!("java.lang.ClassFormatError"), 53 | }; 54 | let frame = Frame { 55 | class, 56 | operand_stack, 57 | local_vars, 58 | method, 59 | }; 60 | let thread = thread.push_frame(frame); 61 | let execute_result = ExecuteResult { thread, offset: 0 }; 62 | (execute_result, code_reader) 63 | } 64 | -------------------------------------------------------------------------------- /src/rtda/operand_stack.rs: -------------------------------------------------------------------------------- 1 | use rtda::slot::Slot; 2 | use util::converter; 3 | 4 | #[derive(Debug)] 5 | pub struct OperandStack { 6 | vec: Vec, 7 | } 8 | 9 | impl OperandStack { 10 | pub fn new(max_stack: usize) -> OperandStack { 11 | OperandStack { 12 | vec: Vec::with_capacity(max_stack), 13 | } 14 | } 15 | 16 | pub fn push_int(mut self, val: i32) -> OperandStack { 17 | self.vec.push(Slot::Num(val)); 18 | self 19 | } 20 | 21 | pub fn pop_int(mut self) -> (i32, OperandStack) { 22 | let val = self.vec.pop().unwrap(); 23 | match val { 24 | Slot::Num(val) => (val, self), 25 | _ => panic!(), 26 | } 27 | } 28 | 29 | pub fn push_long(mut self, val: i64) -> OperandStack { 30 | let [a, b] = converter::i64_to_i32seq(val); 31 | self.vec.push(Slot::Num(a)); 32 | self.vec.push(Slot::Num(b)); 33 | self 34 | } 35 | 36 | pub fn pop_long(mut self) -> (i64, OperandStack) { 37 | let b = match self.vec.pop().unwrap() { 38 | Slot::Num(val) => val, 39 | _ => panic!(), 40 | }; 41 | let a = match self.vec.pop().unwrap() { 42 | Slot::Num(val) => val, 43 | _ => panic!(), 44 | }; 45 | (converter::i32seq_to_i64([a, b]), self) 46 | } 47 | 48 | pub fn push_double(mut self, val: f64) -> OperandStack { 49 | let [a, b] = converter::f64_to_i32seq(val); 50 | self.vec.push(Slot::Num(a)); 51 | self.vec.push(Slot::Num(b)); 52 | self 53 | } 54 | 55 | pub fn pop_double(mut self) -> (f64, OperandStack) { 56 | let b = match self.vec.pop().unwrap() { 57 | Slot::Num(val) => val, 58 | _ => panic!(), 59 | }; 60 | let a = match self.vec.pop().unwrap() { 61 | Slot::Num(val) => val, 62 | _ => panic!(), 63 | }; 64 | (converter::i32seq_to_f64([a, b]), self) 65 | } 66 | 67 | pub fn push_float(mut self, val: f32) -> OperandStack { 68 | let val = converter::f32_to_i32(val); 69 | self.vec.push(Slot::Num(val)); 70 | self 71 | } 72 | 73 | pub fn pop_float(mut self) -> (f32, OperandStack) { 74 | let val = self.vec.pop().unwrap(); 75 | match val { 76 | Slot::Num(val) => (converter::i32_to_f32(val), self), 77 | _ => panic!(), 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/instruction/instruction.rs: -------------------------------------------------------------------------------- 1 | use instruction::comparison::dcmp::*; 2 | use instruction::comparison::fcmp::*; 3 | use instruction::comparison::if_icmp::*; 4 | use instruction::comparison::ifcond::*; 5 | use instruction::comparison::lcmp::*; 6 | use instruction::constant::ldc::*; 7 | use instruction::constant::nop::NOP; 8 | use instruction::constant::xconst::*; 9 | use instruction::constant::xipush::*; 10 | use instruction::control::goto::*; 11 | use instruction::load::iload::*; 12 | use instruction::math::add::*; 13 | use instruction::math::and::*; 14 | use instruction::math::inc::*; 15 | use instruction::math::mul::*; 16 | use instruction::math::neg::*; 17 | use instruction::store::istore::*; 18 | use rtda::thread::Thread; 19 | use util::code_reader::CodeReader; 20 | 21 | pub struct ExecuteResult { 22 | pub thread: Thread, 23 | pub offset: isize, 24 | } 25 | 26 | pub fn execute(pc: usize, thread: Thread) -> (ExecuteResult, CodeReader) { 27 | let (frame, thread) = thread.pop_frame(); 28 | let code = frame.method.clone().code.clone(); 29 | let code_reader = CodeReader::new(code).set_pc(pc); 30 | let (opcode, after_opcode) = code_reader.read_u8(); 31 | 32 | let instruction = match opcode { 33 | 0x00 => NOP, 34 | 0x02 => ICONST_M1, 35 | 0x03 => ICONST_0, 36 | 0x04 => ICONST_1, 37 | 0x05 => ICONST_2, 38 | 0x06 => ICONST_3, 39 | 0x07 => ICONST_4, 40 | 0x08 => ICONST_5, 41 | 0x09 => LCONST_0, 42 | 0x0A => LCONST_1, 43 | 0x0B => FCONST_0, 44 | 0x0C => FCONST_1, 45 | 0x0D => FCONST_2, 46 | 0x0E => DCONST_0, 47 | 0x0F => DCONST_1, 48 | 0x10 => BIPUSH, 49 | 0x12 => LDC, 50 | 0x14 => LDC2_W, 51 | 0x1B => ILOAD_1, 52 | 0x1C => ILOAD_2, 53 | 0x3C => ISTORE_1, 54 | 0x3D => ISTORE_2, 55 | 0x60 => IADD, 56 | 0x68 => IMUL, 57 | 0x69 => LMUL, 58 | 0x6A => FMUL, 59 | 0x6B => DMUL, 60 | 0x74 => INEG, 61 | 0x75 => LNEG, 62 | 0x76 => FNEG, 63 | 0x77 => DNEG, 64 | 0x7E => IAND, 65 | 0x7F => LAND, 66 | 0x84 => IINC, 67 | 0x94 => LCMP, 68 | 0x95 => FCMPL, 69 | 0x96 => FCMPG, 70 | 0x97 => DCMPL, 71 | 0x98 => DCMPG, 72 | 0x99 => IFEQ, 73 | 0x9A => IFNE, 74 | 0x9B => IFLT, 75 | 0x9C => IFGE, 76 | 0x9D => IFGT, 77 | 0x9E => IFLE, 78 | 0x9F => IF_ICMPEQ, 79 | 0xA0 => IF_ICMPNE, 80 | 0xA1 => IF_ICMPLT, 81 | 0xA2 => IF_ICMPGE, 82 | 0xA3 => IF_ICMPGT, 83 | 0xA4 => IF_ICMPLE, 84 | 0xA7 => GOTO, 85 | _ => { 86 | println!("{:?}", frame); 87 | panic!("Unsupported opcode : {:X}", opcode) 88 | } 89 | }; 90 | 91 | let thread = thread.push_frame(frame); 92 | instruction(after_opcode, thread) 93 | } 94 | -------------------------------------------------------------------------------- /src/rtda/frame.rs: -------------------------------------------------------------------------------- 1 | use rtda::heap::class::Class; 2 | use rtda::heap::method::Method; 3 | use rtda::operand_stack::OperandStack; 4 | use rtda::vars::Vars; 5 | use std::rc::Rc; 6 | 7 | #[derive(Debug)] 8 | pub struct Frame { 9 | pub local_vars: Vars, 10 | pub operand_stack: OperandStack, 11 | pub method: Rc, 12 | pub class: Rc, 13 | } 14 | 15 | impl Frame { 16 | pub fn new(class: Rc, method: Rc) -> Frame { 17 | let Method { 18 | max_stack, 19 | max_locals, 20 | .. 21 | } = *method; 22 | 23 | let local_vars = Vars::new(max_locals); 24 | let operand_stack = OperandStack::new(max_stack); 25 | Frame { 26 | class, 27 | local_vars, 28 | operand_stack, 29 | method, 30 | } 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use classfile::constant_pool::ConstantPool; 37 | use classfile::member_info::MemberInfo; 38 | use rtda::frame::Frame; 39 | use rtda::heap::class::Class; 40 | use rtda::heap::method::Method; 41 | use rtda::operand_stack::OperandStack; 42 | use rtda::vars::Vars; 43 | use std::rc::Rc; 44 | use vec_map::VecMap; 45 | 46 | #[test] 47 | fn frame() { 48 | let method = Rc::new(Method::new(MemberInfo { 49 | access_flags: 0u16, 50 | name: "".to_string(), 51 | name_index: 0u16, 52 | descriptor_index: 0u16, 53 | descriptor: "".to_string(), 54 | attributes: Vec::new(), 55 | })); 56 | let class = Rc::new(Class { 57 | access_flags: 0u16, 58 | name: "".to_string(), 59 | constant_pool: ConstantPool { 60 | vec_map: VecMap::new(), 61 | }, 62 | fields: Vec::new(), 63 | methods: Vec::new(), 64 | super_class: None, 65 | instance_slot_count: 0usize, 66 | static_slot_count: 0usize, 67 | static_vars: Vars::new(2), 68 | }); 69 | let frame = Frame::new(class, method); 70 | local_vars(frame.local_vars); 71 | operand_stack(frame.operand_stack); 72 | } 73 | 74 | fn local_vars(local_vars: Vars) { 75 | let local_vars = local_vars.set_int(0, 100); 76 | let local_vars = local_vars.set_int(1, -100); 77 | assert_eq!(local_vars.get_int(0), 100); 78 | assert_eq!(local_vars.get_int(1), -100); 79 | } 80 | 81 | fn operand_stack(operand_stack: OperandStack) { 82 | let operand_stack = operand_stack.push_int(100); 83 | let operand_stack = operand_stack.push_double(2.71828182845f64); 84 | let operand_stack = operand_stack.push_int(-100); 85 | let operand_stack = operand_stack.push_long(2997924580); 86 | let operand_stack = operand_stack.push_float(3.1415926); 87 | let (val, operand_stack) = operand_stack.pop_float(); 88 | assert_eq!(val, 3.1415926); 89 | let (val, operand_stack) = operand_stack.pop_long(); 90 | assert_eq!(val, 2997924580); 91 | let (val, operand_stack) = operand_stack.pop_int(); 92 | assert_eq!(val, -100); 93 | let (val, operand_stack) = operand_stack.pop_double(); 94 | assert_eq!(val, 2.71828182845f64); 95 | let (val, _) = operand_stack.pop_int(); 96 | assert_eq!(val, 100); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/instruction/comparison/lcmp.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn LCMP(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | let (frame, thread) = thread.pop_frame(); 9 | let Frame { 10 | operand_stack, 11 | local_vars, 12 | method, 13 | class, 14 | } = frame; 15 | 16 | let (val2, operand_stack) = operand_stack.pop_long(); 17 | let (val1, operand_stack) = operand_stack.pop_long(); 18 | 19 | let operand_stack = if val1 < val2 { 20 | operand_stack.push_int(-1) 21 | } else if val1 > val2 { 22 | operand_stack.push_int(1) 23 | } else { 24 | operand_stack.push_int(0) 25 | }; 26 | 27 | let frame = Frame { 28 | class, 29 | operand_stack, 30 | local_vars, 31 | method, 32 | }; 33 | let thread = thread.push_frame(frame); 34 | let execute_result = ExecuteResult { thread, offset: 0 }; 35 | (execute_result, code_reader) 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use classfile::constant_pool::ConstantPool; 41 | use classfile::member_info::MemberInfo; 42 | use instruction::comparison::lcmp::*; 43 | use instruction::instruction::ExecuteResult; 44 | use rtda::frame::Frame; 45 | use rtda::heap::class::Class; 46 | use rtda::heap::method::Method; 47 | use rtda::operand_stack::OperandStack; 48 | use rtda::thread::Thread; 49 | use rtda::vars::Vars; 50 | use std::rc::Rc; 51 | use util::code_reader::CodeReader; 52 | use vec_map::VecMap; 53 | 54 | #[test] 55 | #[allow(non_snake_case)] 56 | fn test_LCMP_gt() { 57 | let frame = create_frame(9223372036854775807i64, 9223372036854775806i64); 58 | let thread = Thread::new().push_frame(frame); 59 | let (ExecuteResult { thread, offset: _ }, _) = 60 | LCMP(CodeReader::new(Rc::new(vec![])), thread); 61 | let (frame, _) = thread.pop_frame(); 62 | let (val, _) = frame.operand_stack.pop_int(); 63 | assert_eq!(val, 1); 64 | } 65 | 66 | #[test] 67 | #[allow(non_snake_case)] 68 | fn test_LCMP_lt() { 69 | let frame = create_frame(-9223372036854775806i64, 9223372036854775807i64); 70 | let thread = Thread::new().push_frame(frame); 71 | let (ExecuteResult { thread, offset: _ }, _) = 72 | LCMP(CodeReader::new(Rc::new(vec![])), thread); 73 | let (frame, _) = thread.pop_frame(); 74 | let (val, _) = frame.operand_stack.pop_int(); 75 | assert_eq!(val, -1); 76 | } 77 | 78 | #[test] 79 | #[allow(non_snake_case)] 80 | fn test_LCMP_eq() { 81 | let frame = create_frame(-9223372036854775806i64, -9223372036854775806i64); 82 | let thread = Thread::new().push_frame(frame); 83 | let (ExecuteResult { thread, offset: _ }, _) = 84 | LCMP(CodeReader::new(Rc::new(vec![])), thread); 85 | let (frame, _) = thread.pop_frame(); 86 | let (val, _) = frame.operand_stack.pop_int(); 87 | assert_eq!(val, 0); 88 | } 89 | 90 | fn create_frame(op1: i64, op2: i64) -> Frame { 91 | let operand_stack = OperandStack::new(10); 92 | let operand_stack = operand_stack.push_long(op1); 93 | let operand_stack = operand_stack.push_long(op2); 94 | let method = Rc::new(Method::new(MemberInfo { 95 | access_flags: 0u16, 96 | name: "".to_string(), 97 | name_index: 0u16, 98 | descriptor_index: 0u16, 99 | descriptor: "".to_string(), 100 | attributes: vec![], 101 | })); 102 | let class = Rc::new(Class { 103 | access_flags: 0u16, 104 | name: "".to_string(), 105 | constant_pool: ConstantPool { 106 | vec_map: VecMap::new(), 107 | }, 108 | fields: Vec::new(), 109 | methods: Vec::new(), 110 | super_class: None, 111 | instance_slot_count: 0usize, 112 | static_slot_count: 0usize, 113 | static_vars: Vars::new(2), 114 | }); 115 | Frame { 116 | class, 117 | local_vars: Vars::new(10), 118 | operand_stack, 119 | method, 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/util/modified_utf8.rs: -------------------------------------------------------------------------------- 1 | //! From [rust-jvm](https://github.com/maxmcc/rust-jvm/) 2 | //! Modified UTF-8 string slices. 3 | //! 4 | //! See [§4.4.7](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.7). 5 | 6 | use std::char; 7 | 8 | /// Errors which can occur when attempting to interpret a sequence of `u8` as a modified UTF-8 9 | /// string. 10 | #[derive(Debug)] 11 | pub struct ModifiedUtf8Error { 12 | valid_up_to: usize, 13 | } 14 | 15 | /// Converts a slice of bytes in modified UTF-8 encoding to a string slice. 16 | pub fn from_modified_utf8(bytes: &[u8]) -> Result { 17 | // Refer to §4.4.7 for more information about the modified UTF-8 encoding. 18 | let mut result = String::new(); 19 | let mut offset = 0; 20 | let len = bytes.len(); 21 | while offset < len { 22 | let old_offset = offset; 23 | macro_rules! err { 24 | () => { 25 | return Err(ModifiedUtf8Error { 26 | valid_up_to: old_offset, 27 | }); 28 | }; 29 | } 30 | macro_rules! next { 31 | () => {{ 32 | offset += 1; 33 | if offset >= len { 34 | err!(); 35 | } 36 | bytes[offset] 37 | }}; 38 | } 39 | 40 | let x = bytes[offset]; 41 | if x < 0b0111_1111 { 42 | // pattern: 0xxxxxx 43 | result.push(x as char); 44 | } else if x < 0b1101_1111 { 45 | // pattern: 110xxxxx 46 | let y = next!(); 47 | if y < 0b1011_111 { 48 | // pattern: 10xxxxxx 49 | let c = ((x & 0x1f) << 6) + (y & 0x3f); 50 | result.push(c as char); 51 | } else { 52 | err!() 53 | } 54 | } else if x < 0b1110_1111 { 55 | // pattern: 1110xxxx 56 | let y = next!(); 57 | if y < 0b1011_1111 { 58 | // pattern: 10xxxxxx 59 | let z = next!(); 60 | if z < 0b1011_1111 { 61 | // pattern: 10xxxxxx 62 | let q: u32 = (((x & 0xf) as u32) << 12) 63 | + (((y & 0x3f) as u32) << 6) 64 | + ((z & 0x3f) as u32); 65 | let c = unsafe { char::from_u32_unchecked(q) }; 66 | result.push(c); 67 | } else { 68 | err!() 69 | } 70 | } else { 71 | err!() 72 | } 73 | } else if x == 0b1110_1101 { 74 | // pattern: 11101101 75 | let v = next!(); 76 | if v < 0b1010_1111 { 77 | // pattern: 10101111 78 | let w = next!(); 79 | if w < 0b1011_1111 { 80 | // pattern: 10xxxxxx 81 | let xx = next!(); 82 | if xx == 0b1110_1101 { 83 | // pattern: 11101101 84 | let y = next!(); 85 | if y < 0b1011_1111 { 86 | // pattern: 1011xxxx 87 | let z = next!(); 88 | if z < 0b1011_1111 { 89 | // pattern: 10xxxxxx 90 | let q: u32 = 0x10000u32 91 | + (((v & 0x0f) as u32) << 16) 92 | + (((w & 0x3f) as u32) << 10) 93 | + (((y & 0x0f) as u32) << 6) 94 | + ((z & 0x3f) as u32); 95 | let c = unsafe { char::from_u32_unchecked(q) }; 96 | result.push(c); 97 | } else { 98 | err!() 99 | } 100 | } else { 101 | err!() 102 | } 103 | } else { 104 | err!() 105 | } 106 | } else { 107 | err!() 108 | } 109 | } else { 110 | err!() 111 | } 112 | } else { 113 | err!() 114 | } 115 | 116 | offset += 1; 117 | } 118 | Ok(result) 119 | } 120 | -------------------------------------------------------------------------------- /src/instruction/comparison/dcmp.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | fn _dcmp(frame: Frame, flag: bool) -> (f64, f64, Frame) { 8 | let Frame { 9 | operand_stack, 10 | local_vars, 11 | method, 12 | class, 13 | } = frame; 14 | 15 | let (val2, operand_stack) = operand_stack.pop_double(); 16 | let (val1, operand_stack) = operand_stack.pop_double(); 17 | 18 | let operand_stack = if val1 > val2 { 19 | operand_stack.push_int(1) 20 | } else if val1 < val2 { 21 | operand_stack.push_int(-1) 22 | } else if val1 == val2 { 23 | operand_stack.push_int(0) 24 | } else if flag { 25 | operand_stack.push_int(1) 26 | } else { 27 | operand_stack.push_int(-1) 28 | }; 29 | 30 | let frame = Frame { 31 | class, 32 | operand_stack, 33 | local_vars, 34 | method, 35 | }; 36 | (val1, val2, frame) 37 | } 38 | 39 | #[allow(non_snake_case)] 40 | pub fn DCMPG(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 41 | println!("DCMPG"); 42 | 43 | let (frame, thread) = thread.pop_frame(); 44 | let (_, _, frame) = _dcmp(frame, true); 45 | let thread = thread.push_frame(frame); 46 | let execute_result = ExecuteResult { thread, offset: 0 }; 47 | (execute_result, code_reader) 48 | } 49 | 50 | #[allow(non_snake_case)] 51 | pub fn DCMPL(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 52 | println!("DCMPG"); 53 | 54 | let (frame, thread) = thread.pop_frame(); 55 | let (_, _, frame) = _dcmp(frame, false); 56 | let thread = thread.push_frame(frame); 57 | let execute_result = ExecuteResult { thread, offset: 0 }; 58 | (execute_result, code_reader) 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use classfile::constant_pool::ConstantPool; 64 | use classfile::member_info::MemberInfo; 65 | use instruction::comparison::dcmp::*; 66 | use instruction::instruction::ExecuteResult; 67 | use rtda::frame::Frame; 68 | use rtda::heap::class::Class; 69 | use rtda::heap::method::Method; 70 | use rtda::operand_stack::OperandStack; 71 | use rtda::thread::Thread; 72 | use rtda::vars::Vars; 73 | use std::rc::Rc; 74 | use util::code_reader::CodeReader; 75 | use vec_map::VecMap; 76 | 77 | #[test] 78 | #[allow(non_snake_case)] 79 | fn test_DCMPL() { 80 | let frame = create_frame(1.48, 1.49); 81 | let thread = Thread::new().push_frame(frame); 82 | let (ExecuteResult { thread, offset: _ }, _) = 83 | DCMPL(CodeReader::new(Rc::new(vec![])), thread); 84 | let (frame, _) = thread.pop_frame(); 85 | let (val, _) = frame.operand_stack.pop_int(); 86 | assert_eq!(val, -1); 87 | } 88 | 89 | #[test] 90 | #[allow(non_snake_case)] 91 | fn test_DCMPG() { 92 | let frame = create_frame(1.49, 1.48); 93 | let thread = Thread::new().push_frame(frame); 94 | let (ExecuteResult { thread, offset: _ }, _) = 95 | DCMPG(CodeReader::new(Rc::new(vec![])), thread); 96 | let (frame, _) = thread.pop_frame(); 97 | let (val, _) = frame.operand_stack.pop_int(); 98 | assert_eq!(val, 1); 99 | } 100 | 101 | #[test] 102 | #[allow(non_snake_case)] 103 | fn test_DCMPG_equal() { 104 | let frame = create_frame(1.49, 1.49); 105 | let thread = Thread::new().push_frame(frame); 106 | let (ExecuteResult { thread, offset: _ }, _) = 107 | DCMPG(CodeReader::new(Rc::new(vec![])), thread); 108 | let (frame, _) = thread.pop_frame(); 109 | let (val, _) = frame.operand_stack.pop_int(); 110 | assert_eq!(val, 0); 111 | } 112 | 113 | fn create_frame(op1: f64, op2: f64) -> Frame { 114 | let operand_stack = OperandStack::new(10); 115 | let operand_stack = operand_stack.push_double(op1); 116 | let operand_stack = operand_stack.push_double(op2); 117 | let method = Rc::new(Method::new(MemberInfo { 118 | access_flags: 0u16, 119 | name: "".to_string(), 120 | name_index: 0u16, 121 | descriptor_index: 0u16, 122 | descriptor: "".to_string(), 123 | attributes: vec![], 124 | })); 125 | let class = Rc::new(Class { 126 | access_flags: 0u16, 127 | name: "".to_string(), 128 | constant_pool: ConstantPool { 129 | vec_map: VecMap::new(), 130 | }, 131 | fields: Vec::new(), 132 | methods: Vec::new(), 133 | super_class: None, 134 | instance_slot_count: 0usize, 135 | static_slot_count: 0usize, 136 | static_vars: Vars::new(2), 137 | }); 138 | Frame { 139 | class, 140 | local_vars: Vars::new(10), 141 | operand_stack: operand_stack, 142 | method, 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/instruction/comparison/fcmp.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | fn _fcmp(frame: Frame, flag: bool) -> (f32, f32, Frame) { 8 | let Frame { 9 | operand_stack, 10 | local_vars, 11 | method, 12 | class, 13 | } = frame; 14 | 15 | let (val2, operand_stack) = operand_stack.pop_float(); 16 | let (val1, operand_stack) = operand_stack.pop_float(); 17 | 18 | let operand_stack = if val1 < val2 { 19 | operand_stack.push_int(-1) 20 | } else if val1 > val2 { 21 | operand_stack.push_int(1) 22 | } else if val1 == val2 { 23 | operand_stack.push_int(0) 24 | } else if flag { 25 | operand_stack.push_int(1) 26 | } else { 27 | operand_stack.push_int(-1) 28 | }; 29 | 30 | let frame = Frame { 31 | class, 32 | operand_stack, 33 | local_vars, 34 | method, 35 | }; 36 | (val1, val2, frame) 37 | } 38 | 39 | #[allow(non_snake_case)] 40 | pub fn FCMPG(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 41 | println!("FCMPG"); 42 | 43 | let (frame, thread) = thread.pop_frame(); 44 | let (_, _, frame) = _fcmp(frame, true); 45 | let thread = thread.push_frame(frame); 46 | let execute_result = ExecuteResult { thread, offset: 0 }; 47 | (execute_result, code_reader) 48 | } 49 | 50 | #[allow(non_snake_case)] 51 | pub fn FCMPL(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 52 | println!("FCMPL"); 53 | 54 | let (frame, thread) = thread.pop_frame(); 55 | let (_, _, frame) = _fcmp(frame, false); 56 | let thread = thread.push_frame(frame); 57 | let execute_result = ExecuteResult { thread, offset: 0 }; 58 | (execute_result, code_reader) 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use classfile::constant_pool::ConstantPool; 64 | use classfile::member_info::MemberInfo; 65 | use instruction::comparison::fcmp::*; 66 | use instruction::instruction::ExecuteResult; 67 | use rtda::frame::Frame; 68 | use rtda::heap::class::Class; 69 | use rtda::heap::method::Method; 70 | use rtda::operand_stack::OperandStack; 71 | use rtda::thread::Thread; 72 | use rtda::vars::Vars; 73 | use std::rc::Rc; 74 | use util::code_reader::CodeReader; 75 | use vec_map::VecMap; 76 | 77 | #[test] 78 | #[allow(non_snake_case)] 79 | fn test_FCMPL() { 80 | let frame = create_frame(0.03, 0.042); 81 | let thread = Thread::new().push_frame(frame); 82 | let (ExecuteResult { thread, offset: _ }, _) = 83 | FCMPL(CodeReader::new(Rc::new(vec![])), thread); 84 | let (frame, _) = thread.pop_frame(); 85 | let (val, _) = frame.operand_stack.pop_int(); 86 | assert_eq!(val, -1); 87 | } 88 | 89 | #[test] 90 | #[allow(non_snake_case)] 91 | fn test_FCMPG() { 92 | let frame = create_frame(1.21, 1.1); 93 | let thread = Thread::new().push_frame(frame); 94 | let (ExecuteResult { thread, offset: _ }, _) = 95 | FCMPG(CodeReader::new(Rc::new(vec![])), thread); 96 | let (frame, _) = thread.pop_frame(); 97 | let (val, _) = frame.operand_stack.pop_int(); 98 | assert_eq!(val, 1); 99 | } 100 | 101 | #[test] 102 | #[allow(non_snake_case)] 103 | fn test_FCMPG_equal() { 104 | let frame = create_frame(2.345, 2.345); 105 | let thread = Thread::new().push_frame(frame); 106 | 107 | let (ExecuteResult { thread, offset: _ }, _) = 108 | FCMPG(CodeReader::new(Rc::new(vec![])), thread); 109 | let (frame, _) = thread.pop_frame(); 110 | let (val, _) = frame.operand_stack.pop_int(); 111 | assert_eq!(val, 0); 112 | } 113 | 114 | fn create_frame(op1: f32, op2: f32) -> Frame { 115 | let operand_stack = OperandStack::new(10); 116 | let operand_stack = operand_stack.push_float(op1); 117 | let operand_stack = operand_stack.push_float(op2); 118 | let method = Rc::new(Method::new(MemberInfo { 119 | access_flags: 0u16, 120 | name: "".to_string(), 121 | name_index: 0u16, 122 | descriptor_index: 0u16, 123 | descriptor: "".to_string(), 124 | attributes: vec![], 125 | })); 126 | let class = Rc::new(Class { 127 | access_flags: 0u16, 128 | name: "".to_string(), 129 | constant_pool: ConstantPool { 130 | vec_map: VecMap::new(), 131 | }, 132 | fields: Vec::new(), 133 | methods: Vec::new(), 134 | super_class: None, 135 | instance_slot_count: 0usize, 136 | static_slot_count: 0usize, 137 | static_vars: Vars::new(2), 138 | }); 139 | Frame { 140 | class, 141 | local_vars: Vars::new(10), 142 | operand_stack: operand_stack, 143 | method, 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/classpath/classpath.rs: -------------------------------------------------------------------------------- 1 | extern crate zip; 2 | 3 | use std::env; 4 | use std::fs::read_dir; 5 | use std::fs::File; 6 | use std::io; 7 | use std::io::Error; 8 | use std::io::ErrorKind; 9 | use std::io::Read; 10 | use std::path::Path; 11 | use std::path::PathBuf; 12 | 13 | #[derive(Debug)] 14 | enum Entry { 15 | Dir { path: PathBuf }, 16 | Wildcard { path_vec: Vec }, 17 | Zip { path: PathBuf }, 18 | } 19 | 20 | impl Entry { 21 | fn new(path: &str) -> Entry { 22 | if path.ends_with("*") { 23 | // println!("Entry::new Wildcard {}", path); 24 | let len = path.len(); 25 | let base_path = &path[..len - 1]; 26 | // println!("base_path {:?}", base_path); 27 | let path_vec: Vec = read_dir(base_path) 28 | .unwrap() 29 | .map(|x| x.unwrap()) 30 | .map(|x| x.path()) 31 | .filter(|x| { 32 | x.extension() 33 | .map(|x| x.to_str().unwrap() == "jar") 34 | .unwrap_or(false) 35 | }) 36 | .collect(); 37 | // println!("path_vec {:?}", path_vec); 38 | 39 | Entry::Wildcard { path_vec } 40 | } else if path.ends_with(".jar") { 41 | // println!("Entry::new Zip {}", path); 42 | Entry::Zip { 43 | path: Path::new(path).to_owned(), 44 | } 45 | } else { 46 | // println!("Entry::new Dir {}", path); 47 | Entry::Dir { 48 | path: Path::new(path).to_owned(), 49 | } 50 | } 51 | } 52 | 53 | fn read_class(&self, class_file_name: &str) -> Result, io::Error> { 54 | match self { 55 | Entry::Dir { path } => { 56 | // println!("read class {} using Dir", class_file_name); 57 | let filepath = Path::new(path).join(class_file_name); 58 | let mut file = File::open(filepath)?; 59 | let meta = file.metadata()?; 60 | let mut buf = Vec::::with_capacity(meta.len() as usize); 61 | file.read_to_end(&mut buf)?; 62 | Ok(buf) 63 | } 64 | Entry::Wildcard { path_vec } => { 65 | // println!("read class {} using Wildcard", class_file_name); 66 | path_vec 67 | .iter() 68 | .map(|x| Entry::new(x.to_str().unwrap())) 69 | .map(|x| x.read_class(class_file_name)) 70 | .find(|x| x.is_ok()) 71 | .unwrap_or(Err(Error::new(ErrorKind::Other, "Class not found"))) 72 | } 73 | Entry::Zip { path } => { 74 | // println!("read class {} using Zip", class_file_name); 75 | let file = File::open(path)?; 76 | let mut zip = zip::ZipArchive::new(file)?; 77 | let mut file = zip.by_name(&class_file_name)?; 78 | let mut buf = Vec::::with_capacity(file.size() as usize); 79 | file.read_to_end(&mut buf); 80 | Ok(buf) 81 | } 82 | } 83 | } 84 | } 85 | 86 | #[derive(Debug)] 87 | pub struct ClassPath { 88 | boot: Entry, 89 | user: Entry, 90 | } 91 | 92 | impl ClassPath { 93 | pub fn read_class(&self, name: &str) -> Result, io::Error> { 94 | let class_file_name = name.to_owned() + ".class"; 95 | 96 | self.boot 97 | .read_class(&class_file_name) 98 | .or_else(|x| self.user.read_class(&class_file_name)) 99 | // .or_else(|| self.ext.read_class()) 100 | } 101 | } 102 | 103 | fn parse_boot_classpath(jre: &str) -> Entry { 104 | let jre_lib_path = Path::new(jre) 105 | .join("lib") 106 | .join("*") 107 | .to_str() 108 | .unwrap() 109 | .to_owned(); 110 | Entry::new(&jre_lib_path) 111 | } 112 | 113 | fn parse_user_classpath(cp_opt: Option) -> Entry { 114 | let cp = cp_opt.unwrap_or(".".to_owned()); 115 | Entry::new(&cp) 116 | } 117 | 118 | fn exists(path: &str) -> bool { 119 | Path::new(path).exists() 120 | } 121 | 122 | fn get_jre(jre_opt: Option) -> String { 123 | match jre_opt { 124 | Some(ref jre) if exists(jre) => jre.to_string(), 125 | _ => { 126 | if exists("./jre") { 127 | "./jre".to_string() 128 | } else { 129 | match env::var_os("JAVA_HOME") { 130 | Some(java_home) => Path::new(&java_home) 131 | .join("jre") 132 | .to_str() 133 | .unwrap() 134 | .to_string(), 135 | None => panic!("Can not find JRE folder"), 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | pub fn parse(jre_opt: Option, cp_opt: Option) -> ClassPath { 143 | let jre = get_jre(jre_opt); 144 | let boot = parse_boot_classpath(&jre); 145 | let user = parse_user_classpath(cp_opt); 146 | ClassPath { user, boot } 147 | } 148 | -------------------------------------------------------------------------------- /src/instruction/math/and.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn IAND(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | let (frame, thread) = thread.pop_frame(); 9 | 10 | let Frame { 11 | operand_stack, 12 | local_vars, 13 | method, 14 | class, 15 | } = frame; 16 | 17 | let (v2, operand_stack) = operand_stack.pop_int(); 18 | let (v1, operand_stack) = operand_stack.pop_int(); 19 | let result = v1 & v2; 20 | let operand_stack = operand_stack.push_int(result); 21 | 22 | let frame = Frame { 23 | class, 24 | operand_stack, 25 | local_vars, 26 | method, 27 | }; 28 | let thread = thread.push_frame(frame); 29 | let execute_result = ExecuteResult { thread, offset: 0 }; 30 | (execute_result, code_reader) 31 | } 32 | 33 | #[allow(non_snake_case)] 34 | pub fn LAND(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 35 | let (frame, thread) = thread.pop_frame(); 36 | 37 | let Frame { 38 | operand_stack, 39 | local_vars, 40 | method, 41 | class, 42 | } = frame; 43 | 44 | let (v2, operand_stack) = operand_stack.pop_long(); 45 | let (v1, operand_stack) = operand_stack.pop_long(); 46 | let result = v1 & v2; 47 | let operand_stack = operand_stack.push_long(result); 48 | 49 | let frame = Frame { 50 | class, 51 | operand_stack, 52 | local_vars, 53 | method, 54 | }; 55 | let thread = thread.push_frame(frame); 56 | let execute_result = ExecuteResult { thread, offset: 0 }; 57 | (execute_result, code_reader) 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use classfile::constant_pool::ConstantPool; 63 | use classfile::member_info::MemberInfo; 64 | use instruction::instruction::ExecuteResult; 65 | use instruction::math::and::*; 66 | use rtda::frame::Frame; 67 | use rtda::heap::class::Class; 68 | use rtda::heap::method::Method; 69 | use rtda::thread::Thread; 70 | use rtda::vars::Vars; 71 | use std::rc::Rc; 72 | use util::code_reader::CodeReader; 73 | use vec_map::VecMap; 74 | 75 | #[test] 76 | #[allow(non_snake_case)] 77 | fn test_IAND() { 78 | let method = Rc::new(Method::new(MemberInfo { 79 | access_flags: 0u16, 80 | name: "".to_string(), 81 | name_index: 0u16, 82 | descriptor_index: 0u16, 83 | descriptor: "".to_string(), 84 | attributes: vec![], 85 | })); 86 | let class = Rc::new(Class { 87 | access_flags: 0u16, 88 | name: "".to_string(), 89 | constant_pool: ConstantPool { 90 | vec_map: VecMap::new(), 91 | }, 92 | fields: Vec::new(), 93 | methods: Vec::new(), 94 | super_class: None, 95 | instance_slot_count: 0usize, 96 | static_slot_count: 0usize, 97 | static_vars: Vars::new(2), 98 | }); 99 | let frame = Frame::new(class, method); 100 | let Frame { 101 | operand_stack, 102 | local_vars, 103 | method, 104 | class, 105 | } = frame; 106 | 107 | let operand_stack = operand_stack.push_int(350); 108 | let operand_stack = operand_stack.push_int(678); 109 | 110 | let frame = Frame { 111 | class, 112 | operand_stack, 113 | local_vars, 114 | method, 115 | }; 116 | let thread = Thread::new().push_frame(frame); 117 | let (ExecuteResult { thread, offset: _ }, _) = 118 | IAND(CodeReader::new(Rc::new(vec![])), thread); 119 | let (frame, _) = thread.pop_frame(); 120 | let (val, _) = frame.operand_stack.pop_int(); 121 | assert_eq!(val, 6); 122 | } 123 | 124 | #[test] 125 | #[allow(non_snake_case)] 126 | fn test_LAND() { 127 | let method = Rc::new(Method::new(MemberInfo { 128 | access_flags: 0u16, 129 | name: "".to_string(), 130 | name_index: 0u16, 131 | descriptor_index: 0u16, 132 | descriptor: "".to_string(), 133 | attributes: vec![], 134 | })); 135 | let class = Rc::new(Class { 136 | access_flags: 0u16, 137 | name: "".to_string(), 138 | constant_pool: ConstantPool { 139 | vec_map: VecMap::new(), 140 | }, 141 | fields: Vec::new(), 142 | methods: Vec::new(), 143 | super_class: None, 144 | instance_slot_count: 0usize, 145 | static_slot_count: 0usize, 146 | static_vars: Vars::new(2), 147 | }); 148 | let frame = Frame::new(class, method); 149 | let Frame { 150 | operand_stack, 151 | local_vars, 152 | method, 153 | class, 154 | } = frame; 155 | 156 | let operand_stack = operand_stack.push_long(12345678969); 157 | let operand_stack = operand_stack.push_long(2997924580); 158 | 159 | let frame = Frame { 160 | class, 161 | operand_stack, 162 | local_vars, 163 | method, 164 | }; 165 | let thread = Thread::new().push_frame(frame); 166 | let (ExecuteResult { thread, offset: _ }, _) = 167 | LAND(CodeReader::new(Rc::new(vec![])), thread); 168 | let (frame, _) = thread.pop_frame(); 169 | let (val, _) = frame.operand_stack.pop_long(); 170 | assert_eq!(val, 2458914912); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/rtda/heap/class_loader.rs: -------------------------------------------------------------------------------- 1 | use classfile::class_file::ClassFile; 2 | use classfile::class_reader::ClassReader; 3 | use classfile::constant_info::ConstantInfo; 4 | use classfile::constant_pool::ConstantPool; 5 | 6 | use classpath::classpath::ClassPath; 7 | use rtda::heap::class::Class; 8 | use rtda::heap::field::Field; 9 | use rtda::heap::method::Method; 10 | 11 | use rtda::vars::Vars; 12 | use std::collections::HashMap; 13 | use std::rc::Rc; 14 | 15 | pub struct ClassLoader { 16 | class_path: ClassPath, 17 | class_map: HashMap>, 18 | } 19 | 20 | struct Acc { 21 | constant_pool: ConstantPool, 22 | next_instance_field_slot_id: usize, 23 | next_static_field_slot_id: usize, 24 | static_vars: Vars, 25 | } 26 | 27 | impl ClassLoader { 28 | pub fn new(class_path: ClassPath) -> ClassLoader { 29 | ClassLoader { 30 | class_path, 31 | class_map: HashMap::new(), 32 | } 33 | } 34 | 35 | pub fn load(self, name: String) -> (Rc, ClassLoader) { 36 | println!("load {}", &name); 37 | if self.class_map.contains_key(&name) { 38 | let class = Rc::clone(self.class_map.get(&name).unwrap()); 39 | (class, self) 40 | } else { 41 | let data = self.read(&name); 42 | let (class, mut class_loader) = ClassLoader::define(self, data); 43 | let class_copy = Rc::clone(&class); 44 | class_loader.class_map.insert(name, class); 45 | (class_copy, class_loader) 46 | } 47 | } 48 | 49 | fn read(&self, name: &str) -> Vec { 50 | self.class_path 51 | .read_class(name) 52 | .expect("java.lang.ClassNotFoundException") 53 | } 54 | 55 | fn define(class_loader: ClassLoader, data: Vec) -> (Rc, ClassLoader) { 56 | let class_file = data.parse(); 57 | let name = class_file.class_name().to_owned(); 58 | let super_class_name = class_file.super_class_name().to_owned(); 59 | let ClassFile { 60 | access_flags, 61 | methods, 62 | fields, 63 | constant_pool, 64 | .. 65 | } = class_file; 66 | 67 | let methods: Vec> = methods 68 | .into_iter() 69 | .map(|x| Rc::new(Method::new(x))) 70 | .collect(); 71 | 72 | let (super_class, class_loader) = if name != "java/lang/Object" { 73 | let (class, class_loader) = class_loader.load(super_class_name); 74 | (Some(class), class_loader) 75 | } else { 76 | (None, class_loader) 77 | }; 78 | 79 | fn fold_func(acc: Acc, field: &Field) -> Acc { 80 | let Acc { 81 | next_instance_field_slot_id: instance_field_slot_id, 82 | next_static_field_slot_id: static_field_slot_id, 83 | static_vars, 84 | constant_pool, 85 | } = acc; 86 | let slot_id_delta: usize = if field.is_long_or_double() { 2 } else { 1 }; 87 | let (next_instance_field_slot_id, next_static_field_slot_id, static_vars) = 88 | if field.is_static() { 89 | let static_vars: Vars = if field.is_final() { 90 | let constant_value_index = field.constant_value_index; 91 | if constant_value_index.is_some() { 92 | let constant_value_index = constant_value_index.unwrap(); 93 | match field.class_member.descriptor.as_str() { 94 | // todo: Complete Z B C S I J D Ljava/lang/String 95 | "F" => { 96 | let val = match constant_pool.get(constant_value_index) { 97 | ConstantInfo::Integer(val) => *val, 98 | _ => panic!("Not Integer"), 99 | }; 100 | static_vars.set_int(static_field_slot_id, val) 101 | } 102 | _ => panic!("TODO"), 103 | } 104 | } else { 105 | static_vars 106 | } 107 | } else { 108 | static_vars 109 | }; 110 | ( 111 | instance_field_slot_id, 112 | static_field_slot_id + slot_id_delta, 113 | static_vars, 114 | ) 115 | } else { 116 | ( 117 | instance_field_slot_id + slot_id_delta, 118 | static_field_slot_id, 119 | static_vars, 120 | ) 121 | }; 122 | 123 | Acc { 124 | next_instance_field_slot_id, 125 | next_static_field_slot_id, 126 | static_vars, 127 | constant_pool, 128 | } 129 | } 130 | let next_static_field_slot_id: usize = 0; 131 | let next_instance_field_slot_id: usize = super_class 132 | .clone() 133 | .map(|x| x.instance_slot_count) 134 | .unwrap_or(0); 135 | let static_vars = Vars::new(10); 136 | 137 | let fields: Vec = fields.into_iter().map(|x| Field::new(x)).collect(); 138 | let Acc { 139 | next_instance_field_slot_id: instance_slot_count, 140 | next_static_field_slot_id: static_slot_count, 141 | static_vars, 142 | constant_pool, 143 | } = fields.iter().fold( 144 | Acc { 145 | next_instance_field_slot_id, 146 | next_static_field_slot_id, 147 | constant_pool, 148 | static_vars, 149 | }, 150 | fold_func, 151 | ); 152 | let class = Rc::new(Class { 153 | access_flags, 154 | fields, 155 | name, 156 | super_class, 157 | methods, 158 | instance_slot_count, 159 | static_slot_count, 160 | static_vars, 161 | constant_pool, 162 | }); 163 | 164 | (class, class_loader) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler32" 5 | version = "1.0.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" 8 | 9 | [[package]] 10 | name = "build_const" 11 | version = "0.2.2" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" 14 | 15 | [[package]] 16 | name = "byteorder" 17 | version = "1.3.2" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" 20 | 21 | [[package]] 22 | name = "bzip2" 23 | version = "0.4.2" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "abf8012c8a15d5df745fcf258d93e6149dcf102882c8d8702d9cff778eab43a8" 26 | dependencies = [ 27 | "bzip2-sys", 28 | "libc", 29 | ] 30 | 31 | [[package]] 32 | name = "bzip2-sys" 33 | version = "0.1.6" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b" 36 | dependencies = [ 37 | "cc", 38 | "libc", 39 | ] 40 | 41 | [[package]] 42 | name = "cc" 43 | version = "1.0.18" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" 46 | 47 | [[package]] 48 | name = "cfg-if" 49 | version = "0.1.10" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 52 | 53 | [[package]] 54 | name = "crc" 55 | version = "1.8.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" 58 | dependencies = [ 59 | "build_const", 60 | ] 61 | 62 | [[package]] 63 | name = "crc32fast" 64 | version = "1.0.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "a8795e4883c14e32604fe28607ae96c921f3377d2a80c46f06a9e6e734c364f4" 67 | dependencies = [ 68 | "rustc_version", 69 | ] 70 | 71 | [[package]] 72 | name = "flate2" 73 | version = "1.0.5" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "96971e4fc2737f211ec8236fe16ac67695838ca3e25567c07b4f837d1f8f829c" 76 | dependencies = [ 77 | "flate2-crc", 78 | "libc", 79 | "miniz_oxide_c_api", 80 | ] 81 | 82 | [[package]] 83 | name = "flate2-crc" 84 | version = "0.1.2" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "ff07cb8ecc0cc12a31817c69fcd9f0b00365b7a770b4d1717ef64016d43764ea" 87 | dependencies = [ 88 | "cfg-if", 89 | ] 90 | 91 | [[package]] 92 | name = "jvm" 93 | version = "0.1.0" 94 | dependencies = [ 95 | "byteorder", 96 | "vec_map", 97 | "zip", 98 | ] 99 | 100 | [[package]] 101 | name = "libc" 102 | version = "0.2.42" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" 105 | 106 | [[package]] 107 | name = "miniz_oxide" 108 | version = "0.2.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "5f6d7b3dd914b70db7cef7ab9dc74339ffcadf4d033464a987237bb0b9418cd4" 111 | dependencies = [ 112 | "adler32", 113 | ] 114 | 115 | [[package]] 116 | name = "miniz_oxide_c_api" 117 | version = "0.2.1" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" 120 | dependencies = [ 121 | "cc", 122 | "crc", 123 | "libc", 124 | "miniz_oxide", 125 | ] 126 | 127 | [[package]] 128 | name = "proc-macro2" 129 | version = "1.0.27" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 132 | dependencies = [ 133 | "unicode-xid", 134 | ] 135 | 136 | [[package]] 137 | name = "quote" 138 | version = "1.0.9" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 141 | dependencies = [ 142 | "proc-macro2", 143 | ] 144 | 145 | [[package]] 146 | name = "redox_syscall" 147 | version = "0.1.40" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" 150 | 151 | [[package]] 152 | name = "rustc_version" 153 | version = "0.2.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 156 | dependencies = [ 157 | "semver", 158 | ] 159 | 160 | [[package]] 161 | name = "semver" 162 | version = "0.9.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 165 | dependencies = [ 166 | "semver-parser", 167 | ] 168 | 169 | [[package]] 170 | name = "semver-parser" 171 | version = "0.7.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 174 | 175 | [[package]] 176 | name = "syn" 177 | version = "1.0.72" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" 180 | dependencies = [ 181 | "proc-macro2", 182 | "quote", 183 | "unicode-xid", 184 | ] 185 | 186 | [[package]] 187 | name = "thiserror" 188 | version = "1.0.25" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" 191 | dependencies = [ 192 | "thiserror-impl", 193 | ] 194 | 195 | [[package]] 196 | name = "thiserror-impl" 197 | version = "1.0.25" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" 200 | dependencies = [ 201 | "proc-macro2", 202 | "quote", 203 | "syn", 204 | ] 205 | 206 | [[package]] 207 | name = "time" 208 | version = "0.1.40" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" 211 | dependencies = [ 212 | "libc", 213 | "redox_syscall", 214 | "winapi", 215 | ] 216 | 217 | [[package]] 218 | name = "unicode-xid" 219 | version = "0.2.2" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 222 | 223 | [[package]] 224 | name = "vec_map" 225 | version = "0.8.1" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 228 | 229 | [[package]] 230 | name = "winapi" 231 | version = "0.3.5" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" 234 | dependencies = [ 235 | "winapi-i686-pc-windows-gnu", 236 | "winapi-x86_64-pc-windows-gnu", 237 | ] 238 | 239 | [[package]] 240 | name = "winapi-i686-pc-windows-gnu" 241 | version = "0.4.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 244 | 245 | [[package]] 246 | name = "winapi-x86_64-pc-windows-gnu" 247 | version = "0.4.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 250 | 251 | [[package]] 252 | name = "zip" 253 | version = "0.5.13" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" 256 | dependencies = [ 257 | "byteorder", 258 | "bzip2", 259 | "crc32fast", 260 | "flate2", 261 | "thiserror", 262 | "time", 263 | ] 264 | -------------------------------------------------------------------------------- /src/instruction/math/add.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn IADD(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | println!("IADD"); 9 | let (frame, thread) = thread.pop_frame(); 10 | 11 | let Frame { 12 | operand_stack, 13 | local_vars, 14 | method, 15 | class, 16 | } = frame; 17 | 18 | let (v2, operand_stack) = operand_stack.pop_int(); 19 | let (v1, operand_stack) = operand_stack.pop_int(); 20 | let result = v1 + v2; 21 | let operand_stack = operand_stack.push_int(result); 22 | 23 | let frame = Frame { 24 | class, 25 | operand_stack, 26 | local_vars, 27 | method, 28 | }; 29 | let thread = thread.push_frame(frame); 30 | let execute_result = ExecuteResult { thread, offset: 0 }; 31 | (execute_result, code_reader) 32 | } 33 | 34 | #[allow(non_snake_case)] 35 | pub fn DADD(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 36 | println!("DADD"); 37 | let (frame, thread) = thread.pop_frame(); 38 | 39 | let Frame { 40 | operand_stack, 41 | local_vars, 42 | method, 43 | class, 44 | } = frame; 45 | 46 | let (v2, operand_stack) = operand_stack.pop_double(); 47 | let (v1, operand_stack) = operand_stack.pop_double(); 48 | let result = v1 + v2; 49 | let operand_stack = operand_stack.push_double(result); 50 | 51 | let frame = Frame { 52 | class, 53 | operand_stack, 54 | local_vars, 55 | method, 56 | }; 57 | let thread = thread.push_frame(frame); 58 | let execute_result = ExecuteResult { thread, offset: 0 }; 59 | (execute_result, code_reader) 60 | } 61 | 62 | #[allow(non_snake_case)] 63 | pub fn LADD(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 64 | println!("LADD"); 65 | let (frame, thread) = thread.pop_frame(); 66 | 67 | let Frame { 68 | operand_stack, 69 | local_vars, 70 | method, 71 | class, 72 | } = frame; 73 | 74 | let (v2, operand_stack) = operand_stack.pop_long(); 75 | let (v1, operand_stack) = operand_stack.pop_long(); 76 | let result = v1 + v2; 77 | let operand_stack = operand_stack.push_long(result); 78 | 79 | let frame = Frame { 80 | class, 81 | operand_stack, 82 | local_vars, 83 | method, 84 | }; 85 | let thread = thread.push_frame(frame); 86 | let execute_result = ExecuteResult { thread, offset: 0 }; 87 | (execute_result, code_reader) 88 | } 89 | 90 | #[allow(non_snake_case)] 91 | pub fn FADD(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 92 | println!("FADD"); 93 | let (frame, thread) = thread.pop_frame(); 94 | 95 | let Frame { 96 | operand_stack, 97 | local_vars, 98 | method, 99 | class, 100 | } = frame; 101 | 102 | let (v2, operand_stack) = operand_stack.pop_float(); 103 | let (v1, operand_stack) = operand_stack.pop_float(); 104 | let result = v1 + v2; 105 | let operand_stack = operand_stack.push_float(result); 106 | 107 | let frame = Frame { 108 | class, 109 | operand_stack, 110 | local_vars, 111 | method, 112 | }; 113 | let thread = thread.push_frame(frame); 114 | let execute_result = ExecuteResult { thread, offset: 0 }; 115 | (execute_result, code_reader) 116 | } 117 | 118 | #[cfg(test)] 119 | mod tests { 120 | use classfile::constant_pool::ConstantPool; 121 | use classfile::member_info::MemberInfo; 122 | use instruction::instruction::ExecuteResult; 123 | use instruction::math::add::*; 124 | use rtda::frame::Frame; 125 | use rtda::heap::class::Class; 126 | use rtda::heap::method::Method; 127 | use rtda::thread::Thread; 128 | use rtda::vars::Vars; 129 | use std::rc::Rc; 130 | use util::code_reader::CodeReader; 131 | use vec_map::VecMap; 132 | 133 | #[test] 134 | #[allow(non_snake_case)] 135 | fn test_IADD() { 136 | let method = Rc::new(Method::new(MemberInfo { 137 | access_flags: 0u16, 138 | name: "".to_string(), 139 | name_index: 0u16, 140 | descriptor_index: 0u16, 141 | descriptor: "".to_string(), 142 | attributes: vec![], 143 | })); 144 | let class = Rc::new(Class { 145 | access_flags: 0u16, 146 | name: "".to_string(), 147 | constant_pool: ConstantPool { 148 | vec_map: VecMap::new(), 149 | }, 150 | fields: Vec::new(), 151 | methods: Vec::new(), 152 | super_class: None, 153 | instance_slot_count: 0usize, 154 | static_slot_count: 0usize, 155 | static_vars: Vars::new(2), 156 | }); 157 | let frame = Frame::new(class, method); 158 | let Frame { 159 | operand_stack, 160 | local_vars, 161 | method, 162 | class, 163 | } = frame; 164 | 165 | let operand_stack = operand_stack.push_int(2); 166 | let operand_stack = operand_stack.push_int(3); 167 | 168 | let frame = Frame { 169 | class, 170 | operand_stack, 171 | local_vars, 172 | method, 173 | }; 174 | let thread = Thread::new().push_frame(frame); 175 | let (ExecuteResult { thread, offset: _ }, _) = 176 | IADD(CodeReader::new(Rc::new(vec![])), thread); 177 | let (frame, _) = thread.pop_frame(); 178 | let (val, _) = frame.operand_stack.pop_int(); 179 | assert_eq!(val, 5); 180 | } 181 | 182 | #[test] 183 | #[allow(non_snake_case)] 184 | fn test_DADD() { 185 | let method = Rc::new(Method::new(MemberInfo { 186 | access_flags: 0u16, 187 | name: "".to_string(), 188 | name_index: 0u16, 189 | descriptor_index: 0u16, 190 | descriptor: "".to_string(), 191 | attributes: vec![], 192 | })); 193 | let class = Rc::new(Class { 194 | access_flags: 0u16, 195 | name: "".to_string(), 196 | constant_pool: ConstantPool { 197 | vec_map: VecMap::new(), 198 | }, 199 | fields: Vec::new(), 200 | methods: Vec::new(), 201 | super_class: None, 202 | instance_slot_count: 0usize, 203 | static_slot_count: 0usize, 204 | static_vars: Vars::new(2), 205 | }); 206 | let frame = Frame::new(class, method); 207 | let Frame { 208 | operand_stack, 209 | local_vars, 210 | method, 211 | class, 212 | } = frame; 213 | 214 | let operand_stack = operand_stack.push_double(2.71828182845f64); 215 | let operand_stack = operand_stack.push_double(3.1415926535897926f64); 216 | 217 | let frame = Frame { 218 | class, 219 | operand_stack, 220 | local_vars, 221 | method, 222 | }; 223 | let thread = Thread::new().push_frame(frame); 224 | let (ExecuteResult { thread, offset: _ }, _) = 225 | DADD(CodeReader::new(Rc::new(vec![])), thread); 226 | let (frame, _) = thread.pop_frame(); 227 | let (val, _) = frame.operand_stack.pop_double(); 228 | assert_eq!(val, 5.8598744820397926); 229 | } 230 | 231 | #[test] 232 | #[allow(non_snake_case)] 233 | fn test_FADD() { 234 | let method = Rc::new(Method::new(MemberInfo { 235 | access_flags: 0u16, 236 | name: "".to_string(), 237 | name_index: 0u16, 238 | descriptor_index: 0u16, 239 | descriptor: "".to_string(), 240 | attributes: vec![], 241 | })); 242 | let class = Rc::new(Class { 243 | access_flags: 0u16, 244 | name: "".to_string(), 245 | constant_pool: ConstantPool { 246 | vec_map: VecMap::new(), 247 | }, 248 | fields: Vec::new(), 249 | methods: Vec::new(), 250 | super_class: None, 251 | instance_slot_count: 0usize, 252 | static_slot_count: 0usize, 253 | static_vars: Vars::new(2), 254 | }); 255 | let frame = Frame::new(class, method); 256 | let Frame { 257 | operand_stack, 258 | local_vars, 259 | method, 260 | class, 261 | } = frame; 262 | 263 | let operand_stack = operand_stack.push_float(3.1415926); 264 | let operand_stack = operand_stack.push_float(3.1415926); 265 | 266 | let frame = Frame { 267 | class, 268 | operand_stack, 269 | local_vars, 270 | method, 271 | }; 272 | let thread = Thread::new().push_frame(frame); 273 | let (ExecuteResult { thread, offset: _ }, _) = 274 | FADD(CodeReader::new(Rc::new(vec![])), thread); 275 | let (frame, _) = thread.pop_frame(); 276 | let (val, _) = frame.operand_stack.pop_float(); 277 | assert_eq!(val, 6.2831852); 278 | } 279 | 280 | #[test] 281 | #[allow(non_snake_case)] 282 | fn test_LADD() { 283 | let method = Rc::new(Method::new(MemberInfo { 284 | access_flags: 0u16, 285 | name: "".to_string(), 286 | name_index: 0u16, 287 | descriptor_index: 0u16, 288 | descriptor: "".to_string(), 289 | attributes: vec![], 290 | })); 291 | let class = Rc::new(Class { 292 | access_flags: 0u16, 293 | name: "".to_string(), 294 | constant_pool: ConstantPool { 295 | vec_map: VecMap::new(), 296 | }, 297 | fields: Vec::new(), 298 | methods: Vec::new(), 299 | super_class: None, 300 | instance_slot_count: 0usize, 301 | static_slot_count: 0usize, 302 | static_vars: Vars::new(2), 303 | }); 304 | let frame = Frame::new(class, method); 305 | let Frame { 306 | operand_stack, 307 | local_vars, 308 | method, 309 | class, 310 | } = frame; 311 | 312 | let operand_stack = operand_stack.push_long(12345678969); 313 | let operand_stack = operand_stack.push_long(2997924580); 314 | 315 | let frame = Frame { 316 | class, 317 | operand_stack, 318 | local_vars, 319 | method, 320 | }; 321 | let thread = Thread::new().push_frame(frame); 322 | let (ExecuteResult { thread, offset: _ }, _) = 323 | DADD(CodeReader::new(Rc::new(vec![])), thread); 324 | let (frame, _) = thread.pop_frame(); 325 | let (val, _) = frame.operand_stack.pop_long(); 326 | assert_eq!(val, 15343603549); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/instruction/math/mul.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn DMUL(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | println!("DMUL"); 9 | 10 | let (frame, thread) = thread.pop_frame(); 11 | 12 | let Frame { 13 | operand_stack, 14 | local_vars, 15 | method, 16 | class, 17 | } = frame; 18 | 19 | let (v2, operand_stack) = operand_stack.pop_double(); 20 | let (v1, operand_stack) = operand_stack.pop_double(); 21 | let result = v1 * v2; 22 | let operand_stack = operand_stack.push_double(result); 23 | 24 | let frame = Frame { 25 | class, 26 | operand_stack, 27 | local_vars, 28 | method, 29 | }; 30 | let thread = thread.push_frame(frame); 31 | let execute_result = ExecuteResult { thread, offset: 0 }; 32 | (execute_result, code_reader) 33 | } 34 | 35 | #[allow(non_snake_case)] 36 | pub fn FMUL(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 37 | println!("FMUL"); 38 | let (frame, thread) = thread.pop_frame(); 39 | 40 | let Frame { 41 | operand_stack, 42 | local_vars, 43 | method, 44 | class, 45 | } = frame; 46 | 47 | let (v2, operand_stack) = operand_stack.pop_float(); 48 | let (v1, operand_stack) = operand_stack.pop_float(); 49 | let result = v1 * v2; 50 | let operand_stack = operand_stack.push_float(result); 51 | 52 | let frame = Frame { 53 | class, 54 | operand_stack, 55 | local_vars, 56 | method, 57 | }; 58 | let thread = thread.push_frame(frame); 59 | let execute_result = ExecuteResult { thread, offset: 0 }; 60 | (execute_result, code_reader) 61 | } 62 | 63 | #[allow(non_snake_case)] 64 | pub fn IMUL(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 65 | println!("IMUL"); 66 | let (frame, thread) = thread.pop_frame(); 67 | 68 | let Frame { 69 | operand_stack, 70 | local_vars, 71 | method, 72 | class, 73 | } = frame; 74 | 75 | let (v2, operand_stack) = operand_stack.pop_int(); 76 | let (v1, operand_stack) = operand_stack.pop_int(); 77 | let result = v1 * v2; 78 | let operand_stack = operand_stack.push_int(result); 79 | 80 | let frame = Frame { 81 | class, 82 | operand_stack, 83 | local_vars, 84 | method, 85 | }; 86 | let thread = thread.push_frame(frame); 87 | let execute_result = ExecuteResult { thread, offset: 0 }; 88 | (execute_result, code_reader) 89 | } 90 | 91 | #[allow(non_snake_case)] 92 | pub fn LMUL(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 93 | println!("LMUL"); 94 | let (frame, thread) = thread.pop_frame(); 95 | 96 | let Frame { 97 | operand_stack, 98 | local_vars, 99 | method, 100 | class, 101 | } = frame; 102 | 103 | let (v2, operand_stack) = operand_stack.pop_long(); 104 | let (v1, operand_stack) = operand_stack.pop_long(); 105 | let result = v1 * v2; 106 | let operand_stack = operand_stack.push_long(result); 107 | 108 | let frame = Frame { 109 | class, 110 | operand_stack, 111 | local_vars, 112 | method, 113 | }; 114 | let thread = thread.push_frame(frame); 115 | let execute_result = ExecuteResult { thread, offset: 0 }; 116 | (execute_result, code_reader) 117 | } 118 | 119 | #[cfg(test)] 120 | mod test { 121 | use classfile::constant_pool::ConstantPool; 122 | use classfile::member_info::MemberInfo; 123 | use instruction::instruction::ExecuteResult; 124 | use instruction::math::mul::*; 125 | use rtda::frame::Frame; 126 | use rtda::heap::class::Class; 127 | use rtda::heap::method::Method; 128 | use rtda::thread::Thread; 129 | use rtda::vars::Vars; 130 | use std::rc::Rc; 131 | use util::code_reader::CodeReader; 132 | use vec_map::VecMap; 133 | 134 | #[test] 135 | #[allow(non_snake_case)] 136 | fn test_DMUL() { 137 | let method = Rc::new(Method::new(MemberInfo { 138 | access_flags: 0u16, 139 | name: "".to_string(), 140 | name_index: 0u16, 141 | descriptor_index: 0u16, 142 | descriptor: "".to_string(), 143 | attributes: vec![], 144 | })); 145 | let class = Rc::new(Class { 146 | access_flags: 0u16, 147 | name: "".to_string(), 148 | constant_pool: ConstantPool { 149 | vec_map: VecMap::new(), 150 | }, 151 | fields: Vec::new(), 152 | methods: Vec::new(), 153 | super_class: None, 154 | instance_slot_count: 0usize, 155 | static_slot_count: 0usize, 156 | static_vars: Vars::new(2), 157 | }); 158 | let frame = Frame::new(class, method); 159 | let Frame { 160 | operand_stack, 161 | local_vars, 162 | method, 163 | class, 164 | } = frame; 165 | 166 | let operand_stack = operand_stack.push_double(2.71828182845f64); 167 | let operand_stack = operand_stack.push_double(3.1415926535897926f64); 168 | 169 | let frame = Frame { 170 | class, 171 | operand_stack, 172 | local_vars, 173 | method, 174 | }; 175 | let thread = Thread::new().push_frame(frame); 176 | let (ExecuteResult { thread, offset: _ }, _) = 177 | DMUL(CodeReader::new(Rc::new(vec![])), thread); 178 | let (frame, _) = thread.pop_frame(); 179 | let (val, _) = frame.operand_stack.pop_double(); 180 | assert_eq!(val, 8.53973422264514888498427947f64); 181 | } 182 | 183 | #[test] 184 | #[allow(non_snake_case)] 185 | fn test_FMUL() { 186 | let method = Rc::new(Method::new(MemberInfo { 187 | access_flags: 0u16, 188 | name: "".to_string(), 189 | name_index: 0u16, 190 | descriptor_index: 0u16, 191 | descriptor: "".to_string(), 192 | attributes: vec![], 193 | })); 194 | let class = Rc::new(Class { 195 | access_flags: 0u16, 196 | name: "".to_string(), 197 | constant_pool: ConstantPool { 198 | vec_map: VecMap::new(), 199 | }, 200 | fields: Vec::new(), 201 | methods: Vec::new(), 202 | super_class: None, 203 | instance_slot_count: 0usize, 204 | static_slot_count: 0usize, 205 | static_vars: Vars::new(2), 206 | }); 207 | let frame = Frame::new(class, method); 208 | let Frame { 209 | operand_stack, 210 | local_vars, 211 | method, 212 | class, 213 | } = frame; 214 | 215 | let operand_stack = operand_stack.push_float(2.71828182845f32); 216 | let operand_stack = operand_stack.push_float(3.1415926535897926f32); 217 | 218 | let frame = Frame { 219 | class, 220 | operand_stack, 221 | local_vars, 222 | method, 223 | }; 224 | let thread = Thread::new().push_frame(frame); 225 | let (ExecuteResult { thread, offset: _ }, _) = 226 | FMUL(CodeReader::new(Rc::new(vec![])), thread); 227 | let (frame, _) = thread.pop_frame(); 228 | let (val, _) = frame.operand_stack.pop_float(); 229 | assert_eq!(val, 8.53973422264514888498427947f32); 230 | } 231 | 232 | #[test] 233 | #[allow(non_snake_case)] 234 | fn test_IMUL() { 235 | let method = Rc::new(Method::new(MemberInfo { 236 | access_flags: 0u16, 237 | name: "".to_string(), 238 | name_index: 0u16, 239 | descriptor_index: 0u16, 240 | descriptor: "".to_string(), 241 | attributes: vec![], 242 | })); 243 | let class = Rc::new(Class { 244 | access_flags: 0u16, 245 | name: "".to_string(), 246 | constant_pool: ConstantPool { 247 | vec_map: VecMap::new(), 248 | }, 249 | fields: Vec::new(), 250 | methods: Vec::new(), 251 | super_class: None, 252 | instance_slot_count: 0usize, 253 | static_slot_count: 0usize, 254 | static_vars: Vars::new(2), 255 | }); 256 | let frame = Frame::new(class, method); 257 | let Frame { 258 | operand_stack, 259 | local_vars, 260 | method, 261 | class, 262 | } = frame; 263 | 264 | let operand_stack = operand_stack.push_int(2); 265 | let operand_stack = operand_stack.push_int(3); 266 | 267 | let frame = Frame { 268 | class, 269 | operand_stack, 270 | local_vars, 271 | method, 272 | }; 273 | let thread = Thread::new().push_frame(frame); 274 | let (ExecuteResult { thread, offset: _ }, _) = 275 | IMUL(CodeReader::new(Rc::new(vec![])), thread); 276 | let (frame, _) = thread.pop_frame(); 277 | let (val, _) = frame.operand_stack.pop_int(); 278 | assert_eq!(val, 6); 279 | } 280 | 281 | #[test] 282 | #[allow(non_snake_case)] 283 | fn test_LMUL() { 284 | let method = Rc::new(Method::new(MemberInfo { 285 | access_flags: 0u16, 286 | name: "".to_string(), 287 | name_index: 0u16, 288 | descriptor_index: 0u16, 289 | descriptor: "".to_string(), 290 | attributes: vec![], 291 | })); 292 | let class = Rc::new(Class { 293 | access_flags: 0u16, 294 | name: "".to_string(), 295 | constant_pool: ConstantPool { 296 | vec_map: VecMap::new(), 297 | }, 298 | fields: Vec::new(), 299 | methods: Vec::new(), 300 | super_class: None, 301 | instance_slot_count: 0usize, 302 | static_slot_count: 0usize, 303 | static_vars: Vars::new(2), 304 | }); 305 | let frame = Frame::new(class, method); 306 | let Frame { 307 | operand_stack, 308 | local_vars, 309 | method, 310 | class, 311 | } = frame; 312 | 313 | let operand_stack = operand_stack.push_long(1234567890); 314 | let operand_stack = operand_stack.push_long(2997924580); 315 | 316 | let frame = Frame { 317 | class, 318 | operand_stack, 319 | local_vars, 320 | method, 321 | }; 322 | let thread = Thread::new().push_frame(frame); 323 | let (ExecuteResult { thread, offset: _ }, _) = 324 | LMUL(CodeReader::new(Rc::new(vec![])), thread); 325 | let (frame, _) = thread.pop_frame(); 326 | let (val, _) = frame.operand_stack.pop_long(); 327 | assert_eq!(val, 3701141423109736200); 328 | } 329 | 330 | } 331 | -------------------------------------------------------------------------------- /src/instruction/math/neg.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | pub fn DNEG(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 8 | let (frame, thread) = thread.pop_frame(); 9 | 10 | let Frame { 11 | operand_stack, 12 | local_vars, 13 | method, 14 | class, 15 | } = frame; 16 | 17 | let (v, operand_stack) = operand_stack.pop_double(); 18 | let operand_stack = operand_stack.push_double(-v); 19 | let frame = Frame { 20 | class, 21 | operand_stack, 22 | local_vars, 23 | method, 24 | }; 25 | let thread = thread.push_frame(frame); 26 | let execute_result = ExecuteResult { thread, offset: 0 }; 27 | (execute_result, code_reader) 28 | } 29 | 30 | #[allow(non_snake_case)] 31 | pub fn FNEG(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 32 | let (frame, thread) = thread.pop_frame(); 33 | 34 | let Frame { 35 | operand_stack, 36 | local_vars, 37 | method, 38 | class, 39 | } = frame; 40 | 41 | let (v, operand_stack) = operand_stack.pop_float(); 42 | let operand_stack = operand_stack.push_float(-v); 43 | let frame = Frame { 44 | class, 45 | operand_stack, 46 | local_vars, 47 | method, 48 | }; 49 | let thread = thread.push_frame(frame); 50 | let execute_result = ExecuteResult { thread, offset: 0 }; 51 | (execute_result, code_reader) 52 | } 53 | 54 | #[allow(non_snake_case)] 55 | pub fn INEG(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 56 | let (frame, thread) = thread.pop_frame(); 57 | 58 | let Frame { 59 | operand_stack, 60 | local_vars, 61 | method, 62 | class, 63 | } = frame; 64 | 65 | let (v, operand_stack) = operand_stack.pop_int(); 66 | let operand_stack = operand_stack.push_int(-v); 67 | let frame = Frame { 68 | class, 69 | operand_stack, 70 | local_vars, 71 | method, 72 | }; 73 | let thread = thread.push_frame(frame); 74 | let execute_result = ExecuteResult { thread, offset: 0 }; 75 | (execute_result, code_reader) 76 | } 77 | 78 | #[allow(non_snake_case)] 79 | pub fn LNEG(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 80 | let (frame, thread) = thread.pop_frame(); 81 | 82 | let Frame { 83 | operand_stack, 84 | local_vars, 85 | method, 86 | class, 87 | } = frame; 88 | 89 | let (v, operand_stack) = operand_stack.pop_long(); 90 | let operand_stack = operand_stack.push_long(-v); 91 | let frame = Frame { 92 | class, 93 | operand_stack, 94 | local_vars, 95 | method, 96 | }; 97 | let thread = thread.push_frame(frame); 98 | let execute_result = ExecuteResult { thread, offset: 0 }; 99 | (execute_result, code_reader) 100 | } 101 | 102 | #[cfg(test)] 103 | mod tests { 104 | use classfile::constant_pool::ConstantPool; 105 | use classfile::member_info::MemberInfo; 106 | use instruction::instruction::ExecuteResult; 107 | use instruction::math::neg::*; 108 | use rtda::frame::Frame; 109 | use rtda::heap::class::Class; 110 | use rtda::heap::method::Method; 111 | use rtda::thread::Thread; 112 | use rtda::vars::Vars; 113 | use std::f32; 114 | use std::f64; 115 | use std::rc::Rc; 116 | use util::code_reader::CodeReader; 117 | use vec_map::VecMap; 118 | 119 | #[test] 120 | #[allow(non_snake_case)] 121 | fn test_DNEG() { 122 | let method = Rc::new(Method::new(MemberInfo { 123 | access_flags: 0u16, 124 | name: "".to_string(), 125 | name_index: 0u16, 126 | descriptor_index: 0u16, 127 | descriptor: "".to_string(), 128 | attributes: vec![], 129 | })); 130 | let class = Rc::new(Class { 131 | access_flags: 0u16, 132 | name: "".to_string(), 133 | constant_pool: ConstantPool { 134 | vec_map: VecMap::new(), 135 | }, 136 | fields: Vec::new(), 137 | methods: Vec::new(), 138 | super_class: None, 139 | instance_slot_count: 0usize, 140 | static_slot_count: 0usize, 141 | static_vars: Vars::new(2), 142 | }); 143 | let frame = Frame::new(class, method); 144 | let Frame { 145 | operand_stack, 146 | local_vars, 147 | method, 148 | class, 149 | } = frame; 150 | 151 | let operand_stack = operand_stack.push_double(2f64); 152 | 153 | let frame = Frame { 154 | class, 155 | operand_stack, 156 | local_vars, 157 | method, 158 | }; 159 | let thread = Thread::new().push_frame(frame); 160 | let (ExecuteResult { thread, offset: _ }, _) = 161 | DNEG(CodeReader::new(Rc::new(vec![])), thread); 162 | let (frame, _) = thread.pop_frame(); 163 | let (val, _) = frame.operand_stack.pop_double(); 164 | assert_eq!(val, -2f64); 165 | } 166 | 167 | #[test] 168 | #[allow(non_snake_case)] 169 | fn test_DNEG_zero() { 170 | let method = Rc::new(Method::new(MemberInfo { 171 | access_flags: 0u16, 172 | name: "".to_string(), 173 | name_index: 0u16, 174 | descriptor_index: 0u16, 175 | descriptor: "".to_string(), 176 | attributes: vec![], 177 | })); 178 | let class = Rc::new(Class { 179 | access_flags: 0u16, 180 | name: "".to_string(), 181 | constant_pool: ConstantPool { 182 | vec_map: VecMap::new(), 183 | }, 184 | fields: Vec::new(), 185 | methods: Vec::new(), 186 | super_class: None, 187 | instance_slot_count: 0usize, 188 | static_slot_count: 0usize, 189 | static_vars: Vars::new(2), 190 | }); 191 | let frame = Frame::new(class, method); 192 | let Frame { 193 | operand_stack, 194 | local_vars, 195 | method, 196 | class, 197 | } = frame; 198 | 199 | let operand_stack = operand_stack.push_double(-0f64); 200 | 201 | let frame = Frame { 202 | class, 203 | operand_stack, 204 | local_vars, 205 | method, 206 | }; 207 | let thread = Thread::new().push_frame(frame); 208 | let (ExecuteResult { thread, offset: _ }, _) = 209 | DNEG(CodeReader::new(Rc::new(vec![])), thread); 210 | let (frame, _) = thread.pop_frame(); 211 | let (val, _) = frame.operand_stack.pop_double(); 212 | assert_eq!(val, 0f64); 213 | } 214 | 215 | #[test] 216 | #[allow(non_snake_case)] 217 | fn test_DNEG_inf() { 218 | let method = Rc::new(Method::new(MemberInfo { 219 | access_flags: 0u16, 220 | name: "".to_string(), 221 | name_index: 0u16, 222 | descriptor_index: 0u16, 223 | descriptor: "".to_string(), 224 | attributes: vec![], 225 | })); 226 | let class = Rc::new(Class { 227 | access_flags: 0u16, 228 | name: "".to_string(), 229 | constant_pool: ConstantPool { 230 | vec_map: VecMap::new(), 231 | }, 232 | fields: Vec::new(), 233 | methods: Vec::new(), 234 | super_class: None, 235 | instance_slot_count: 0usize, 236 | static_slot_count: 0usize, 237 | static_vars: Vars::new(2), 238 | }); 239 | let frame = Frame::new(class, method); 240 | let Frame { 241 | operand_stack, 242 | local_vars, 243 | method, 244 | class, 245 | } = frame; 246 | 247 | let operand_stack = operand_stack.push_double(f64::INFINITY); 248 | 249 | let frame = Frame { 250 | class, 251 | operand_stack, 252 | local_vars, 253 | method, 254 | }; 255 | let thread = Thread::new().push_frame(frame); 256 | let (ExecuteResult { thread, offset: _ }, _) = 257 | DNEG(CodeReader::new(Rc::new(vec![])), thread); 258 | let (frame, _) = thread.pop_frame(); 259 | let (val, _) = frame.operand_stack.pop_double(); 260 | assert_eq!(val, f64::NEG_INFINITY); 261 | } 262 | 263 | #[test] 264 | #[allow(non_snake_case)] 265 | fn test_FNEG() { 266 | let method = Rc::new(Method::new(MemberInfo { 267 | access_flags: 0u16, 268 | name: "".to_string(), 269 | name_index: 0u16, 270 | descriptor_index: 0u16, 271 | descriptor: "".to_string(), 272 | attributes: vec![], 273 | })); 274 | let class = Rc::new(Class { 275 | access_flags: 0u16, 276 | name: "".to_string(), 277 | constant_pool: ConstantPool { 278 | vec_map: VecMap::new(), 279 | }, 280 | fields: Vec::new(), 281 | methods: Vec::new(), 282 | super_class: None, 283 | instance_slot_count: 0usize, 284 | static_slot_count: 0usize, 285 | static_vars: Vars::new(2), 286 | }); 287 | let frame = Frame::new(class, method); 288 | let Frame { 289 | operand_stack, 290 | local_vars, 291 | method, 292 | class, 293 | } = frame; 294 | 295 | let operand_stack = operand_stack.push_float(-100.7678f32); 296 | 297 | let frame = Frame { 298 | class, 299 | operand_stack, 300 | local_vars, 301 | method, 302 | }; 303 | let thread = Thread::new().push_frame(frame); 304 | let (ExecuteResult { thread, offset: _ }, _) = 305 | FNEG(CodeReader::new(Rc::new(vec![])), thread); 306 | let (frame, _) = thread.pop_frame(); 307 | let (val, _) = frame.operand_stack.pop_float(); 308 | assert_eq!(val, 100.7678f32); 309 | } 310 | 311 | #[test] 312 | #[allow(non_snake_case)] 313 | fn test_FNEG_max_min() { 314 | let method = Rc::new(Method::new(MemberInfo { 315 | access_flags: 0u16, 316 | name: "".to_string(), 317 | name_index: 0u16, 318 | descriptor_index: 0u16, 319 | descriptor: "".to_string(), 320 | attributes: vec![], 321 | })); 322 | let class = Rc::new(Class { 323 | access_flags: 0u16, 324 | name: "".to_string(), 325 | constant_pool: ConstantPool { 326 | vec_map: VecMap::new(), 327 | }, 328 | fields: Vec::new(), 329 | methods: Vec::new(), 330 | super_class: None, 331 | instance_slot_count: 0usize, 332 | static_slot_count: 0usize, 333 | static_vars: Vars::new(2), 334 | }); 335 | let frame = Frame::new(class, method); 336 | let Frame { 337 | operand_stack, 338 | local_vars, 339 | method, 340 | class, 341 | } = frame; 342 | 343 | let operand_stack = operand_stack.push_float(f32::MAX); 344 | 345 | let frame = Frame { 346 | class, 347 | operand_stack, 348 | local_vars, 349 | method, 350 | }; 351 | let thread = Thread::new().push_frame(frame); 352 | let (ExecuteResult { thread, offset: _ }, _) = 353 | FNEG(CodeReader::new(Rc::new(vec![])), thread); 354 | let (frame, _) = thread.pop_frame(); 355 | let (val, _) = frame.operand_stack.pop_float(); 356 | assert_eq!(val, f32::MIN); 357 | } 358 | #[test] 359 | #[allow(non_snake_case)] 360 | fn test_INEG() { 361 | let method = Rc::new(Method::new(MemberInfo { 362 | access_flags: 0u16, 363 | name: "".to_string(), 364 | name_index: 0u16, 365 | descriptor_index: 0u16, 366 | descriptor: "".to_string(), 367 | attributes: vec![], 368 | })); 369 | let class = Rc::new(Class { 370 | access_flags: 0u16, 371 | name: "".to_string(), 372 | constant_pool: ConstantPool { 373 | vec_map: VecMap::new(), 374 | }, 375 | fields: Vec::new(), 376 | methods: Vec::new(), 377 | super_class: None, 378 | instance_slot_count: 0usize, 379 | static_slot_count: 0usize, 380 | static_vars: Vars::new(2), 381 | }); 382 | let frame = Frame::new(class, method); 383 | let Frame { 384 | operand_stack, 385 | local_vars, 386 | method, 387 | class, 388 | } = frame; 389 | 390 | let operand_stack = operand_stack.push_int(234556); 391 | 392 | let frame = Frame { 393 | class, 394 | operand_stack, 395 | local_vars, 396 | method, 397 | }; 398 | let thread = Thread::new().push_frame(frame); 399 | let (ExecuteResult { thread, offset: _ }, _) = 400 | INEG(CodeReader::new(Rc::new(vec![])), thread); 401 | let (frame, _) = thread.pop_frame(); 402 | let (val, _) = frame.operand_stack.pop_int(); 403 | assert_eq!(val, -234556); 404 | } 405 | 406 | #[test] 407 | #[allow(non_snake_case)] 408 | fn test_LNEG() { 409 | let method = Rc::new(Method::new(MemberInfo { 410 | access_flags: 0u16, 411 | name: "".to_string(), 412 | name_index: 0u16, 413 | descriptor_index: 0u16, 414 | descriptor: "".to_string(), 415 | attributes: vec![], 416 | })); 417 | let class = Rc::new(Class { 418 | access_flags: 0u16, 419 | name: "".to_string(), 420 | constant_pool: ConstantPool { 421 | vec_map: VecMap::new(), 422 | }, 423 | fields: Vec::new(), 424 | methods: Vec::new(), 425 | super_class: None, 426 | instance_slot_count: 0usize, 427 | static_slot_count: 0usize, 428 | static_vars: Vars::new(2), 429 | }); 430 | let frame = Frame::new(class, method); 431 | let Frame { 432 | operand_stack, 433 | local_vars, 434 | method, 435 | class, 436 | } = frame; 437 | 438 | let operand_stack = operand_stack.push_long(-54875845748435i64); 439 | 440 | let frame = Frame { 441 | class, 442 | operand_stack, 443 | local_vars, 444 | method, 445 | }; 446 | let thread = Thread::new().push_frame(frame); 447 | let (ExecuteResult { thread, offset: _ }, _) = 448 | LNEG(CodeReader::new(Rc::new(vec![])), thread); 449 | let (frame, _) = thread.pop_frame(); 450 | let (val, _) = frame.operand_stack.pop_long(); 451 | assert_eq!(val, 54875845748435i64); 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /src/instruction/comparison/ifcond.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | fn _ifcond(frame: Frame) -> (i32, Frame) { 7 | let Frame { 8 | operand_stack, 9 | local_vars, 10 | method, 11 | class, 12 | } = frame; 13 | 14 | let (val, operand_stack) = operand_stack.pop_int(); 15 | let frame = Frame { 16 | class, 17 | operand_stack, 18 | local_vars, 19 | method, 20 | }; 21 | (val, frame) 22 | } 23 | 24 | #[allow(non_snake_case)] 25 | pub fn IFEQ(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 26 | println!("IFEQ"); 27 | let (offset, code_reader) = code_reader.read_i16(); 28 | let (frame, thread) = thread.pop_frame(); 29 | 30 | let (val, frame) = _ifcond(frame); 31 | let offset = if val == 0 { offset as isize } else { 0 }; 32 | let thread = thread.push_frame(frame); 33 | let execute_result = ExecuteResult { thread, offset }; 34 | (execute_result, code_reader) 35 | } 36 | 37 | #[allow(non_snake_case)] 38 | pub fn IFNE(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 39 | println!("IFNE"); 40 | let (offset, code_reader) = code_reader.read_i16(); 41 | 42 | let (frame, thread) = thread.pop_frame(); 43 | 44 | let (val, frame) = _ifcond(frame); 45 | let offset = if val != 0 { offset as isize } else { 0 }; 46 | let thread = thread.push_frame(frame); 47 | let execute_result = ExecuteResult { thread, offset }; 48 | (execute_result, code_reader) 49 | } 50 | 51 | #[allow(non_snake_case)] 52 | pub fn IFLT(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 53 | println!("IFLT"); 54 | let (offset, code_reader) = code_reader.read_i16(); 55 | 56 | let (frame, thread) = thread.pop_frame(); 57 | 58 | let (val, frame) = _ifcond(frame); 59 | let offset = if val < 0 { offset as isize } else { 0 }; 60 | let thread = thread.push_frame(frame); 61 | let execute_result = ExecuteResult { thread, offset }; 62 | (execute_result, code_reader) 63 | } 64 | 65 | #[allow(non_snake_case)] 66 | pub fn IFGE(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 67 | println!("IFGE"); 68 | let (offset, code_reader) = code_reader.read_i16(); 69 | 70 | let (frame, thread) = thread.pop_frame(); 71 | 72 | let (val, frame) = _ifcond(frame); 73 | let offset = if val >= 0 { offset as isize } else { 0 }; 74 | let thread = thread.push_frame(frame); 75 | let execute_result = ExecuteResult { thread, offset }; 76 | (execute_result, code_reader) 77 | } 78 | 79 | #[allow(non_snake_case)] 80 | pub fn IFGT(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 81 | println!("IFGT"); 82 | let (offset, code_reader) = code_reader.read_i16(); 83 | 84 | let (frame, thread) = thread.pop_frame(); 85 | 86 | let (val, frame) = _ifcond(frame); 87 | let offset = if val > 0 { offset as isize } else { 0 }; 88 | let thread = thread.push_frame(frame); 89 | let execute_result = ExecuteResult { thread, offset }; 90 | (execute_result, code_reader) 91 | } 92 | 93 | #[allow(non_snake_case)] 94 | pub fn IFLE(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 95 | println!("IFLE"); 96 | let (offset, code_reader) = code_reader.read_i16(); 97 | 98 | let (frame, thread) = thread.pop_frame(); 99 | 100 | let (val, frame) = _ifcond(frame); 101 | let offset = if val <= 0 { offset as isize } else { 0 }; 102 | let thread = thread.push_frame(frame); 103 | let execute_result = ExecuteResult { thread, offset }; 104 | (execute_result, code_reader) 105 | } 106 | 107 | #[cfg(test)] 108 | mod tests { 109 | use classfile::constant_pool::ConstantPool; 110 | use classfile::member_info::MemberInfo; 111 | use instruction::comparison::ifcond::*; 112 | use instruction::instruction::ExecuteResult; 113 | use rtda::frame::Frame; 114 | use rtda::heap::class::Class; 115 | use rtda::heap::method::Method; 116 | use rtda::thread::Thread; 117 | use rtda::vars::Vars; 118 | use std::rc::Rc; 119 | use util::code_reader::CodeReader; 120 | use vec_map::VecMap; 121 | 122 | #[test] 123 | #[allow(non_snake_case)] 124 | fn test_IFEQ_success() { 125 | let method = Rc::new(Method::new(MemberInfo { 126 | access_flags: 0u16, 127 | name: "".to_string(), 128 | name_index: 0u16, 129 | descriptor_index: 0u16, 130 | descriptor: "".to_string(), 131 | attributes: vec![], 132 | })); 133 | let class = Rc::new(Class { 134 | access_flags: 0u16, 135 | name: "".to_string(), 136 | constant_pool: ConstantPool { 137 | vec_map: VecMap::new(), 138 | }, 139 | fields: Vec::new(), 140 | methods: Vec::new(), 141 | super_class: None, 142 | instance_slot_count: 0usize, 143 | static_slot_count: 0usize, 144 | static_vars: Vars::new(2), 145 | }); 146 | let frame = Frame::new(class, method); 147 | let Frame { 148 | operand_stack, 149 | local_vars, 150 | method, 151 | class, 152 | } = frame; 153 | 154 | let operand_stack = operand_stack.push_int(0); 155 | let frame = Frame { 156 | class, 157 | operand_stack, 158 | local_vars, 159 | method, 160 | }; 161 | let thread = Thread::new().push_frame(frame); 162 | let (ExecuteResult { thread: _, offset }, _) = 163 | IFEQ(CodeReader::new(Rc::new(vec![1, 1])), thread); 164 | assert_eq!(offset, 257); 165 | } 166 | 167 | #[allow(non_snake_case)] 168 | fn test_IFEQ_fail() { 169 | let method = Rc::new(Method::new(MemberInfo { 170 | access_flags: 0u16, 171 | name: "".to_string(), 172 | name_index: 0u16, 173 | descriptor_index: 0u16, 174 | descriptor: "".to_string(), 175 | attributes: vec![], 176 | })); 177 | let class = Rc::new(Class { 178 | access_flags: 0u16, 179 | name: "".to_string(), 180 | constant_pool: ConstantPool { 181 | vec_map: VecMap::new(), 182 | }, 183 | fields: Vec::new(), 184 | methods: Vec::new(), 185 | super_class: None, 186 | instance_slot_count: 0usize, 187 | static_slot_count: 0usize, 188 | static_vars: Vars::new(2), 189 | }); 190 | let frame = Frame::new(class, method); 191 | let Frame { 192 | operand_stack, 193 | local_vars, 194 | method, 195 | class, 196 | } = frame; 197 | 198 | let operand_stack = operand_stack.push_int(0); 199 | let frame = Frame { 200 | class, 201 | operand_stack, 202 | local_vars, 203 | method, 204 | }; 205 | let thread = Thread::new().push_frame(frame); 206 | let (ExecuteResult { thread: _, offset }, _) = 207 | IFEQ(CodeReader::new(Rc::new(vec![1, 1])), thread); 208 | assert_eq!(offset, 0); 209 | } 210 | 211 | #[test] 212 | #[allow(non_snake_case)] 213 | fn test_IFNE_success() { 214 | let method = Rc::new(Method::new(MemberInfo { 215 | access_flags: 0u16, 216 | name: "".to_string(), 217 | name_index: 0u16, 218 | descriptor_index: 0u16, 219 | descriptor: "".to_string(), 220 | attributes: vec![], 221 | })); 222 | let class = Rc::new(Class { 223 | access_flags: 0u16, 224 | name: "".to_string(), 225 | constant_pool: ConstantPool { 226 | vec_map: VecMap::new(), 227 | }, 228 | fields: Vec::new(), 229 | methods: Vec::new(), 230 | super_class: None, 231 | instance_slot_count: 0usize, 232 | static_slot_count: 0usize, 233 | static_vars: Vars::new(2), 234 | }); 235 | let frame = Frame::new(class, method); 236 | let Frame { 237 | operand_stack, 238 | local_vars, 239 | method, 240 | class, 241 | } = frame; 242 | 243 | let operand_stack = operand_stack.push_int(1); 244 | let frame = Frame { 245 | class, 246 | operand_stack, 247 | local_vars, 248 | method, 249 | }; 250 | let thread = Thread::new().push_frame(frame); 251 | let (ExecuteResult { thread: _, offset }, _) = 252 | IFNE(CodeReader::new(Rc::new(vec![1, 1])), thread); 253 | assert_eq!(offset, 257); 254 | } 255 | 256 | #[allow(non_snake_case)] 257 | fn test_IFNE_fail() { 258 | let method = Rc::new(Method::new(MemberInfo { 259 | access_flags: 0u16, 260 | name: "".to_string(), 261 | name_index: 0u16, 262 | descriptor_index: 0u16, 263 | descriptor: "".to_string(), 264 | attributes: vec![], 265 | })); 266 | let class = Rc::new(Class { 267 | access_flags: 0u16, 268 | name: "".to_string(), 269 | constant_pool: ConstantPool { 270 | vec_map: VecMap::new(), 271 | }, 272 | fields: Vec::new(), 273 | methods: Vec::new(), 274 | super_class: None, 275 | instance_slot_count: 0usize, 276 | static_slot_count: 0usize, 277 | static_vars: Vars::new(2), 278 | }); 279 | let frame = Frame::new(class, method); 280 | let Frame { 281 | operand_stack, 282 | local_vars, 283 | method, 284 | class, 285 | } = frame; 286 | 287 | let operand_stack = operand_stack.push_int(0); 288 | let frame = Frame { 289 | class, 290 | operand_stack, 291 | local_vars, 292 | method, 293 | }; 294 | let thread = Thread::new().push_frame(frame); 295 | let (ExecuteResult { thread: _, offset }, _) = 296 | IFNE(CodeReader::new(Rc::new(vec![1, 1])), thread); 297 | assert_eq!(offset, 0); 298 | } 299 | 300 | #[test] 301 | #[allow(non_snake_case)] 302 | fn test_IFLT_success() { 303 | let method = Rc::new(Method::new(MemberInfo { 304 | access_flags: 0u16, 305 | name: "".to_string(), 306 | name_index: 0u16, 307 | descriptor_index: 0u16, 308 | descriptor: "".to_string(), 309 | attributes: vec![], 310 | })); 311 | let class = Rc::new(Class { 312 | access_flags: 0u16, 313 | name: "".to_string(), 314 | constant_pool: ConstantPool { 315 | vec_map: VecMap::new(), 316 | }, 317 | fields: Vec::new(), 318 | methods: Vec::new(), 319 | super_class: None, 320 | instance_slot_count: 0usize, 321 | static_slot_count: 0usize, 322 | static_vars: Vars::new(2), 323 | }); 324 | let frame = Frame::new(class, method); 325 | let Frame { 326 | operand_stack, 327 | local_vars, 328 | method, 329 | class, 330 | } = frame; 331 | 332 | let operand_stack = operand_stack.push_int(-1); 333 | let frame = Frame { 334 | class, 335 | operand_stack, 336 | local_vars, 337 | method, 338 | }; 339 | let thread = Thread::new().push_frame(frame); 340 | let (ExecuteResult { thread: _, offset }, _) = 341 | IFLT(CodeReader::new(Rc::new(vec![1, 1])), thread); 342 | assert_eq!(offset, 257); 343 | } 344 | 345 | #[allow(non_snake_case)] 346 | fn test_IFLT_fail() { 347 | let method = Rc::new(Method::new(MemberInfo { 348 | access_flags: 0u16, 349 | name: "".to_string(), 350 | name_index: 0u16, 351 | descriptor_index: 0u16, 352 | descriptor: "".to_string(), 353 | attributes: vec![], 354 | })); 355 | let class = Rc::new(Class { 356 | access_flags: 0u16, 357 | name: "".to_string(), 358 | constant_pool: ConstantPool { 359 | vec_map: VecMap::new(), 360 | }, 361 | fields: Vec::new(), 362 | methods: Vec::new(), 363 | super_class: None, 364 | instance_slot_count: 0usize, 365 | static_slot_count: 0usize, 366 | static_vars: Vars::new(2), 367 | }); 368 | let frame = Frame::new(class, method); 369 | let Frame { 370 | operand_stack, 371 | local_vars, 372 | method, 373 | class, 374 | } = frame; 375 | 376 | let operand_stack = operand_stack.push_int(0); 377 | let frame = Frame { 378 | class, 379 | operand_stack, 380 | local_vars, 381 | method, 382 | }; 383 | let thread = Thread::new().push_frame(frame); 384 | let (ExecuteResult { thread: _, offset }, _) = 385 | IFLT(CodeReader::new(Rc::new(vec![1, 1])), thread); 386 | assert_eq!(offset, 0); 387 | } 388 | 389 | #[test] 390 | #[allow(non_snake_case)] 391 | fn test_IFGE_success() { 392 | let method = Rc::new(Method::new(MemberInfo { 393 | access_flags: 0u16, 394 | name: "".to_string(), 395 | name_index: 0u16, 396 | descriptor_index: 0u16, 397 | descriptor: "".to_string(), 398 | attributes: vec![], 399 | })); 400 | let class = Rc::new(Class { 401 | access_flags: 0u16, 402 | name: "".to_string(), 403 | constant_pool: ConstantPool { 404 | vec_map: VecMap::new(), 405 | }, 406 | fields: Vec::new(), 407 | methods: Vec::new(), 408 | super_class: None, 409 | instance_slot_count: 0usize, 410 | static_slot_count: 0usize, 411 | static_vars: Vars::new(2), 412 | }); 413 | let frame = Frame::new(class, method); 414 | let Frame { 415 | operand_stack, 416 | local_vars, 417 | method, 418 | class, 419 | } = frame; 420 | 421 | let operand_stack = operand_stack.push_int(0); 422 | let frame = Frame { 423 | class, 424 | operand_stack, 425 | local_vars, 426 | method, 427 | }; 428 | let thread = Thread::new().push_frame(frame); 429 | let (ExecuteResult { thread: _, offset }, _) = 430 | IFGE(CodeReader::new(Rc::new(vec![1, 1])), thread); 431 | assert_eq!(offset, 257); 432 | } 433 | 434 | #[allow(non_snake_case)] 435 | fn test_IFGE_fail() { 436 | let method = Rc::new(Method::new(MemberInfo { 437 | access_flags: 0u16, 438 | name: "".to_string(), 439 | name_index: 0u16, 440 | descriptor_index: 0u16, 441 | descriptor: "".to_string(), 442 | attributes: vec![], 443 | })); 444 | let class = Rc::new(Class { 445 | access_flags: 0u16, 446 | name: "".to_string(), 447 | constant_pool: ConstantPool { 448 | vec_map: VecMap::new(), 449 | }, 450 | fields: Vec::new(), 451 | methods: Vec::new(), 452 | super_class: None, 453 | instance_slot_count: 0usize, 454 | static_slot_count: 0usize, 455 | static_vars: Vars::new(2), 456 | }); 457 | let frame = Frame::new(class, method); 458 | let Frame { 459 | operand_stack, 460 | local_vars, 461 | method, 462 | class, 463 | } = frame; 464 | 465 | let operand_stack = operand_stack.push_int(-1); 466 | let frame = Frame { 467 | class, 468 | operand_stack, 469 | local_vars, 470 | method, 471 | }; 472 | let thread = Thread::new().push_frame(frame); 473 | let (ExecuteResult { thread: _, offset }, _) = 474 | IFGE(CodeReader::new(Rc::new(vec![1, 1])), thread); 475 | assert_eq!(offset, 0); 476 | } 477 | 478 | #[test] 479 | #[allow(non_snake_case)] 480 | fn test_IFGT_success() { 481 | let method = Rc::new(Method::new(MemberInfo { 482 | access_flags: 0u16, 483 | name: "".to_string(), 484 | name_index: 0u16, 485 | descriptor_index: 0u16, 486 | descriptor: "".to_string(), 487 | attributes: vec![], 488 | })); 489 | let class = Rc::new(Class { 490 | access_flags: 0u16, 491 | name: "".to_string(), 492 | constant_pool: ConstantPool { 493 | vec_map: VecMap::new(), 494 | }, 495 | fields: Vec::new(), 496 | methods: Vec::new(), 497 | super_class: None, 498 | instance_slot_count: 0usize, 499 | static_slot_count: 0usize, 500 | static_vars: Vars::new(2), 501 | }); 502 | let frame = Frame::new(class, method); 503 | let Frame { 504 | operand_stack, 505 | local_vars, 506 | method, 507 | class, 508 | } = frame; 509 | 510 | let operand_stack = operand_stack.push_int(1); 511 | let frame = Frame { 512 | class, 513 | operand_stack, 514 | local_vars, 515 | method, 516 | }; 517 | let thread = Thread::new().push_frame(frame); 518 | let (ExecuteResult { thread: _, offset }, _) = 519 | IFGT(CodeReader::new(Rc::new(vec![1, 1])), thread); 520 | assert_eq!(offset, 257); 521 | } 522 | 523 | #[allow(non_snake_case)] 524 | fn test_IFGT_fail() { 525 | let method = Rc::new(Method::new(MemberInfo { 526 | access_flags: 0u16, 527 | name: "".to_string(), 528 | name_index: 0u16, 529 | descriptor_index: 0u16, 530 | descriptor: "".to_string(), 531 | attributes: vec![], 532 | })); 533 | let class = Rc::new(Class { 534 | access_flags: 0u16, 535 | name: "".to_string(), 536 | constant_pool: ConstantPool { 537 | vec_map: VecMap::new(), 538 | }, 539 | fields: Vec::new(), 540 | methods: Vec::new(), 541 | super_class: None, 542 | instance_slot_count: 0usize, 543 | static_slot_count: 0usize, 544 | static_vars: Vars::new(2), 545 | }); 546 | let frame = Frame::new(class, method); 547 | let Frame { 548 | operand_stack, 549 | local_vars, 550 | method, 551 | class, 552 | } = frame; 553 | 554 | let operand_stack = operand_stack.push_int(0); 555 | let frame = Frame { 556 | class, 557 | operand_stack, 558 | local_vars, 559 | method, 560 | }; 561 | let thread = Thread::new().push_frame(frame); 562 | let (ExecuteResult { thread: _, offset }, _) = 563 | IFGT(CodeReader::new(Rc::new(vec![1, 1])), thread); 564 | assert_eq!(offset, 0); 565 | } 566 | 567 | #[test] 568 | #[allow(non_snake_case)] 569 | fn test_IFLE_success() { 570 | let method = Rc::new(Method::new(MemberInfo { 571 | access_flags: 0u16, 572 | name: "".to_string(), 573 | name_index: 0u16, 574 | descriptor_index: 0u16, 575 | descriptor: "".to_string(), 576 | attributes: vec![], 577 | })); 578 | let class = Rc::new(Class { 579 | access_flags: 0u16, 580 | name: "".to_string(), 581 | constant_pool: ConstantPool { 582 | vec_map: VecMap::new(), 583 | }, 584 | fields: Vec::new(), 585 | methods: Vec::new(), 586 | super_class: None, 587 | instance_slot_count: 0usize, 588 | static_slot_count: 0usize, 589 | static_vars: Vars::new(2), 590 | }); 591 | let frame = Frame::new(class, method); 592 | let Frame { 593 | operand_stack, 594 | local_vars, 595 | method, 596 | class, 597 | } = frame; 598 | 599 | let operand_stack = operand_stack.push_int(0); 600 | let frame = Frame { 601 | class, 602 | operand_stack, 603 | local_vars, 604 | method, 605 | }; 606 | let thread = Thread::new().push_frame(frame); 607 | let (ExecuteResult { thread: _, offset }, _) = 608 | IFLE(CodeReader::new(Rc::new(vec![1, 1])), thread); 609 | assert_eq!(offset, 257); 610 | } 611 | 612 | #[allow(non_snake_case)] 613 | fn test_IFLE_fail() { 614 | let method = Rc::new(Method::new(MemberInfo { 615 | access_flags: 0u16, 616 | name: "".to_string(), 617 | name_index: 0u16, 618 | descriptor_index: 0u16, 619 | descriptor: "".to_string(), 620 | attributes: vec![], 621 | })); 622 | let class = Rc::new(Class { 623 | access_flags: 0u16, 624 | name: "".to_string(), 625 | constant_pool: ConstantPool { 626 | vec_map: VecMap::new(), 627 | }, 628 | fields: Vec::new(), 629 | methods: Vec::new(), 630 | super_class: None, 631 | instance_slot_count: 0usize, 632 | static_slot_count: 0usize, 633 | static_vars: Vars::new(2), 634 | }); 635 | let frame = Frame::new(class, method); 636 | let Frame { 637 | operand_stack, 638 | local_vars, 639 | method, 640 | class, 641 | } = frame; 642 | 643 | let operand_stack = operand_stack.push_int(1); 644 | let frame = Frame { 645 | class, 646 | operand_stack, 647 | local_vars, 648 | method, 649 | }; 650 | let thread = Thread::new().push_frame(frame); 651 | let (ExecuteResult { thread: _, offset }, _) = 652 | IFLE(CodeReader::new(Rc::new(vec![1, 1])), thread); 653 | assert_eq!(offset, 0); 654 | } 655 | } 656 | -------------------------------------------------------------------------------- /src/instruction/comparison/if_icmp.rs: -------------------------------------------------------------------------------- 1 | use instruction::instruction::ExecuteResult; 2 | use rtda::frame::Frame; 3 | use rtda::thread::Thread; 4 | use util::code_reader::CodeReader; 5 | 6 | #[allow(non_snake_case)] 7 | fn _icmpPop(frame: Frame) -> (i32, i32, Frame) { 8 | let Frame { 9 | operand_stack, 10 | local_vars, 11 | method, 12 | class, 13 | } = frame; 14 | 15 | let (val2, operand_stack) = operand_stack.pop_int(); 16 | let (val1, operand_stack) = operand_stack.pop_int(); 17 | 18 | let frame = Frame { 19 | class, 20 | operand_stack, 21 | local_vars, 22 | method, 23 | }; 24 | (val1, val2, frame) 25 | } 26 | 27 | #[allow(non_snake_case)] 28 | pub fn IF_ICMPGT(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 29 | println!("IF_ICMPGT"); 30 | let (offset, code_reader) = code_reader.read_i16(); 31 | 32 | let (frame, thread) = thread.pop_frame(); 33 | 34 | let (val1, val2, frame) = _icmpPop(frame); 35 | let offset = if val1 > val2 { offset as isize } else { 0 }; 36 | let thread = thread.push_frame(frame); 37 | let execute_result = ExecuteResult { thread, offset }; 38 | (execute_result, code_reader) 39 | } 40 | 41 | #[allow(non_snake_case)] 42 | pub fn IF_ICMPGE(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 43 | println!("IF_ICMPGE"); 44 | let (offset, code_reader) = code_reader.read_i16(); 45 | 46 | let (frame, thread) = thread.pop_frame(); 47 | 48 | let (val1, val2, frame) = _icmpPop(frame); 49 | let offset = if val1 >= val2 { offset as isize } else { 0 }; 50 | let thread = thread.push_frame(frame); 51 | let execute_result = ExecuteResult { thread, offset }; 52 | (execute_result, code_reader) 53 | } 54 | 55 | #[allow(non_snake_case)] 56 | pub fn IF_ICMPEQ(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 57 | println!("IF_ICMPEQ"); 58 | let (offset, code_reader) = code_reader.read_i16(); 59 | 60 | let (frame, thread) = thread.pop_frame(); 61 | 62 | let (val1, val2, frame) = _icmpPop(frame); 63 | let offset = if val1 == val2 { offset as isize } else { 0 }; 64 | let thread = thread.push_frame(frame); 65 | let execute_result = ExecuteResult { thread, offset }; 66 | (execute_result, code_reader) 67 | } 68 | 69 | #[allow(non_snake_case)] 70 | pub fn IF_ICMPNE(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 71 | println!("IF_ICMPNE"); 72 | let (offset, code_reader) = code_reader.read_i16(); 73 | 74 | let (frame, thread) = thread.pop_frame(); 75 | 76 | let (val1, val2, frame) = _icmpPop(frame); 77 | let offset = if val1 != val2 { offset as isize } else { 0 }; 78 | let thread = thread.push_frame(frame); 79 | let execute_result = ExecuteResult { thread, offset }; 80 | (execute_result, code_reader) 81 | } 82 | 83 | #[allow(non_snake_case)] 84 | pub fn IF_ICMPLT(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 85 | println!("IF_ICMPLT"); 86 | let (offset, code_reader) = code_reader.read_i16(); 87 | 88 | let (frame, thread) = thread.pop_frame(); 89 | 90 | let (val1, val2, frame) = _icmpPop(frame); 91 | let offset = if val1 < val2 { offset as isize } else { 0 }; 92 | let thread = thread.push_frame(frame); 93 | let execute_result = ExecuteResult { thread, offset }; 94 | (execute_result, code_reader) 95 | } 96 | 97 | #[allow(non_snake_case)] 98 | pub fn IF_ICMPLE(code_reader: CodeReader, thread: Thread) -> (ExecuteResult, CodeReader) { 99 | println!("IF_ICMPLE"); 100 | let (offset, code_reader) = code_reader.read_i16(); 101 | 102 | let (frame, thread) = thread.pop_frame(); 103 | 104 | let (val1, val2, frame) = _icmpPop(frame); 105 | let offset = if val1 <= val2 { offset as isize } else { 0 }; 106 | let thread = thread.push_frame(frame); 107 | let execute_result = ExecuteResult { thread, offset }; 108 | (execute_result, code_reader) 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use classfile::constant_pool::ConstantPool; 114 | use classfile::member_info::MemberInfo; 115 | use instruction::comparison::if_icmp::*; 116 | use instruction::instruction::ExecuteResult; 117 | use rtda::frame::Frame; 118 | use rtda::heap::class::Class; 119 | use rtda::heap::method::Method; 120 | use rtda::thread::Thread; 121 | use rtda::vars::Vars; 122 | use std::rc::Rc; 123 | use util::code_reader::CodeReader; 124 | use vec_map::VecMap; 125 | 126 | #[test] 127 | #[allow(non_snake_case)] 128 | fn test_IF_ICMPGT_success() { 129 | let method = Rc::new(Method::new(MemberInfo { 130 | access_flags: 0u16, 131 | name: "".to_string(), 132 | name_index: 0u16, 133 | descriptor_index: 0u16, 134 | descriptor: "".to_string(), 135 | attributes: vec![], 136 | })); 137 | 138 | let class = Rc::new(Class { 139 | access_flags: 0u16, 140 | name: "".to_string(), 141 | constant_pool: ConstantPool { 142 | vec_map: VecMap::new(), 143 | }, 144 | fields: Vec::new(), 145 | methods: Vec::new(), 146 | super_class: None, 147 | instance_slot_count: 0usize, 148 | static_slot_count: 0usize, 149 | static_vars: Vars::new(2), 150 | }); 151 | let frame = Frame::new(class, method); 152 | let Frame { 153 | operand_stack, 154 | local_vars, 155 | method, 156 | class, 157 | } = frame; 158 | 159 | let operand_stack = operand_stack.push_int(1); 160 | let operand_stack = operand_stack.push_int(0); 161 | let frame = Frame { 162 | class, 163 | operand_stack, 164 | local_vars, 165 | method, 166 | }; 167 | let thread = Thread::new().push_frame(frame); 168 | let (ExecuteResult { thread: _, offset }, _) = 169 | IF_ICMPGT(CodeReader::new(Rc::new(vec![1, 1])), thread); 170 | assert_eq!(offset, 257); 171 | } 172 | 173 | #[test] 174 | #[allow(non_snake_case)] 175 | fn test_IF_ICMPGT_fail() { 176 | let method = Rc::new(Method::new(MemberInfo { 177 | access_flags: 0u16, 178 | name: "".to_string(), 179 | name_index: 0u16, 180 | descriptor_index: 0u16, 181 | descriptor: "".to_string(), 182 | attributes: vec![], 183 | })); 184 | 185 | let class = Rc::new(Class { 186 | access_flags: 0u16, 187 | name: "".to_string(), 188 | constant_pool: ConstantPool { 189 | vec_map: VecMap::new(), 190 | }, 191 | fields: Vec::new(), 192 | methods: Vec::new(), 193 | super_class: None, 194 | instance_slot_count: 0usize, 195 | static_slot_count: 0usize, 196 | static_vars: Vars::new(2), 197 | }); 198 | 199 | let frame = Frame::new(class, method); 200 | let Frame { 201 | operand_stack, 202 | local_vars, 203 | method, 204 | class, 205 | } = frame; 206 | 207 | let operand_stack = operand_stack.push_int(1); 208 | let operand_stack = operand_stack.push_int(2); 209 | let frame = Frame { 210 | class, 211 | operand_stack, 212 | local_vars, 213 | method, 214 | }; 215 | let thread = Thread::new().push_frame(frame); 216 | let (ExecuteResult { thread: _, offset }, _) = 217 | IF_ICMPGT(CodeReader::new(Rc::new(vec![1, 1])), thread); 218 | assert_eq!(offset, 0); 219 | } 220 | 221 | #[test] 222 | #[allow(non_snake_case)] 223 | fn test_IF_ICMPGE_success() { 224 | let method = Rc::new(Method::new(MemberInfo { 225 | access_flags: 0u16, 226 | name: "".to_string(), 227 | name_index: 0u16, 228 | descriptor_index: 0u16, 229 | descriptor: "".to_string(), 230 | attributes: vec![], 231 | })); 232 | 233 | let class = Rc::new(Class { 234 | access_flags: 0u16, 235 | name: "".to_string(), 236 | constant_pool: ConstantPool { 237 | vec_map: VecMap::new(), 238 | }, 239 | fields: Vec::new(), 240 | methods: Vec::new(), 241 | super_class: None, 242 | instance_slot_count: 0usize, 243 | static_slot_count: 0usize, 244 | static_vars: Vars::new(2), 245 | }); 246 | let frame = Frame::new(class, method); 247 | let Frame { 248 | operand_stack, 249 | local_vars, 250 | method, 251 | class, 252 | } = frame; 253 | 254 | let operand_stack = operand_stack.push_int(1); 255 | let operand_stack = operand_stack.push_int(1); 256 | let frame = Frame { 257 | class, 258 | operand_stack, 259 | local_vars, 260 | method, 261 | }; 262 | let thread = Thread::new().push_frame(frame); 263 | let (ExecuteResult { thread: _, offset }, _) = 264 | IF_ICMPGE(CodeReader::new(Rc::new(vec![1, 1])), thread); 265 | assert_eq!(offset, 257); 266 | } 267 | 268 | #[test] 269 | #[allow(non_snake_case)] 270 | fn test_IF_ICMPGE_fail() { 271 | let method = Rc::new(Method::new(MemberInfo { 272 | access_flags: 0u16, 273 | name: "".to_string(), 274 | name_index: 0u16, 275 | descriptor_index: 0u16, 276 | descriptor: "".to_string(), 277 | attributes: vec![], 278 | })); 279 | 280 | let class = Rc::new(Class { 281 | access_flags: 0u16, 282 | name: "".to_string(), 283 | constant_pool: ConstantPool { 284 | vec_map: VecMap::new(), 285 | }, 286 | fields: Vec::new(), 287 | methods: Vec::new(), 288 | super_class: None, 289 | instance_slot_count: 0usize, 290 | static_slot_count: 0usize, 291 | static_vars: Vars::new(2), 292 | }); 293 | 294 | let frame = Frame::new(class, method); 295 | let Frame { 296 | operand_stack, 297 | local_vars, 298 | method, 299 | class, 300 | } = frame; 301 | 302 | let operand_stack = operand_stack.push_int(0); 303 | let operand_stack = operand_stack.push_int(2); 304 | let frame = Frame { 305 | class, 306 | operand_stack, 307 | local_vars, 308 | method, 309 | }; 310 | let thread = Thread::new().push_frame(frame); 311 | let (ExecuteResult { thread: _, offset }, _) = 312 | IF_ICMPGE(CodeReader::new(Rc::new(vec![1, 1])), thread); 313 | assert_eq!(offset, 0); 314 | } 315 | 316 | #[test] 317 | #[allow(non_snake_case)] 318 | fn test_IF_ICMPEQ_success() { 319 | let method = Rc::new(Method::new(MemberInfo { 320 | access_flags: 0u16, 321 | name: "".to_string(), 322 | name_index: 0u16, 323 | descriptor_index: 0u16, 324 | descriptor: "".to_string(), 325 | attributes: vec![], 326 | })); 327 | let class = Rc::new(Class { 328 | access_flags: 0u16, 329 | name: "".to_string(), 330 | constant_pool: ConstantPool { 331 | vec_map: VecMap::new(), 332 | }, 333 | fields: Vec::new(), 334 | methods: Vec::new(), 335 | super_class: None, 336 | instance_slot_count: 0usize, 337 | static_slot_count: 0usize, 338 | static_vars: Vars::new(2), 339 | }); 340 | let frame = Frame::new(class, method); 341 | let Frame { 342 | operand_stack, 343 | local_vars, 344 | method, 345 | class, 346 | } = frame; 347 | 348 | let operand_stack = operand_stack.push_int(-1); 349 | let operand_stack = operand_stack.push_int(-1); 350 | let frame = Frame { 351 | class, 352 | operand_stack, 353 | local_vars, 354 | method, 355 | }; 356 | let thread = Thread::new().push_frame(frame); 357 | let (ExecuteResult { thread: _, offset }, _) = 358 | IF_ICMPEQ(CodeReader::new(Rc::new(vec![1, 1])), thread); 359 | assert_eq!(offset, 257); 360 | } 361 | 362 | #[test] 363 | #[allow(non_snake_case)] 364 | fn test_IF_ICMPEQ_fail() { 365 | let method = Rc::new(Method::new(MemberInfo { 366 | access_flags: 0u16, 367 | name: "".to_string(), 368 | name_index: 0u16, 369 | descriptor_index: 0u16, 370 | descriptor: "".to_string(), 371 | attributes: vec![], 372 | })); 373 | let class = Rc::new(Class { 374 | access_flags: 0u16, 375 | name: "".to_string(), 376 | constant_pool: ConstantPool { 377 | vec_map: VecMap::new(), 378 | }, 379 | fields: Vec::new(), 380 | methods: Vec::new(), 381 | super_class: None, 382 | instance_slot_count: 0usize, 383 | static_slot_count: 0usize, 384 | static_vars: Vars::new(2), 385 | }); 386 | let frame = Frame::new(class, method); 387 | let Frame { 388 | operand_stack, 389 | local_vars, 390 | method, 391 | class, 392 | } = frame; 393 | 394 | let operand_stack = operand_stack.push_int(0); 395 | let operand_stack = operand_stack.push_int(1); 396 | let frame = Frame { 397 | class, 398 | operand_stack, 399 | local_vars, 400 | method, 401 | }; 402 | let thread = Thread::new().push_frame(frame); 403 | let (ExecuteResult { thread: _, offset }, _) = 404 | IF_ICMPEQ(CodeReader::new(Rc::new(vec![1, 1])), thread); 405 | assert_eq!(offset, 0); 406 | } 407 | 408 | #[test] 409 | #[allow(non_snake_case)] 410 | fn test_IF_ICMPNE_success() { 411 | let method = Rc::new(Method::new(MemberInfo { 412 | access_flags: 0u16, 413 | name: "".to_string(), 414 | name_index: 0u16, 415 | descriptor_index: 0u16, 416 | descriptor: "".to_string(), 417 | attributes: vec![], 418 | })); 419 | let class = Rc::new(Class { 420 | access_flags: 0u16, 421 | name: "".to_string(), 422 | constant_pool: ConstantPool { 423 | vec_map: VecMap::new(), 424 | }, 425 | fields: Vec::new(), 426 | methods: Vec::new(), 427 | super_class: None, 428 | instance_slot_count: 0usize, 429 | static_slot_count: 0usize, 430 | static_vars: Vars::new(2), 431 | }); 432 | let frame = Frame::new(class, method); 433 | let Frame { 434 | operand_stack, 435 | local_vars, 436 | method, 437 | class, 438 | } = frame; 439 | 440 | let operand_stack = operand_stack.push_int(0); 441 | let operand_stack = operand_stack.push_int(1); 442 | let frame = Frame { 443 | class, 444 | operand_stack, 445 | local_vars, 446 | method, 447 | }; 448 | let thread = Thread::new().push_frame(frame); 449 | let (ExecuteResult { thread: _, offset }, _) = 450 | IF_ICMPNE(CodeReader::new(Rc::new(vec![1, 1])), thread); 451 | assert_eq!(offset, 257); 452 | } 453 | 454 | #[test] 455 | #[allow(non_snake_case)] 456 | fn test_IF_ICMPNE_fail() { 457 | let method = Rc::new(Method::new(MemberInfo { 458 | access_flags: 0u16, 459 | name: "".to_string(), 460 | name_index: 0u16, 461 | descriptor_index: 0u16, 462 | descriptor: "".to_string(), 463 | attributes: vec![], 464 | })); 465 | let class = Rc::new(Class { 466 | access_flags: 0u16, 467 | name: "".to_string(), 468 | constant_pool: ConstantPool { 469 | vec_map: VecMap::new(), 470 | }, 471 | fields: Vec::new(), 472 | methods: Vec::new(), 473 | super_class: None, 474 | instance_slot_count: 0usize, 475 | static_slot_count: 0usize, 476 | static_vars: Vars::new(2), 477 | }); 478 | let frame = Frame::new(class, method); 479 | let Frame { 480 | operand_stack, 481 | local_vars, 482 | method, 483 | class, 484 | } = frame; 485 | 486 | let operand_stack = operand_stack.push_int(-1); 487 | let operand_stack = operand_stack.push_int(-1); 488 | let frame = Frame { 489 | class, 490 | operand_stack, 491 | local_vars, 492 | method, 493 | }; 494 | let thread = Thread::new().push_frame(frame); 495 | let (ExecuteResult { thread: _, offset }, _) = 496 | IF_ICMPNE(CodeReader::new(Rc::new(vec![1, 1])), thread); 497 | assert_eq!(offset, 0); 498 | } 499 | 500 | #[test] 501 | #[allow(non_snake_case)] 502 | fn test_IF_ICMPLT_success() { 503 | let method = Rc::new(Method::new(MemberInfo { 504 | access_flags: 0u16, 505 | name: "".to_string(), 506 | name_index: 0u16, 507 | descriptor_index: 0u16, 508 | descriptor: "".to_string(), 509 | attributes: vec![], 510 | })); 511 | let class = Rc::new(Class { 512 | access_flags: 0u16, 513 | name: "".to_string(), 514 | constant_pool: ConstantPool { 515 | vec_map: VecMap::new(), 516 | }, 517 | fields: Vec::new(), 518 | methods: Vec::new(), 519 | super_class: None, 520 | instance_slot_count: 0usize, 521 | static_slot_count: 0usize, 522 | static_vars: Vars::new(2), 523 | }); 524 | let frame = Frame::new(class, method); 525 | let Frame { 526 | operand_stack, 527 | local_vars, 528 | method, 529 | class, 530 | } = frame; 531 | 532 | let operand_stack = operand_stack.push_int(1); 533 | let operand_stack = operand_stack.push_int(2); 534 | let frame = Frame { 535 | class, 536 | operand_stack, 537 | local_vars, 538 | method, 539 | }; 540 | let thread = Thread::new().push_frame(frame); 541 | let (ExecuteResult { thread: _, offset }, _) = 542 | IF_ICMPLT(CodeReader::new(Rc::new(vec![1, 1])), thread); 543 | assert_eq!(offset, 257); 544 | } 545 | 546 | #[test] 547 | #[allow(non_snake_case)] 548 | fn test_IF_ICMPLT_fail() { 549 | let method = Rc::new(Method::new(MemberInfo { 550 | access_flags: 0u16, 551 | name: "".to_string(), 552 | name_index: 0u16, 553 | descriptor_index: 0u16, 554 | descriptor: "".to_string(), 555 | attributes: vec![], 556 | })); 557 | let class = Rc::new(Class { 558 | access_flags: 0u16, 559 | name: "".to_string(), 560 | constant_pool: ConstantPool { 561 | vec_map: VecMap::new(), 562 | }, 563 | fields: Vec::new(), 564 | methods: Vec::new(), 565 | super_class: None, 566 | instance_slot_count: 0usize, 567 | static_slot_count: 0usize, 568 | static_vars: Vars::new(2), 569 | }); 570 | let frame = Frame::new(class, method); 571 | let Frame { 572 | operand_stack, 573 | local_vars, 574 | method, 575 | class, 576 | } = frame; 577 | 578 | let operand_stack = operand_stack.push_int(0); 579 | let operand_stack = operand_stack.push_int(0); 580 | let frame = Frame { 581 | class, 582 | operand_stack, 583 | local_vars, 584 | method, 585 | }; 586 | let thread = Thread::new().push_frame(frame); 587 | let (ExecuteResult { thread: _, offset }, _) = 588 | IF_ICMPLT(CodeReader::new(Rc::new(vec![1, 1])), thread); 589 | assert_eq!(offset, 0); 590 | } 591 | 592 | #[test] 593 | #[allow(non_snake_case)] 594 | fn test_IF_ICMPLE_success() { 595 | let method = Rc::new(Method::new(MemberInfo { 596 | access_flags: 0u16, 597 | name: "".to_string(), 598 | name_index: 0u16, 599 | descriptor_index: 0u16, 600 | descriptor: "".to_string(), 601 | attributes: vec![], 602 | })); 603 | let class = Rc::new(Class { 604 | access_flags: 0u16, 605 | name: "".to_string(), 606 | constant_pool: ConstantPool { 607 | vec_map: VecMap::new(), 608 | }, 609 | fields: Vec::new(), 610 | methods: Vec::new(), 611 | super_class: None, 612 | instance_slot_count: 0usize, 613 | static_slot_count: 0usize, 614 | static_vars: Vars::new(2), 615 | }); 616 | let frame = Frame::new(class, method); 617 | let Frame { 618 | operand_stack, 619 | local_vars, 620 | method, 621 | class, 622 | } = frame; 623 | 624 | let operand_stack = operand_stack.push_int(0); 625 | let operand_stack = operand_stack.push_int(0); 626 | let frame = Frame { 627 | class, 628 | operand_stack, 629 | local_vars, 630 | method, 631 | }; 632 | let thread = Thread::new().push_frame(frame); 633 | let (ExecuteResult { thread: _, offset }, _) = 634 | IF_ICMPLE(CodeReader::new(Rc::new(vec![1, 1])), thread); 635 | assert_eq!(offset, 257); 636 | } 637 | 638 | #[test] 639 | #[allow(non_snake_case)] 640 | fn test_IF_ICMPLE_fail() { 641 | let method = Rc::new(Method::new(MemberInfo { 642 | access_flags: 0u16, 643 | name: "".to_string(), 644 | name_index: 0u16, 645 | descriptor_index: 0u16, 646 | descriptor: "".to_string(), 647 | attributes: vec![], 648 | })); 649 | let class = Rc::new(Class { 650 | access_flags: 0u16, 651 | name: "".to_string(), 652 | constant_pool: ConstantPool { 653 | vec_map: VecMap::new(), 654 | }, 655 | fields: Vec::new(), 656 | methods: Vec::new(), 657 | super_class: None, 658 | instance_slot_count: 0usize, 659 | static_slot_count: 0usize, 660 | static_vars: Vars::new(2), 661 | }); 662 | let frame = Frame::new(class, method); 663 | let Frame { 664 | operand_stack, 665 | local_vars, 666 | method, 667 | class, 668 | } = frame; 669 | 670 | let operand_stack = operand_stack.push_int(2); 671 | let operand_stack = operand_stack.push_int(1); 672 | let frame = Frame { 673 | class, 674 | operand_stack, 675 | local_vars, 676 | method, 677 | }; 678 | let thread = Thread::new().push_frame(frame); 679 | let (ExecuteResult { thread: _, offset }, _) = 680 | IF_ICMPLE(CodeReader::new(Rc::new(vec![1, 1])), thread); 681 | assert_eq!(offset, 0); 682 | } 683 | } 684 | -------------------------------------------------------------------------------- /src/classfile/class_reader.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | 3 | use vec_map::VecMap; 4 | 5 | use self::byteorder::{BigEndian, ByteOrder}; 6 | 7 | use classfile::attribute_info::{ 8 | AttributeInfo, ExceptionTableEntry, LineNumberTableEntry, LocalVariableTableEntry, 9 | }; 10 | use classfile::class_file::ClassFile; 11 | use classfile::constant_info::ConstantInfo; 12 | use classfile::constant_pool::ConstantPool; 13 | use classfile::member_info::MemberInfo; 14 | use std::rc::Rc; 15 | use util::modified_utf8::from_modified_utf8; 16 | 17 | const CONSTANT_UTF8: u8 = 1; 18 | const CONSTANT_INTEGER: u8 = 3; 19 | const CONSTANT_FLOAT: u8 = 4; 20 | const CONSTANT_LONG: u8 = 5; 21 | const CONSTANT_DOUBLE: u8 = 6; 22 | const CONSTANT_CLASS: u8 = 7; 23 | const CONSTANT_STRING: u8 = 8; 24 | const CONSTANT_FIELDREF: u8 = 9; 25 | const CONSTANT_METHODREF: u8 = 10; 26 | const CONSTANT_INTERFACE_METHODREF: u8 = 11; 27 | const CONSTANT_NAME_AND_TYPE: u8 = 12; 28 | 29 | #[derive(Debug)] 30 | pub struct VersionInfo { 31 | major_version: u16, 32 | minor_version: u16, 33 | } 34 | 35 | pub trait ClassReader { 36 | fn read_u8(&self) -> (u8, &[u8]); 37 | fn read_u16(&self) -> (u16, &[u8]); 38 | fn read_u16s(&self) -> (Vec, &[u8]); 39 | fn read_u32(&self) -> (u32, &[u8]); 40 | fn read_i32(&self) -> (i32, &[u8]); 41 | fn read_f32(&self) -> (f32, &[u8]); 42 | fn read_i64(&self) -> (i64, &[u8]); 43 | fn read_f64(&self) -> (f64, &[u8]); 44 | fn read_bytes(&self, n: usize) -> (&[u8], &[u8]); 45 | fn read_and_check_magic(&self) -> (u32, &[u8]); 46 | fn read_and_check_version(&self) -> (VersionInfo, &[u8]); 47 | fn read_constant_info(&self) -> (ConstantInfo, &[u8]); 48 | fn read_constant_pool(&self) -> (ConstantPool, &[u8]); 49 | fn read_access_flags(&self) -> (u16, &[u8]); 50 | fn read_this_class(&self) -> (u16, &[u8]); 51 | fn read_super_class(&self) -> (u16, &[u8]); 52 | fn read_interfaces(&self) -> (Vec, &[u8]); 53 | fn read_member(&self, constant_pool: &ConstantPool) -> (MemberInfo, &[u8]); 54 | fn read_members(&self, constant_pool: &ConstantPool) -> (Vec, &[u8]); 55 | fn read_exception_table(&self) -> (Vec, &[u8]); 56 | fn read_line_number_table(&self) -> (Vec, &[u8]); 57 | fn read_local_variable_table(&self) -> (Vec, &[u8]); 58 | fn read_attribute(&self, constant_pool: &ConstantPool) -> (AttributeInfo, &[u8]); 59 | fn read_attributes(&self, constant_pool: &ConstantPool) -> (Vec, &[u8]); 60 | fn parse(&self) -> ClassFile; 61 | } 62 | 63 | impl ClassReader for [u8] { 64 | fn read_u8(&self) -> (u8, &[u8]) { 65 | let (a, b) = self.split_at(1); 66 | (a[0], b) 67 | } 68 | 69 | fn read_u16(&self) -> (u16, &[u8]) { 70 | let (a, b) = self.split_at(2); 71 | (BigEndian::read_u16(a), b) 72 | } 73 | 74 | fn read_u16s(&self) -> (Vec, &[u8]) { 75 | let (n, after_n) = self.read_u16(); 76 | let mut s: Vec = Vec::with_capacity(n as usize); 77 | let mut rest = after_n; 78 | for _ in 1..=n { 79 | let (value, next_rest) = rest.read_u16(); 80 | s.push(value); 81 | rest = next_rest; 82 | } 83 | (s, rest) 84 | } 85 | 86 | fn read_u32(&self) -> (u32, &[u8]) { 87 | let (a, b) = self.split_at(4); 88 | (BigEndian::read_u32(a), b) 89 | } 90 | 91 | fn read_i32(&self) -> (i32, &[u8]) { 92 | let (a, b) = self.split_at(4); 93 | (BigEndian::read_i32(a), b) 94 | } 95 | 96 | fn read_f32(&self) -> (f32, &[u8]) { 97 | let (a, b) = self.split_at(4); 98 | (BigEndian::read_f32(a), b) 99 | } 100 | 101 | fn read_i64(&self) -> (i64, &[u8]) { 102 | let (a, b) = self.split_at(8); 103 | (BigEndian::read_i64(a), b) 104 | } 105 | 106 | fn read_f64(&self) -> (f64, &[u8]) { 107 | let (a, b) = self.split_at(8); 108 | (BigEndian::read_f64(a), b) 109 | } 110 | 111 | fn read_bytes(&self, n: usize) -> (&[u8], &[u8]) { 112 | self.split_at(n) 113 | } 114 | 115 | fn read_and_check_magic(&self) -> (u32, &[u8]) { 116 | let result = self.read_u32(); 117 | let (magic, _) = result; 118 | assert_eq!(magic, 0xCAFEBABE); 119 | result 120 | } 121 | 122 | fn read_and_check_version(&self) -> (VersionInfo, &[u8]) { 123 | let (minor_version, after_minor_version) = self.read_u16(); 124 | let (major_version, after_major_version) = after_minor_version.read_u16(); 125 | assert_eq!(major_version, 52); 126 | assert_eq!(minor_version, 0); 127 | let version_info = VersionInfo { 128 | major_version, 129 | minor_version, 130 | }; 131 | (version_info, after_major_version) 132 | } 133 | 134 | fn read_constant_info(&self) -> (ConstantInfo, &[u8]) { 135 | let (tag, after_tag) = self.read_u8(); 136 | match tag { 137 | CONSTANT_INTEGER => { 138 | let (val, rest) = after_tag.read_i32(); 139 | (ConstantInfo::Integer(val), rest) 140 | } 141 | CONSTANT_FLOAT => { 142 | let (val, rest) = after_tag.read_f32(); 143 | (ConstantInfo::Float(val), rest) 144 | } 145 | CONSTANT_LONG => { 146 | let (val, rest) = after_tag.read_i64(); 147 | (ConstantInfo::Long(val), rest) 148 | } 149 | CONSTANT_DOUBLE => { 150 | let (val, rest) = after_tag.read_f64(); 151 | (ConstantInfo::Double(val), rest) 152 | } 153 | CONSTANT_UTF8 => { 154 | let (length, after_length) = after_tag.read_u16(); 155 | let (bytes, rest) = after_length.read_bytes(length as usize); 156 | let string: String = from_modified_utf8(bytes).unwrap(); 157 | (ConstantInfo::UTF8(string), rest) 158 | } 159 | CONSTANT_STRING => { 160 | let (val, rest) = after_tag.read_u16(); 161 | (ConstantInfo::String(val), rest) 162 | } 163 | CONSTANT_CLASS => { 164 | let (name_index, rest) = after_tag.read_u16(); 165 | (ConstantInfo::Class { name_index }, rest) 166 | } 167 | CONSTANT_NAME_AND_TYPE => { 168 | let (name_index, after_name_index) = after_tag.read_u16(); 169 | let (descriptor_index, rest) = after_name_index.read_u16(); 170 | ( 171 | ConstantInfo::NameAndType { 172 | name_index, 173 | descriptor_index, 174 | }, 175 | rest, 176 | ) 177 | } 178 | CONSTANT_FIELDREF => { 179 | let (class_index, after_class_index) = after_tag.read_u16(); 180 | let (name_and_type_index, rest) = after_class_index.read_u16(); 181 | ( 182 | ConstantInfo::FieldRef { 183 | class_index, 184 | name_and_type_index, 185 | }, 186 | rest, 187 | ) 188 | } 189 | CONSTANT_METHODREF => { 190 | let (class_index, after_class_index) = after_tag.read_u16(); 191 | let (name_and_type_index, rest) = after_class_index.read_u16(); 192 | ( 193 | ConstantInfo::MethodRef { 194 | class_index, 195 | name_and_type_index, 196 | }, 197 | rest, 198 | ) 199 | } 200 | CONSTANT_INTERFACE_METHODREF => { 201 | let (class_index, after_class_index) = after_tag.read_u16(); 202 | let (name_and_type_index, rest) = after_class_index.read_u16(); 203 | ( 204 | ConstantInfo::InterfaceMethodRef { 205 | class_index, 206 | name_and_type_index, 207 | }, 208 | rest, 209 | ) 210 | } 211 | _ => { 212 | panic!("Wrong tag type"); 213 | } 214 | } 215 | } 216 | 217 | fn read_constant_pool(&self) -> (ConstantPool, &[u8]) { 218 | let (count, after_count) = self.read_u16(); 219 | let mut constant_pool: ConstantPool = ConstantPool { 220 | vec_map: VecMap::with_capacity((count + 1) as usize), 221 | }; 222 | 223 | let mut i: usize = 1; 224 | let mut rest: &[u8] = after_count; 225 | 226 | while i < (count as usize) { 227 | let (constant_info, next_rest) = rest.read_constant_info(); 228 | rest = next_rest; 229 | let add = match constant_info { 230 | ConstantInfo::Long(_) | ConstantInfo::Double(_) => 2, 231 | _ => 1, 232 | }; 233 | constant_pool.insert(i, constant_info); 234 | i = i + add; 235 | } 236 | 237 | (constant_pool, rest) 238 | } 239 | 240 | fn read_access_flags(&self) -> (u16, &[u8]) { 241 | self.read_u16() 242 | } 243 | 244 | fn read_this_class(&self) -> (u16, &[u8]) { 245 | self.read_u16() 246 | } 247 | 248 | fn read_super_class(&self) -> (u16, &[u8]) { 249 | self.read_u16() 250 | } 251 | 252 | fn read_interfaces(&self) -> (Vec, &[u8]) { 253 | self.read_u16s() 254 | } 255 | 256 | fn read_member(&self, constant_pool: &ConstantPool) -> (MemberInfo, &[u8]) { 257 | let (access_flags, after_access_flags) = self.read_u16(); 258 | let (name_index, after_name_index) = after_access_flags.read_u16(); 259 | let (descriptor_index, after_descriptor_index) = after_name_index.read_u16(); 260 | let (attributes, after_attributes) = after_descriptor_index.read_attributes(constant_pool); 261 | let name = match constant_pool.get(name_index as usize) { 262 | ConstantInfo::UTF8(ref name) => name.to_owned(), 263 | _ => panic!("name isn't UTF8"), 264 | }; 265 | let descriptor = match constant_pool.get(descriptor_index as usize) { 266 | ConstantInfo::UTF8(ref descriptor) => descriptor.to_owned(), 267 | _ => panic!("descriptor isn't UTF8"), 268 | }; 269 | let member_info = MemberInfo { 270 | access_flags, 271 | name_index, 272 | descriptor_index, 273 | attributes, 274 | name, 275 | descriptor, 276 | }; 277 | (member_info, after_attributes) 278 | } 279 | 280 | fn read_members(&self, constant_pool: &ConstantPool) -> (Vec, &[u8]) { 281 | let (count, after_count) = self.read_u16(); 282 | 283 | let mut members: Vec = Vec::with_capacity(count as usize); 284 | let mut rest = after_count; 285 | 286 | for _ in 1..=count { 287 | let (member, after_member) = rest.read_member(constant_pool); 288 | members.push(member); 289 | rest = after_member; 290 | } 291 | 292 | (members, rest) 293 | } 294 | 295 | fn read_exception_table(&self) -> (Vec, &[u8]) { 296 | let (exception_table_length, after_exception_table_length) = self.read_u16(); 297 | let mut exception_table: Vec = 298 | Vec::with_capacity(exception_table_length as usize); 299 | let mut rest = after_exception_table_length; 300 | for _ in 1..=exception_table_length { 301 | let (start_pc, after_start_pc) = rest.read_u16(); 302 | let (end_pc, after_end_pc) = after_start_pc.read_u16(); 303 | let (handler_pc, after_handler_pc) = after_end_pc.read_u16(); 304 | let (catch_type, after_catch_type) = after_handler_pc.read_u16(); 305 | let exception_table_entry = ExceptionTableEntry { 306 | start_pc, 307 | end_pc, 308 | handler_pc, 309 | catch_type, 310 | }; 311 | exception_table.push(exception_table_entry); 312 | rest = after_catch_type; 313 | } 314 | (exception_table, rest) 315 | } 316 | 317 | fn read_line_number_table(&self) -> (Vec, &[u8]) { 318 | let (line_number_table_length, after_line_number_table_length) = self.read_u16(); 319 | let mut line_number_table: Vec = 320 | Vec::with_capacity(line_number_table_length as usize); 321 | let mut rest = after_line_number_table_length; 322 | for _ in 1..=line_number_table_length { 323 | let (start_pc, after_start_pc) = rest.read_u16(); 324 | let (line_number, after_line_number) = after_start_pc.read_u16(); 325 | let line_number_table_entry = LineNumberTableEntry { 326 | start_pc, 327 | line_number, 328 | }; 329 | line_number_table.push(line_number_table_entry); 330 | rest = after_line_number; 331 | } 332 | (line_number_table, rest) 333 | } 334 | 335 | fn read_local_variable_table(&self) -> (Vec, &[u8]) { 336 | let (local_variable_table_length, after_local_variable_table_length) = self.read_u16(); 337 | let mut local_variable_table: Vec = 338 | Vec::with_capacity(local_variable_table_length as usize); 339 | let mut rest = after_local_variable_table_length; 340 | for _ in 1..=local_variable_table_length { 341 | let (start_pc, after_start_pc) = rest.read_u16(); 342 | let (length, after_length) = after_start_pc.read_u16(); 343 | let (name_index, after_name_index) = after_length.read_u16(); 344 | let (descriptor_index, after_descriptor_index) = after_name_index.read_u16(); 345 | let (index, after_index) = after_descriptor_index.read_u16(); 346 | let local_variable_table_entry = LocalVariableTableEntry { 347 | start_pc, 348 | length, 349 | name_index, 350 | descriptor_index, 351 | index, 352 | }; 353 | local_variable_table.push(local_variable_table_entry); 354 | rest = after_index; 355 | } 356 | (local_variable_table, rest) 357 | } 358 | 359 | fn read_attribute(&self, constant_pool: &ConstantPool) -> (AttributeInfo, &[u8]) { 360 | let (attribute_name_index, after_attribute_name_index) = self.read_u16(); 361 | let attribute_name = match constant_pool.get(attribute_name_index as usize) { 362 | ConstantInfo::UTF8(attribute_name) => attribute_name, 363 | _ => panic!("attribute_name isn't UTF8"), 364 | }; 365 | let (attribute_length, after_attribute_length) = after_attribute_name_index.read_u32(); 366 | 367 | match attribute_name.as_str() { 368 | "Code" => { 369 | let (max_stack, after_max_stack) = after_attribute_length.read_u16(); 370 | let (max_locals, after_max_locals) = after_max_stack.read_u16(); 371 | let (code_length, after_code_length) = after_max_locals.read_u32(); 372 | let (code, after_code) = after_code_length.read_bytes(code_length as usize); 373 | let (exception_table, after_exception_table) = after_code.read_exception_table(); 374 | let (attributes, after_attributes) = 375 | after_exception_table.read_attributes(constant_pool); 376 | 377 | ( 378 | AttributeInfo::Code { 379 | max_stack, 380 | max_locals, 381 | code: Rc::new(code.to_vec()), 382 | exception_table, 383 | attributes, 384 | }, 385 | after_attributes, 386 | ) 387 | } 388 | "ConstantValue" => { 389 | let (constant_value_index, after_constant_value_index) = 390 | after_attribute_length.read_u16(); 391 | ( 392 | AttributeInfo::ConstantValue { 393 | constant_value_index, 394 | }, 395 | after_constant_value_index, 396 | ) 397 | } 398 | "Deprecated" => (AttributeInfo::Deprecated, after_attribute_length), 399 | "Exceptions" => { 400 | let (exception_index_table, after_exception_index_table) = 401 | after_attribute_length.read_u16s(); 402 | ( 403 | AttributeInfo::Exceptions { 404 | exception_index_table, 405 | }, 406 | after_exception_index_table, 407 | ) 408 | } 409 | "SourceFile" => { 410 | let (sourcefile_index, after_sourcefile_index) = after_attribute_length.read_u16(); 411 | ( 412 | AttributeInfo::SourceFile { sourcefile_index }, 413 | after_sourcefile_index, 414 | ) 415 | } 416 | "Synthetic" => (AttributeInfo::Synthetic {}, after_attribute_length), 417 | 418 | "LineNumberTable" => { 419 | let (line_number_table, after_line_number_table) = 420 | after_attribute_length.read_line_number_table(); 421 | ( 422 | AttributeInfo::LineNumberTable { line_number_table }, 423 | after_line_number_table, 424 | ) 425 | } 426 | 427 | "LocalVariableTable" => { 428 | let (local_variable_table, after_local_variable_table) = 429 | after_attribute_length.read_local_variable_table(); 430 | ( 431 | AttributeInfo::LocalVariableTable { 432 | local_variable_table, 433 | }, 434 | after_local_variable_table, 435 | ) 436 | } 437 | _ => { 438 | let (_, after_attribute_info) = 439 | after_attribute_length.read_bytes(attribute_length as usize); 440 | let attribute_name = attribute_name.to_string(); 441 | ( 442 | AttributeInfo::Unparsed { 443 | attribute_name, 444 | attribute_length, 445 | }, 446 | after_attribute_info, 447 | ) 448 | } 449 | } 450 | } 451 | 452 | fn read_attributes(&self, constant_pool: &ConstantPool) -> (Vec, &[u8]) { 453 | let (attributes_count, after_attributes_count) = self.read_u16(); 454 | let mut attributes: Vec = Vec::with_capacity(attributes_count as usize); 455 | let mut rest = after_attributes_count; 456 | for _ in 1..=attributes_count { 457 | let (attribute_info, next_rest) = rest.read_attribute(constant_pool); 458 | attributes.push(attribute_info); 459 | rest = next_rest; 460 | } 461 | (attributes, rest) 462 | } 463 | 464 | fn parse(&self) -> ClassFile { 465 | let (_, after_magic) = self.read_and_check_magic(); 466 | let (version_info, after_version_info) = after_magic.read_and_check_version(); 467 | let VersionInfo { 468 | major_version, 469 | minor_version, 470 | } = version_info; 471 | let (constant_pool, after_constant_pool) = after_version_info.read_constant_pool(); 472 | let (access_flags, after_access_flags) = after_constant_pool.read_access_flags(); 473 | let (this_class, after_this_class) = after_access_flags.read_this_class(); 474 | let (super_class, after_super_class) = after_this_class.read_this_class(); 475 | let (interfaces, after_interfaces) = after_super_class.read_interfaces(); 476 | let (fields, after_fields) = after_interfaces.read_members(&constant_pool); 477 | let (methods, after_methods) = after_fields.read_members(&constant_pool); 478 | let (attributes, _) = after_methods.read_attributes(&constant_pool); 479 | ClassFile { 480 | major_version, 481 | minor_version, 482 | constant_pool, 483 | access_flags, 484 | this_class, 485 | super_class, 486 | interfaces, 487 | fields, 488 | methods, 489 | attributes, 490 | } 491 | } 492 | } 493 | 494 | #[cfg(test)] 495 | mod tests { 496 | use classfile::attribute_info::{AttributeInfo, LineNumberTableEntry}; 497 | use classfile::class_file::ClassFile; 498 | use classfile::class_reader::ClassReader; 499 | use classfile::constant_info::ConstantInfo; 500 | use classfile::member_info::MemberInfo; 501 | use std::fs::File; 502 | use std::io::Read; 503 | 504 | #[test] 505 | fn parse() { 506 | // todo: assert access_flags 507 | let path: &str = "src/test_data/Object.class"; 508 | let input = File::open(path).unwrap(); 509 | let bytes: Vec = input.bytes().map(|x| x.unwrap()).collect(); 510 | let ClassFile { 511 | major_version, 512 | minor_version, 513 | constant_pool, 514 | access_flags, 515 | this_class, 516 | super_class, 517 | interfaces, 518 | fields, 519 | methods, 520 | attributes, 521 | } = bytes.parse(); 522 | assert_eq!(major_version, 52); 523 | assert_eq!(minor_version, 0); 524 | assert_eq!(constant_pool.capacity(), 79); 525 | match constant_pool.get(1) { 526 | ConstantInfo::Class { name_index } => assert_eq!(*name_index, 49u16), 527 | _ => panic!(), 528 | }; 529 | match constant_pool.get(9) { 530 | ConstantInfo::MethodRef { 531 | class_index, 532 | name_and_type_index, 533 | } => { 534 | assert_eq!(class_index.to_owned(), 1 as u16); 535 | assert_eq!(name_and_type_index.to_owned(), 59 as u16); 536 | } 537 | _ => panic!(), 538 | }; 539 | match constant_pool.get(13) { 540 | ConstantInfo::Integer(value) => assert_eq!(*value, 999999i32), 541 | _ => panic!(), 542 | }; 543 | match constant_pool.get(22) { 544 | ConstantInfo::UTF8(value) => assert_eq!(value, "registerNatives"), 545 | _ => panic!(), 546 | }; 547 | match constant_pool.get(50) { 548 | ConstantInfo::NameAndType { 549 | name_index, 550 | descriptor_index, 551 | } => { 552 | assert_eq!(*name_index, 18u16); 553 | assert_eq!(*descriptor_index, 19u16); 554 | } 555 | _ => panic!(), 556 | }; 557 | match constant_pool.get(77) { 558 | ConstantInfo::UTF8(value) => assert_eq!(value, "(Ljava/lang/String;)V"), 559 | _ => panic!(), 560 | }; 561 | assert_eq!(access_flags, 33); 562 | assert_eq!(this_class, 17); 563 | assert_eq!(super_class, 0); 564 | assert_eq!(interfaces.len(), 0); 565 | assert_eq!(fields.len(), 0); 566 | assert_eq!(methods.len(), 14); 567 | match methods.get(2).unwrap() { 568 | MemberInfo { 569 | name_index, 570 | descriptor_index, 571 | attributes, 572 | access_flags: _, 573 | name: _, 574 | descriptor: _, 575 | } => { 576 | assert_eq!(*name_index, 23u16); 577 | assert_eq!(*descriptor_index, 24u16); 578 | assert_eq!(attributes.len(), 1usize); 579 | } 580 | } 581 | match methods.get(13).unwrap() { 582 | MemberInfo { 583 | name_index, 584 | descriptor_index, 585 | attributes, 586 | access_flags: _, 587 | name: _, 588 | descriptor: _, 589 | } => { 590 | assert_eq!(*name_index, 46u16); 591 | assert_eq!(*descriptor_index, 19u16); 592 | assert_eq!(attributes.len(), 1usize); 593 | match attributes.get(0).unwrap() { 594 | AttributeInfo::Code { 595 | max_stack, 596 | max_locals, 597 | code, 598 | exception_table, 599 | attributes, 600 | } => { 601 | assert_eq!(*max_stack, 0u16); 602 | assert_eq!(*max_locals, 0u16); 603 | assert_eq!(code.len(), 4usize); 604 | assert_eq!(exception_table.len(), 0usize); 605 | assert_eq!(attributes.len(), 1usize); 606 | match attributes.get(0).unwrap() { 607 | AttributeInfo::LineNumberTable { line_number_table } => { 608 | match line_number_table.get(0).unwrap() { 609 | LineNumberTableEntry { 610 | start_pc, 611 | line_number, 612 | } => { 613 | assert_eq!(*start_pc, 0u16); 614 | assert_eq!(*line_number, 41u16) 615 | } 616 | } 617 | } 618 | _ => panic!(), 619 | } 620 | } 621 | _ => panic!(), 622 | } 623 | } 624 | } 625 | assert_eq!(attributes.len(), 1); 626 | match attributes.get(0).unwrap() { 627 | AttributeInfo::SourceFile { sourcefile_index } => { 628 | assert_eq!(*sourcefile_index, 48u16); 629 | } 630 | _ => panic!(), 631 | } 632 | } 633 | } 634 | --------------------------------------------------------------------------------