Repository: douchuan/jvm Branch: master Commit: 5678df31a13e Files: 349 Total size: 710.7 KB Directory structure: gitextract_9nbsz2bv/ ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── crates/ │ ├── class-parser/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── class.rs │ │ ├── lib.rs │ │ └── signature.rs │ ├── class-verification/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── checker.rs │ │ └── lib.rs │ ├── classfile/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── attributes.rs │ │ ├── classfile.rs │ │ ├── constant_pool.rs │ │ ├── consts.rs │ │ ├── field_info.rs │ │ ├── flags.rs │ │ ├── lib.rs │ │ ├── method_info.rs │ │ ├── opcode.rs │ │ ├── signature.rs │ │ └── version.rs │ └── vm/ │ ├── Cargo.toml │ ├── README.md │ └── src/ │ ├── lib.rs │ ├── native/ │ │ ├── common/ │ │ │ ├── check_format.rs │ │ │ ├── mod.rs │ │ │ └── reflect.rs │ │ ├── java_io_FileDescriptor.rs │ │ ├── java_io_FileInputStream.rs │ │ ├── java_io_FileOutputStream.rs │ │ ├── java_io_UnixFileSystem.rs │ │ ├── java_lang_Class.rs │ │ ├── java_lang_ClassLoader.rs │ │ ├── java_lang_Double.rs │ │ ├── java_lang_Float.rs │ │ ├── java_lang_Object.rs │ │ ├── java_lang_Runtime.rs │ │ ├── java_lang_String.rs │ │ ├── java_lang_System.rs │ │ ├── java_lang_Thread.rs │ │ ├── java_lang_Throwable.rs │ │ ├── java_lang_reflect_Array.rs │ │ ├── java_lang_reflect_Proxy.rs │ │ ├── java_security_AccessController.rs │ │ ├── java_util_concurrent_atomic_AtomicLong.rs │ │ ├── mod.rs │ │ ├── sun_misc_Signal.rs │ │ ├── sun_misc_URLClassPath.rs │ │ ├── sun_misc_Unsafe.rs │ │ ├── sun_misc_VM.rs │ │ ├── sun_nio_cs_StreamEncoder.rs │ │ ├── sun_reflect_ConstantPool.rs │ │ ├── sun_reflect_NativeConstructorAccessorImpl.rs │ │ ├── sun_reflect_NativeMethodAccessorImpl.rs │ │ └── sun_reflect_Reflection.rs │ ├── oop/ │ │ ├── ary.rs │ │ ├── class.rs │ │ ├── consts.rs │ │ ├── field.rs │ │ ├── inst.rs │ │ ├── mirror.rs │ │ ├── mod.rs │ │ ├── reference.rs │ │ └── values.rs │ ├── runtime/ │ │ ├── class_loader.rs │ │ ├── class_path_manager.rs │ │ ├── cmp.rs │ │ ├── constant_pool.rs │ │ ├── consts.rs │ │ ├── dataarea.rs │ │ ├── exception.rs │ │ ├── frame.rs │ │ ├── init_vm.rs │ │ ├── interp.rs │ │ ├── invoke.rs │ │ ├── local.rs │ │ ├── method.rs │ │ ├── mod.rs │ │ ├── slot.rs │ │ ├── stack.rs │ │ ├── sys_dic.rs │ │ ├── thread/ │ │ │ ├── condvar.rs │ │ │ ├── java_thread.rs │ │ │ ├── main.rs │ │ │ ├── mod.rs │ │ │ ├── mutex.rs │ │ │ ├── thread_pool.rs │ │ │ └── threads.rs │ │ └── vm.rs │ ├── types.rs │ └── util/ │ ├── attributes.rs │ ├── consts.rs │ ├── debug.rs │ ├── macros.rs │ ├── mod.rs │ ├── oop.rs │ └── sys.rs ├── jvm/ │ ├── Cargo.toml │ ├── README.md │ ├── r.sh │ ├── sample/ │ │ └── HelloWorld.java │ ├── src/ │ │ ├── main.rs │ │ └── options.rs │ └── t.sh ├── libjvm/ │ ├── Cargo.toml │ ├── README.md │ └── src/ │ ├── invocation.rs │ ├── lib.rs │ ├── native.rs │ ├── private.rs │ └── util.rs ├── note.txt └── tools/ ├── javap/ │ ├── Cargo.toml │ ├── run.sh │ ├── src/ │ │ ├── cmd/ │ │ │ ├── disassemble.rs │ │ │ └── mod.rs │ │ ├── main.rs │ │ ├── misc/ │ │ │ ├── class_path_manager.rs │ │ │ ├── mod.rs │ │ │ └── sys_info.rs │ │ ├── sd/ │ │ │ └── mod.rs │ │ ├── strategy.rs │ │ ├── template.rs │ │ ├── trans/ │ │ │ ├── access_flag.rs │ │ │ ├── class_file.rs │ │ │ ├── code.rs │ │ │ ├── constant_pool_trans.rs │ │ │ ├── field.rs │ │ │ ├── instruction/ │ │ │ │ ├── aaload.rs │ │ │ │ ├── aastore.rs │ │ │ │ ├── aconst_null.rs │ │ │ │ ├── aload.rs │ │ │ │ ├── aload_0.rs │ │ │ │ ├── aload_1.rs │ │ │ │ ├── aload_2.rs │ │ │ │ ├── aload_3.rs │ │ │ │ ├── anewarray.rs │ │ │ │ ├── areturn.rs │ │ │ │ ├── arraylength.rs │ │ │ │ ├── astore.rs │ │ │ │ ├── astore_0.rs │ │ │ │ ├── astore_1.rs │ │ │ │ ├── astore_2.rs │ │ │ │ ├── astore_3.rs │ │ │ │ ├── athrow.rs │ │ │ │ ├── baload.rs │ │ │ │ ├── bastore.rs │ │ │ │ ├── bipush.rs │ │ │ │ ├── caload.rs │ │ │ │ ├── castore.rs │ │ │ │ ├── checkcast.rs │ │ │ │ ├── d2f.rs │ │ │ │ ├── d2i.rs │ │ │ │ ├── d2l.rs │ │ │ │ ├── dadd.rs │ │ │ │ ├── daload.rs │ │ │ │ ├── dastore.rs │ │ │ │ ├── dcmpg.rs │ │ │ │ ├── dcmpl.rs │ │ │ │ ├── dconst_0.rs │ │ │ │ ├── dconst_1.rs │ │ │ │ ├── ddiv.rs │ │ │ │ ├── dload.rs │ │ │ │ ├── dload_0.rs │ │ │ │ ├── dload_1.rs │ │ │ │ ├── dload_2.rs │ │ │ │ ├── dload_3.rs │ │ │ │ ├── dmul.rs │ │ │ │ ├── dneg.rs │ │ │ │ ├── drem.rs │ │ │ │ ├── dreturn.rs │ │ │ │ ├── dstore.rs │ │ │ │ ├── dstore_0.rs │ │ │ │ ├── dstore_1.rs │ │ │ │ ├── dstore_2.rs │ │ │ │ ├── dstore_3.rs │ │ │ │ ├── dsub.rs │ │ │ │ ├── dup.rs │ │ │ │ ├── dup2.rs │ │ │ │ ├── dup2_x1.rs │ │ │ │ ├── dup2_x2.rs │ │ │ │ ├── dup_x1.rs │ │ │ │ ├── dup_x2.rs │ │ │ │ ├── f2d.rs │ │ │ │ ├── f2i.rs │ │ │ │ ├── f2l.rs │ │ │ │ ├── fadd.rs │ │ │ │ ├── faload.rs │ │ │ │ ├── fastore.rs │ │ │ │ ├── fcmpg.rs │ │ │ │ ├── fcmpl.rs │ │ │ │ ├── fconst_0.rs │ │ │ │ ├── fconst_1.rs │ │ │ │ ├── fconst_2.rs │ │ │ │ ├── fdiv.rs │ │ │ │ ├── fload.rs │ │ │ │ ├── fload_0.rs │ │ │ │ ├── fload_1.rs │ │ │ │ ├── fload_2.rs │ │ │ │ ├── fload_3.rs │ │ │ │ ├── fmul.rs │ │ │ │ ├── fneg.rs │ │ │ │ ├── frem.rs │ │ │ │ ├── freturn.rs │ │ │ │ ├── fstore.rs │ │ │ │ ├── fstore_0.rs │ │ │ │ ├── fstore_1.rs │ │ │ │ ├── fstore_2.rs │ │ │ │ ├── fstore_3.rs │ │ │ │ ├── fsub.rs │ │ │ │ ├── getfield.rs │ │ │ │ ├── getstatic.rs │ │ │ │ ├── goto.rs │ │ │ │ ├── goto_w.rs │ │ │ │ ├── i2b.rs │ │ │ │ ├── i2c.rs │ │ │ │ ├── i2d.rs │ │ │ │ ├── i2f.rs │ │ │ │ ├── i2l.rs │ │ │ │ ├── i2s.rs │ │ │ │ ├── iadd.rs │ │ │ │ ├── iaload.rs │ │ │ │ ├── iand.rs │ │ │ │ ├── iastore.rs │ │ │ │ ├── iconst_0.rs │ │ │ │ ├── iconst_1.rs │ │ │ │ ├── iconst_2.rs │ │ │ │ ├── iconst_3.rs │ │ │ │ ├── iconst_4.rs │ │ │ │ ├── iconst_5.rs │ │ │ │ ├── iconst_m1.rs │ │ │ │ ├── idiv.rs │ │ │ │ ├── if_acmpeq.rs │ │ │ │ ├── if_acmpne.rs │ │ │ │ ├── if_icmpeq.rs │ │ │ │ ├── if_icmpge.rs │ │ │ │ ├── if_icmpgt.rs │ │ │ │ ├── if_icmple.rs │ │ │ │ ├── if_icmplt.rs │ │ │ │ ├── if_icmpne.rs │ │ │ │ ├── ifeq.rs │ │ │ │ ├── ifge.rs │ │ │ │ ├── ifgt.rs │ │ │ │ ├── ifle.rs │ │ │ │ ├── iflt.rs │ │ │ │ ├── ifne.rs │ │ │ │ ├── ifnonnull.rs │ │ │ │ ├── ifnull.rs │ │ │ │ ├── iinc.rs │ │ │ │ ├── iload.rs │ │ │ │ ├── iload_0.rs │ │ │ │ ├── iload_1.rs │ │ │ │ ├── iload_2.rs │ │ │ │ ├── iload_3.rs │ │ │ │ ├── imul.rs │ │ │ │ ├── ineg.rs │ │ │ │ ├── instanceof.rs │ │ │ │ ├── invokedynamic.rs │ │ │ │ ├── invokeinterface.rs │ │ │ │ ├── invokespecial.rs │ │ │ │ ├── invokestatic.rs │ │ │ │ ├── invokevirtual.rs │ │ │ │ ├── ior.rs │ │ │ │ ├── irem.rs │ │ │ │ ├── ireturn.rs │ │ │ │ ├── ishl.rs │ │ │ │ ├── ishr.rs │ │ │ │ ├── istore.rs │ │ │ │ ├── istore_0.rs │ │ │ │ ├── istore_1.rs │ │ │ │ ├── istore_2.rs │ │ │ │ ├── istore_3.rs │ │ │ │ ├── isub.rs │ │ │ │ ├── iushr.rs │ │ │ │ ├── ixor.rs │ │ │ │ ├── jsr.rs │ │ │ │ ├── jsr_w.rs │ │ │ │ ├── l2d.rs │ │ │ │ ├── l2f.rs │ │ │ │ ├── l2i.rs │ │ │ │ ├── ladd.rs │ │ │ │ ├── laload.rs │ │ │ │ ├── land.rs │ │ │ │ ├── lastore.rs │ │ │ │ ├── lcmp.rs │ │ │ │ ├── lconst_0.rs │ │ │ │ ├── lconst_1.rs │ │ │ │ ├── ldc.rs │ │ │ │ ├── ldc2_w.rs │ │ │ │ ├── ldc_w.rs │ │ │ │ ├── ldiv.rs │ │ │ │ ├── lload.rs │ │ │ │ ├── lload_0.rs │ │ │ │ ├── lload_1.rs │ │ │ │ ├── lload_2.rs │ │ │ │ ├── lload_3.rs │ │ │ │ ├── lmul.rs │ │ │ │ ├── lneg.rs │ │ │ │ ├── lookupswitch.rs │ │ │ │ ├── lor.rs │ │ │ │ ├── lrem.rs │ │ │ │ ├── lreturn.rs │ │ │ │ ├── lshl.rs │ │ │ │ ├── lshr.rs │ │ │ │ ├── lstore.rs │ │ │ │ ├── lstore_0.rs │ │ │ │ ├── lstore_1.rs │ │ │ │ ├── lstore_2.rs │ │ │ │ ├── lstore_3.rs │ │ │ │ ├── lsub.rs │ │ │ │ ├── lushr.rs │ │ │ │ ├── lxor.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── monitorenter.rs │ │ │ │ ├── monitorexit.rs │ │ │ │ ├── multianewarray.rs │ │ │ │ ├── new.rs │ │ │ │ ├── newarray.rs │ │ │ │ ├── nop.rs │ │ │ │ ├── pop.rs │ │ │ │ ├── pop2.rs │ │ │ │ ├── putfield.rs │ │ │ │ ├── putstatic.rs │ │ │ │ ├── ret.rs │ │ │ │ ├── return_void.rs │ │ │ │ ├── saload.rs │ │ │ │ ├── sastore.rs │ │ │ │ ├── sipush.rs │ │ │ │ ├── swap.rs │ │ │ │ ├── tableswitch.rs │ │ │ │ └── wide.rs │ │ │ ├── method.rs │ │ │ ├── mod.rs │ │ │ └── signature_type.rs │ │ └── util/ │ │ ├── mod.rs │ │ └── sys.rs │ └── test/ │ ├── AbstractGraphicObject.java │ ├── EnumMobile.java │ ├── Football.java │ ├── HelloWorld.java │ ├── Hockey.java │ ├── Interface1.java │ └── Sports.java └── misc/ ├── instruction.py └── native.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /target **/*.rs.bk *.class .idea ================================================ FILE: Cargo.toml ================================================ [workspace] members = [ "jvm", "libjvm", "crates/classfile", "crates/class-parser", "crates/vm", "tools/javap" ] #https://doc.rust-lang.org/cargo/reference/profiles.html [profile.release] lto = "fat" codegen-units = 1 panic = "abort" ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 douchuan and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ Have a great goal is a very important thing, such as Moon Landing. What is the meaning of this plan? That's the process of achieving this goal, the produce of industry and technology. Sun is a great company, in the era of C++, they created JVM & HotSpot. Now, we have Rust, a better tool, let’s remake JVM! Pay tribute to medical workers at the front! Thank you for your contribution to the fight against the epidemic. ## Roadmap - Pass test cases in JDK - Pass TCK - GC (crate) - JIT / interp (crate) - class verification (crate) - After GC built, ready for optimize System.arraycopy (the key of performance) - WebAssembly, make the JVM work in Browser - java options (-version, -server...) In summary, the roadmap is built on a 3-step progress. - Pass TCK - Refactor & Rewrite - Divide into several crates, build a collection of modular and reusable vm technologies Well, it's a long term plan, Sun spent 30 years to improve VM, Oracle continue doing it. The journey of a thousand miles begins with one first step. Even the sage was once an ordinary human being. Just Do It. ## Running ```shell # setup JDK # setup rust toolchain # clone this project # compile sample by javac cd jvm/sample javac HelloWorld.java cd .. # modify r.sh JAVA_HOME according to your env # exec sample cd jvm bash r.sh ``` ================================================ FILE: crates/class-parser/Cargo.toml ================================================ [package] name = "class-parser" version = "0.1.0" authors = ["Dou Chuan <1843657913@qq.com>"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] classfile = { path = "../classfile", version = "0.1.0" } nom = "5.1.1" ================================================ FILE: crates/class-parser/README.md ================================================ ## JVM class parser This is the `class-parser` crate, which contains parser of `.class`. The binary format parser based on [`nom`](https://crates.io/crates/nom). ================================================ FILE: crates/class-parser/src/class.rs ================================================ use classfile::{ attributes::{self, Tag as AttrTag, TargetInfo, TypeAnnotation}, constant_pool, AttributeType, ClassFile, ConstantPool, FieldInfo, MethodInfo, Version, }; use std::sync::Arc; use nom::{ call, count, do_parse, named, named_args, number::streaming::{be_u16, be_u32, be_u8}, peek, switch, tag, take, value, }; named!( version, do_parse!(minor: be_u16 >> major: be_u16 >> (Version { minor, major })) ); named!( constant_tag, do_parse!(tag: be_u8 >> (constant_pool::Tag::from(tag))) ); // Const generics still not in stable, // idk how to write this fancier without them D: // Hope compiler will rewrite this properly macro_rules! gen_take_exact { ($count: expr, $name: ident) => { fn $name(input: &[u8]) -> nom::IResult<&[u8], [u8; $count]> { let mut output = [0; $count]; // TODO: Nom error assert!(input.len() >= $count); for i in 0..$count { output[i] = input[i]; } Ok((&input[$count..], output)) } }; } gen_take_exact!(4, take_exact_4); gen_take_exact!(8, take_exact_8); named!( cp_entry, do_parse!( ct: constant_tag >> entry: switch!(value!(ct), constant_pool::Tag::Class => do_parse!( name_index: be_u16 >> (constant_pool::Type::Class { name_index }) ) | constant_pool::Tag::FieldRef => do_parse!( class_index: be_u16 >> name_and_type_index: be_u16 >> (constant_pool::Type::FieldRef { class_index, name_and_type_index }) ) | constant_pool::Tag::MethodRef => do_parse!( class_index: be_u16 >> name_and_type_index: be_u16 >> (constant_pool::Type::MethodRef { class_index, name_and_type_index }) ) | constant_pool::Tag::InterfaceMethodRef => do_parse!( class_index: be_u16 >> name_and_type_index: be_u16 >> (constant_pool::Type::InterfaceMethodRef { class_index, name_and_type_index }) ) | constant_pool::Tag::String => do_parse!( string_index: be_u16 >> (constant_pool::Type::String { string_index }) ) | constant_pool::Tag::Integer => do_parse!( v: take_exact_4 >> (constant_pool::Type::Integer { v }) ) | constant_pool::Tag::Float => do_parse!( v: take_exact_4 >> (constant_pool::Type::Float { v }) ) | constant_pool::Tag::Long => do_parse!( v: take_exact_8 >> (constant_pool::Type::Long { v }) ) | constant_pool::Tag::Double => do_parse!( v: take_exact_8 >> (constant_pool::Type::Double { v }) ) | constant_pool::Tag::NameAndType => do_parse!( name_index: be_u16 >> desc_index: be_u16 >> (constant_pool::Type::NameAndType { name_index, desc_index }) ) | constant_pool::Tag::Utf8 => do_parse!( length: be_u16 >> bytes: take!(length) >> (constant_pool::Type::Utf8 { bytes: Arc::new(Vec::from(bytes)) }) ) | constant_pool::Tag::MethodHandle => do_parse!( ref_kind: be_u8 >> ref_index: be_u16 >> (constant_pool::Type::MethodHandle { ref_kind, ref_index }) ) | constant_pool::Tag::MethodType => do_parse!( desc_index: be_u16 >> (constant_pool::Type::MethodType { desc_index }) ) | constant_pool::Tag::InvokeDynamic => do_parse!( bootstrap_method_attr_index: be_u16 >> name_and_type_index: be_u16 >> (constant_pool::Type::InvokeDynamic { bootstrap_method_attr_index, name_and_type_index }) ) ) >> (entry) ) ); fn constant_pool(input: &[u8]) -> nom::IResult<&[u8], ConstantPool> { let (mut input, count) = be_u16(input)?; let mut output = Vec::with_capacity(count as usize); output.push(constant_pool::Type::Nop); let mut i = 1; while i < count { let (new_input, constant_type) = cp_entry(input)?; input = new_input; i += 1; output.push(constant_type.clone()); //spec 4.4.5 match constant_type { constant_pool::Type::Long { .. } | constant_pool::Type::Double { .. } => { i += 1; output.push(constant_pool::Type::Nop); } _ => (), } } Ok((input, Arc::new(output))) } use attributes::VerificationTypeInfo; named!( verification_type_info, do_parse!( id: be_u8 >> inner: switch!(value!(id), 0 => value!(VerificationTypeInfo::Top) | 1 => value!(VerificationTypeInfo::Integer) | 2 => value!(VerificationTypeInfo::Float) | 3 => value!(VerificationTypeInfo::Long) | 4 => value!(VerificationTypeInfo::Double) | 5 => value!(VerificationTypeInfo::Null) | 6 => value!(VerificationTypeInfo::UninitializedThis) | 7 => do_parse!( cpool_index: be_u16 >> (VerificationTypeInfo::Object {cpool_index}) ) | 8 => do_parse!( offset: be_u16 >> (VerificationTypeInfo::Uninitialized {offset}) ) ) >> (inner) ) ); named!( stack_map_frame, do_parse!( frame_type: be_u8 >> inner: switch!(value!(frame_type), 0..=63 => value!(attributes::StackMapFrame::Same {tag: frame_type, offset_delta: frame_type as u16}) | 64..=127 => do_parse!( offset_delta: value!((frame_type-64) as u16) >> type_info: verification_type_info >> (attributes::StackMapFrame::SameLocals1StackItem { tag: frame_type, offset_delta, stack: [type_info], }) ) | 128..=246 => value!(attributes::StackMapFrame::Reserved(frame_type)) | 247 => do_parse!( offset_delta: be_u16 >> type_info: verification_type_info >> (attributes::StackMapFrame::SameLocals1StackItem { tag: frame_type, offset_delta, stack: [type_info], }) ) | 248..=250 => do_parse!( offset_delta: be_u16 >> (attributes::StackMapFrame::Chop { tag: frame_type, offset_delta, }) ) | 251 => do_parse!( offset_delta: be_u16 >> (attributes::StackMapFrame::SameExtended { tag: frame_type, offset_delta }) ) | 252..=254 => do_parse!( offset_delta: be_u16 >> locals_count: value!(frame_type - 251) >> locals: count!(verification_type_info, locals_count as usize) >> (attributes::StackMapFrame::Append { tag: frame_type, offset_delta, locals, }) ) | 255 => do_parse!( offset_delta: be_u16 >> locals_count: be_u16 >> locals: count!(verification_type_info, locals_count as usize) >> stack_count: be_u16 >> stack: count!(verification_type_info, stack_count as usize) >> (attributes::StackMapFrame::Full { tag: frame_type, offset_delta, locals, stack, }) ) ) >> (inner) ) ); named!( inner_class, do_parse!( inner_class_info_index: be_u16 >> outer_class_info_index: be_u16 >> inner_name_index: be_u16 >> inner_class_access_flags: be_u16 >> (attributes::InnerClass { inner_class_info_index, outer_class_info_index, inner_name_index, inner_class_access_flags, }) ) ); named!( enclosing_method, do_parse!( class_index: be_u16 >> method_index: be_u16 >> (attributes::EnclosingMethod { class_index, method_index, }) ) ); named!( line_number, do_parse!(start_pc: be_u16 >> number: be_u16 >> (attributes::LineNumber { start_pc, number })) ); named!( local_variable, do_parse!( start_pc: be_u16 >> length: be_u16 >> name_index: be_u16 >> signature_index: be_u16 >> index: be_u16 >> (attributes::LocalVariable { start_pc, length, name_index, signature_index, index, }) ) ); named!( element_value_tag, do_parse!(tag: be_u8 >> (attributes::ElementValueTag::from(tag))) ); use attributes::{ElementValueTag, ElementValueType}; // I didn't found a way to turn byte/char/double/float/... boilerplate into a macro( named_args!(element_value_type(cp: ConstantPool), do_parse!( tag: element_value_tag >> inner: switch!(value!(tag), ElementValueTag::Byte => do_parse!( val_index: be_u16 >> (ElementValueType::Byte {val_index}) ) | ElementValueTag::Char => do_parse!( val_index: be_u16 >> (ElementValueType::Char {val_index}) ) | ElementValueTag::Double => do_parse!( val_index: be_u16 >> (ElementValueType::Double {val_index}) ) | ElementValueTag::Float => do_parse!( val_index: be_u16 >> (ElementValueType::Float {val_index}) ) | ElementValueTag::Byte => do_parse!( val_index: be_u16 >> (ElementValueType::Byte {val_index}) ) | ElementValueTag::Int => do_parse!( val_index: be_u16 >> (ElementValueType::Int {val_index}) ) | ElementValueTag::Long => do_parse!( val_index: be_u16 >> (ElementValueType::Long {val_index}) ) | ElementValueTag::Short => do_parse!( val_index: be_u16 >> (ElementValueType::Short {val_index}) ) | ElementValueTag::Boolean => do_parse!( val_index: be_u16 >> (ElementValueType::Boolean {val_index}) ) | ElementValueTag::String => do_parse!( val_index: be_u16 >> (ElementValueType::String {val_index}) ) | ElementValueTag::Enum => do_parse!( type_index: be_u16 >> val_index: be_u16 >> (ElementValueType::Enum {type_index, val_index}) ) | ElementValueTag::Class => do_parse!( index: be_u16 >> (ElementValueType::Class {index}) ) | ElementValueTag::Annotation => do_parse!( value: call!(annotation_entry, cp) >> (ElementValueType::Annotation(attributes::AnnotationElementValue {value})) ) | ElementValueTag::Array => do_parse!( array_size: be_u16 >> values: count!(call!(element_value_type, cp.clone()), array_size as usize) >> (ElementValueType::Array { values, }) ) | ElementValueTag::Unknown => value!(ElementValueType::Unknown) ) >> (inner) )); named_args!(element_value_pair(cp: ConstantPool), do_parse!( name_index: be_u16 >> value: call!(element_value_type, cp) >> (attributes::ElementValuePair {name_index, value}) )); named_args!(annotation_entry(cp: ConstantPool), do_parse!( type_index: be_u16 >> pair_count: be_u16 >> pairs: count!(call!(element_value_pair, cp.clone()), pair_count as usize) >> type_name: value!(constant_pool::get_utf8(&cp, type_index as usize).clone()) >> (attributes::AnnotationEntry {type_name, pairs}) )); named!( local_var_target_table, do_parse!( start_pc: be_u16 >> length: be_u16 >> index: be_u16 >> (attributes::LocalVarTargetTable { start_pc, length, index }) ) ); named!( target_info, do_parse!( target_type: be_u8 >> inner: switch!(value!(target_type), 0x00 | 0x01 => do_parse!( type_parameter_index: be_u8 >> (TargetInfo::TypeParameter { type_parameter_index }) ) | 0x10 => do_parse!( supertype_index: be_u16 >> (TargetInfo::SuperType { supertype_index }) ) | 0x11 | 0x12 => do_parse!( type_parameter_index: be_u8 >> bound_index: be_u8 >> (TargetInfo::TypeParameterBound {type_parameter_index, bound_index}) ) | 0x13 | 0x14 | 0x15 => value!(TargetInfo::Empty) | 0x16 => do_parse!( formal_parameter_index: be_u8 >> (TargetInfo::FormalParameter {formal_parameter_index}) ) | 0x17 => do_parse!( throws_type_index: be_u16 >> (TargetInfo::Throws {throws_type_index}) ) | 0x40 | 0x41 => do_parse!( item_count: be_u16 >> items: count!(local_var_target_table, item_count as usize) >> (TargetInfo::LocalVar {table: items}) ) | 0x42 => do_parse!( exception_table_index: be_u16 >> (TargetInfo::Catch {exception_table_index}) ) | 0x43 | 0x44 | 0x45 | 0x46 => do_parse!( offset: be_u16 >> (TargetInfo::Offset {offset}) ) | 0x47 | 0x48 | 0x49 | 0x4A | 0x4B => do_parse!( offset: be_u16 >> type_argument_index: be_u8 >> (TargetInfo::TypeArgument {offset, type_argument_index}) ) ) >> (inner) ) ); named!( type_path, do_parse!( type_path_kind: be_u8 >> type_argument_index: be_u8 >> (attributes::TypePath { type_path_kind, type_argument_index, }) ) ); named_args!(type_annotation(cp: ConstantPool), do_parse!( target_info: target_info >> target_path_part_count: be_u8 >> target_path: count!(type_path, target_path_part_count as usize) >> type_index: be_u16 >> pair_count: be_u16 >> pairs: count!(call!(element_value_pair, cp.clone()), pair_count as usize) >> (attributes::TypeAnnotation { target_info, target_path, type_index, pairs, }) )); named!( bootstrap_method, do_parse!( method_ref: be_u16 >> arg_count: be_u16 >> args: count!(be_u16, arg_count as usize) >> (attributes::BootstrapMethod { method_ref, args }) ) ); named!( method_parameter, do_parse!( name_index: be_u16 >> acc_flags: be_u16 >> (attributes::MethodParameter { name_index, acc_flags }) ) ); named!( code_exception, do_parse!( start_pc: be_u16 >> end_pc: be_u16 >> handler_pc: be_u16 >> catch_type: be_u16 >> (attributes::CodeException { start_pc, end_pc, handler_pc, catch_type }) ) ); named_args!(attr_sized(tag: AttrTag, self_len: usize, cp: ConstantPool), switch!(value!(tag), AttrTag::ConstantValue => do_parse!( constant_value_index: be_u16 >> (AttributeType::ConstantValue {constant_value_index}) ) | AttrTag::Code => do_parse!( max_stack: be_u16 >> max_locals: be_u16 >> len: be_u32 >> code: take!(len) >> // TODO: Parse code in same time?) exception_count: be_u16 >> exceptions: count!(code_exception, exception_count as usize) >> attrs: call!(attr_type_vec, cp) >> (AttributeType::Code(attributes::Code { max_stack, max_locals, code: Arc::new(Vec::from(code)), exceptions, attrs, })) ) | AttrTag::StackMapTable => do_parse!( frame_count: be_u16 >> frames: count!(stack_map_frame, frame_count as usize) >> (AttributeType::StackMapTable { entries: frames }) ) | AttrTag::Exceptions => do_parse!( exception_count: be_u16 >> exceptions: count!(be_u16, exception_count as usize) >> (AttributeType::Exceptions { exceptions }) ) | AttrTag::InnerClasses => do_parse!( class_count: be_u16 >> classes: count!(inner_class, class_count as usize) >> (AttributeType::InnerClasses { classes }) ) | AttrTag::EnclosingMethod => do_parse!( em: enclosing_method >> (AttributeType::EnclosingMethod { em }) ) | AttrTag::Synthetic => value!(AttributeType::Synthetic) | AttrTag::Signature => do_parse!( signature_index: be_u16 >> (AttributeType::Signature { signature_index }) ) | AttrTag::SourceFile => do_parse!( source_file_index: be_u16 >> (AttributeType::SourceFile { source_file_index }) ) | AttrTag::SourceDebugExtension => do_parse!( debug_extension: take!(self_len) >> (AttributeType::SourceDebugExtension { debug_extension: Arc::new(Vec::from(debug_extension)) }) ) | AttrTag::LineNumberTable => do_parse!( line_count: be_u16 >> lines: count!(line_number, line_count as usize) >> (AttributeType::LineNumberTable { tables: lines }) ) | AttrTag::LocalVariableTable => do_parse!( variable_count: be_u16 >> variables: count!(local_variable, variable_count as usize) >> (AttributeType::LocalVariableTable { tables: variables }) ) | AttrTag::LocalVariableTypeTable => do_parse!( variable_count: be_u16 >> variables: count!(local_variable, variable_count as usize) >> (AttributeType::LocalVariableTypeTable { tables: variables }) ) | AttrTag::Deprecated => value!(AttributeType::Deprecated) | AttrTag::RuntimeVisibleAnnotations => do_parse!( raw: peek!(take!(self_len)) >> annotation_count: be_u16 >> annotations: count!(call!(annotation_entry, cp.clone()), annotation_count as usize) >> (AttributeType::RuntimeVisibleAnnotations {raw: Arc::new(Vec::from(raw)), annotations}) ) | AttrTag::RuntimeInvisibleAnnotations => do_parse!( raw: peek!(take!(self_len)) >> annotation_count: be_u16 >> annotations: count!(call!(annotation_entry, cp.clone()), annotation_count as usize) >> (AttributeType::RuntimeInvisibleAnnotations {raw: Arc::new(Vec::from(raw)), annotations}) ) | AttrTag::RuntimeVisibleParameterAnnotations => do_parse!( raw: peek!(take!(self_len)) >> annotation_count: be_u16 >> annotations: count!(call!(annotation_entry, cp.clone()), annotation_count as usize) >> (AttributeType::RuntimeVisibleParameterAnnotations {raw: Arc::new(Vec::from(raw)), annotations}) ) | AttrTag::RuntimeInvisibleParameterAnnotations => do_parse!( raw: peek!(take!(self_len)) >> annotation_count: be_u16 >> annotations: count!(call!(annotation_entry, cp.clone()), annotation_count as usize) >> (AttributeType::RuntimeInvisibleParameterAnnotations {raw: Arc::new(Vec::from(raw)), annotations}) ) | AttrTag::RuntimeVisibleTypeAnnotations => do_parse!( raw: peek!(take!(self_len)) >> annotation_count: be_u16 >> annotations: count!(call!(type_annotation, cp.clone()), annotation_count as usize) >> (AttributeType::RuntimeVisibleTypeAnnotations {raw: Arc::new(Vec::from(raw)), annotations}) ) | AttrTag::RuntimeInvisibleTypeAnnotations => do_parse!( raw: peek!(take!(self_len)) >> annotation_count: be_u16 >> annotations: count!(call!(type_annotation, cp.clone()), annotation_count as usize) >> (AttributeType::RuntimeInvisibleTypeAnnotations {raw: Arc::new(Vec::from(raw)), annotations}) ) | AttrTag::AnnotationDefault => do_parse!( raw: peek!(take!(self_len)) >> default_value: call!(element_value_type, cp) >> (AttributeType::AnnotationDefault {raw: Arc::new(Vec::from(raw)), default_value}) ) | AttrTag::BootstrapMethods => do_parse!( method_count: be_u16 >> methods: count!(bootstrap_method, method_count as usize) >> (AttributeType::BootstrapMethods {n:method_count, methods}) ) | AttrTag::MethodParameters => do_parse!( parameter_count: be_u8 >> parameters: count!(method_parameter, parameter_count as usize) >> (AttributeType::MethodParameters {parameters}) ) | AttrTag::Unknown => do_parse!( _data: take!(self_len) >> (AttributeType::Unknown) ) )); named_args!(attr_tag(cp: ConstantPool), do_parse!( name_index: be_u16 >> name: value!(constant_pool::get_utf8(&cp, name_index as usize)) >> inner: value!(AttrTag::from(name.as_slice())) >> (inner) )); named_args!(attr_type(cp: ConstantPool), do_parse!( tag: call!(attr_tag, cp.clone()) >> length: be_u32 >> attr: call!(attr_sized, tag, length as usize, cp) >> (attr) )); named_args!(attr_type_vec(cp: ConstantPool)>, do_parse!( attrs_count: be_u16 >> attrs: count!(call!(attr_type, cp.clone()), attrs_count as usize) >> (attrs) )); named_args!(field(cp: ConstantPool), do_parse!( acc_flags: be_u16 >> name_index: be_u16 >> desc_index: be_u16 >> attrs: call!(attr_type_vec, cp) >> (FieldInfo { acc_flags, name_index, desc_index, attrs, }) )); named_args!(method_info(cp: ConstantPool), do_parse!( acc_flags: be_u16 >> name_index: be_u16 >> desc_index: be_u16 >> attrs: call!(attr_type_vec, cp) >> (MethodInfo { acc_flags, name_index, desc_index, attrs, }) )); named!( class_file, do_parse!( _magic: tag!(b"\xCA\xFE\xBA\xBE") >> version: version >> cp: constant_pool >> acc_flags: be_u16 >> this_class: be_u16 >> super_class: be_u16 >> interfaces_count: be_u16 >> interfaces: count!(be_u16, interfaces_count as usize) >> fields_count: be_u16 >> fields: count!(call!(field, cp.clone()), fields_count as usize) >> method_count: be_u16 >> methods: count!(call!(method_info, cp.clone()), method_count as usize) >> attrs: call!(attr_type_vec, cp.clone()) >> (ClassFile { version, cp: cp.clone(), acc_flags, this_class, super_class, interfaces, fields, methods, attrs }) ) ); pub fn parse(input: &[u8]) -> nom::IResult<&[u8], ClassFile> { class_file(input) } ================================================ FILE: crates/class-parser/src/lib.rs ================================================ #![allow(unused)] mod class; mod signature; pub use class::parse as parse_class; pub use signature::{ClassSignature, FieldSignature, MethodSignature}; ================================================ FILE: crates/class-parser/src/signature.rs ================================================ use classfile::BytesRef; use classfile::SignatureType as Type; use nom::bytes::complete::{take, take_till}; use nom::character::complete::{char, one_of}; use nom::combinator::{peek, verify}; use nom::error::make_error; use nom::lib::std::fmt::{Error, Formatter}; use nom::{ branch::alt, bytes::complete::{is_not, tag}, error::{ErrorKind, ParseError, VerboseError}, multi::many1, sequence::delimited, AsBytes, Err, IResult, }; #[derive(Debug)] pub struct ClassSignature { pub items: Vec, } #[derive(Debug, Clone)] pub struct MethodSignature { /* TestNG, org.testng.collections.Maps (Ljava/util/Map;)Ljava/util/Map; public static java.util.Map newHashMap(java.util.Map); */ pub generics: Vec<(BytesRef, Type)>, pub args: Vec, pub retype: Type, } pub struct FieldSignature { pub field_type: Type, } impl ClassSignature { pub fn new(raw: &[u8]) -> Self { let s = unsafe { std::str::from_utf8_unchecked(raw) }; let (_, cs) = Self::parse(s).unwrap(); cs } fn parse(i: &str) -> IResult<&str, ClassSignature> { let (i, items) = parse_types(i)?; Ok((i, ClassSignature { items })) } } impl MethodSignature { pub fn new(raw: &[u8]) -> Self { let s = unsafe { std::str::from_utf8_unchecked(raw) }; let (_, r) = Self::parse(s).unwrap(); r } fn parse(i: &str) -> IResult<&str, MethodSignature> { fn arg0(i: &str) -> IResult<&str, MethodSignature> { let (i, _) = tag("()")(i)?; let (i, retype) = parse_type(i)?; Ok(( i, MethodSignature { generics: vec![], args: vec![], retype, }, )) } fn args(i: &str) -> IResult<&str, MethodSignature> { let (i_return, i_args) = delimited(char('('), is_not(")"), char(')'))(i)?; let (_, args) = parse_types(i_args)?; let (i, retype) = parse_type(i_return)?; Ok(( i, MethodSignature { generics: vec![], args, retype, }, )) } fn generic(i: &str) -> IResult<&str, MethodSignature> { let (i, _) = tag("<")(i)?; let (i, generics) = many1(generic_declare)(i)?; let (i, _) = tag(">")(i)?; let (i, mut r) = MethodSignature::parse(i)?; r.generics = generics; Ok((i, r)) } alt((arg0, args, generic))(i) } } impl FieldSignature { pub fn new(raw: &[u8]) -> Self { let s = unsafe { std::str::from_utf8_unchecked(raw) }; let (_, r) = Self::parse(s).unwrap(); r } fn parse(mut i: &str) -> IResult<&str, FieldSignature> { let (i, field_type) = parse_type(i)?; Ok((i, FieldSignature { field_type })) } } impl Default for MethodSignature { fn default() -> Self { Self { generics: Vec::new(), args: Vec::new(), retype: Type::Void, } } } /////////////////////////// //parser /////////////////////////// fn primitive<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Type, E> { let (i, t) = one_of("BCDFIJSZV")(i)?; let t = match t { 'B' => Type::Byte, 'C' => Type::Char, 'D' => Type::Double, 'F' => Type::Float, 'I' => Type::Int, 'J' => Type::Long, 'S' => Type::Short, 'Z' => Type::Boolean, 'V' => Type::Void, _ => unreachable!(), }; Ok((i, t)) } fn object_desc<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, BytesRef, E> { // should stop when reach ';' or '<' //such as: //(Lorg/testng/internal/IConfiguration;Lorg/testng/ISuite;Lorg/testng/xml/XmlTest;Ljava/lang/String;Lorg/testng/internal/annotations/IAnnotationFinder;ZLjava/util/List;)V // if only take_till(|c| c == ';'), can't process like: // Lxx/xx/xx; let (_, _) = alt((tag("L"), tag("T")))(input)?; let (i, desc) = take_till(|c| c == ';' || c == '<')(input)?; let (i, _) = tag(";")(i)?; let mut buf = Vec::with_capacity(1 + desc.len() + 1); buf.extend_from_slice(desc.as_bytes()); buf.push(b';'); let desc = std::sync::Arc::new(buf); Ok((i, desc)) } fn object_generic<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Type, E> { let (i, tag_prefix) = alt((tag("L"), tag("T")))(i)?; let (i, container) = take_till(|c| c == '<')(i)?; let (mut i, _) = tag("<")(i)?; //signature like: //Ljava/lang/Class<+Lcom/google/inject/Module;>; //<=> 'java.lang.Class' let mut prefix = None; if i.starts_with('+') { prefix = Some(b'+'); let (i2, _) = tag("+")(i)?; i = i2; } let (i, generic_args) = many1(parse_type)(i)?; let (i, _) = tag(">")(i)?; let (i, _) = tag(";")(i)?; //build results let mut buf = Vec::with_capacity(1 + container.len() + 1); buf.extend_from_slice(tag_prefix.as_bytes()); buf.extend_from_slice(container.as_bytes()); buf.push(b';'); let desc = std::sync::Arc::new(buf); Ok((i, Type::Object(desc, Some(generic_args), prefix))) } fn object_normal<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Type, E> { let (i, _) = peek(alt((tag("L"), tag("T"))))(i)?; let (i, desc) = object_desc(i)?; Ok((i, Type::Object(desc, None, None))) } fn object<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Type, E> { alt((object_normal, object_generic))(i) } fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Type, E> { let (i, _) = peek(tag("["))(i)?; let (i, ary_tags) = take_till(|c| c != '[')(i)?; let (mut i, t) = peek(take(1u8))(i)?; let mut buf = vec![]; buf.extend_from_slice(ary_tags.as_bytes()); match t { "L" | "T" => { let (i2, desc) = object_desc(i)?; i = i2; buf.extend_from_slice(desc.as_slice()); } v => { let (i2, _) = take(1u8)(i)?; i = i2; buf.extend_from_slice(v.as_bytes()) } } let desc = std::sync::Arc::new(buf); Ok((i, Type::Array(desc))) } fn generic_declare<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, (BytesRef, Type), E> { let (i, generic_type) = take_till(|c| c == ':')(i)?; let (i, _) = tag(":")(i)?; let (i, t) = parse_type(i)?; let generic_type = std::sync::Arc::new(Vec::from(generic_type)); Ok((i, (generic_type, t))) } fn parse_type<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Type, E> { alt((primitive, object, array))(i) } fn parse_types<'a, E: ParseError<&'a str>>(mut input: &'a str) -> IResult<&str, Vec, E> { let it = std::iter::from_fn(move || { match parse_type::<'a, E>(input) { // when successful, a nom parser returns a tuple of // the remaining input and the output value. // So we replace the captured input data with the // remaining input, to be parsed on the next call Ok((i, o)) => { input = i; Some(o) } _ => None, } }); let mut args = vec![]; for v in it { args.push(v); } Ok((input, args)) } #[cfg(test)] mod tests { use super::ClassSignature; use super::FieldSignature; use super::MethodSignature; use super::Type as SignatureType; use std::sync::Arc; #[test] fn t_method_no_arg() { let expected = MethodSignature { generics: vec![], args: vec![], retype: SignatureType::Void, }; let (_, r) = MethodSignature::parse("()V").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn method_primitive() { let table = vec![ ( MethodSignature { generics: vec![], args: vec![SignatureType::Byte], retype: SignatureType::Void, }, "(B)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Char], retype: SignatureType::Void, }, "(C)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Double], retype: SignatureType::Void, }, "(D)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Float], retype: SignatureType::Void, }, "(F)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Int], retype: SignatureType::Void, }, "(I)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Long], retype: SignatureType::Void, }, "(J)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Short], retype: SignatureType::Void, }, "(S)V", ), ( MethodSignature { generics: vec![], args: vec![SignatureType::Boolean], retype: SignatureType::Void, }, "(Z)V", ), ]; for (expected, desc) in table.iter() { let (_, r) = MethodSignature::parse(desc).unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } } #[test] fn method_array_object() { let expected = MethodSignature { generics: vec![], args: vec![SignatureType::Array(Arc::new(Vec::from( "[[Ljava/lang/String;", )))], retype: SignatureType::Void, }; let (_, r) = MethodSignature::parse("([[Ljava/lang/String;)V").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn method_mix() { let expected = MethodSignature { generics: vec![], args: vec![ SignatureType::Byte, SignatureType::Char, SignatureType::Double, SignatureType::Float, SignatureType::Int, SignatureType::Long, SignatureType::Short, SignatureType::Boolean, SignatureType::Object(Arc::new(Vec::from("Ljava/lang/Integer;")), None, None), ], retype: SignatureType::Object(Arc::new(Vec::from("Ljava/lang/String;")), None, None), }; let (_, r) = MethodSignature::parse("(BCDFIJSZLjava/lang/Integer;)Ljava/lang/String;").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn method_arg_generic() { let generic_args = vec![SignatureType::Object( Arc::new(Vec::from("Ljava/lang/String;")), None, None, )]; let expected = MethodSignature { generics: vec![], args: vec![SignatureType::Object( Arc::new(Vec::from("Ljava/util/List;")), Some(generic_args), None, )], retype: SignatureType::Void, }; let (_, r) = MethodSignature::parse("(Ljava/util/List;)V").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); let expected = MethodSignature { generics: vec![], args: vec![ SignatureType::Object( Arc::new(Vec::from("Lorg/testng/internal/IConfiguration;")), None, None, ), SignatureType::Object(Arc::new(Vec::from("Lorg/testng/ISuite;")), None, None), SignatureType::Object(Arc::new(Vec::from("Lorg/testng/xml/XmlTest;")), None, None), SignatureType::Object(Arc::new(Vec::from("Ljava/lang/String;")), None, None), SignatureType::Object( Arc::new(Vec::from( "Lorg/testng/internal/annotations/IAnnotationFinder;", )), None, None, ), SignatureType::Boolean, SignatureType::Object( Arc::new(Vec::from("Ljava/util/List;")), Some(vec![SignatureType::Object( Arc::new(Vec::from("Lorg/testng/IInvokedMethodListener;")), None, None, )]), None, ), ], retype: SignatureType::Void, }; let (_, r) = MethodSignature::parse("(Lorg/testng/internal/IConfiguration;Lorg/testng/ISuite;Lorg/testng/xml/XmlTest;Ljava/lang/String;Lorg/testng/internal/annotations/IAnnotationFinder;ZLjava/util/List;)V").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn generic1() { let expected = MethodSignature { generics: vec![], args: vec![ SignatureType::Object(Arc::new(Vec::from("TK;")), None, None), SignatureType::Object(Arc::new(Vec::from("TV;")), None, None), ], retype: SignatureType::Void, }; let (_, r) = MethodSignature::parse("(TK;TV;)V").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } //'T' tag in args #[test] fn generic2() { let expected = MethodSignature { generics: vec![], args: vec![SignatureType::Object( Arc::new(Vec::from("TK;")), None, None, )], retype: SignatureType::Object( Arc::new(Vec::from("Ljava/util/Set;")), Some(vec![SignatureType::Object( Arc::new(Vec::from("TV;")), None, None, )]), None, ), }; let (_, r) = MethodSignature::parse("(TK;)Ljava/util/Set;").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn generic_nest1() { let expected = MethodSignature { generics: vec![], args: vec![], retype: SignatureType::Object( Arc::new(Vec::from("Ljava/util/Set;")), Some(vec![SignatureType::Object( Arc::new(Vec::from("Ljava/util/Map$Entry;")), Some(vec![ SignatureType::Object(Arc::new(Vec::from("TK;")), None, None), SignatureType::Object( Arc::new(Vec::from("Ljava/util/Set;")), Some(vec![SignatureType::Object( Arc::new(Vec::from("TV;")), None, None, )]), None, ), ]), None, )]), None, ), }; let (_, r) = MethodSignature::parse( "()Ljava/util/Set;>;>;", ) .unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn generic_method() { let expected = MethodSignature { generics: vec![ ( Arc::new(Vec::from("K")), SignatureType::Object(Arc::new(Vec::from("Ljava/lang/Object;")), None, None), ), ( Arc::new(Vec::from("V")), SignatureType::Object(Arc::new(Vec::from("Ljava/lang/Object;")), None, None), ), ], args: vec![SignatureType::Object( Arc::new(Vec::from("Ljava/util/Map;")), Some(vec![ SignatureType::Object(Arc::new(Vec::from("TK;")), None, None), SignatureType::Object(Arc::new(Vec::from("TV;")), None, None), ]), None, )], retype: SignatureType::Object( Arc::new(Vec::from("Ljava/util/Map;")), Some(vec![ SignatureType::Object(Arc::new(Vec::from("TK;")), None, None), SignatureType::Object(Arc::new(Vec::from("TV;")), None, None), ]), None, ), }; let (_, r) = MethodSignature::parse("(Ljava/util/Map;)Ljava/util/Map;").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn method_return_generic() { let generic_args = vec![SignatureType::Object( Arc::new(Vec::from("Lorg/testng/ITestNGListener;")), None, None, )]; let expected = MethodSignature { generics: vec![], args: vec![], retype: SignatureType::Object( Arc::new(Vec::from("Ljava/util/List;")), Some(generic_args), None, ), }; let (_, r) = MethodSignature::parse("()Ljava/util/List;").unwrap(); assert_eq!(r.args, expected.args); assert_eq!(r.retype, expected.retype); } #[test] fn field() { macro_rules! setup_test { ($desc: expr, $tp: expr) => { let (_, sig) = FieldSignature::parse($desc).unwrap(); assert_eq!(sig.field_type, $tp); }; } setup_test!("B", SignatureType::Byte); setup_test!("C", SignatureType::Char); setup_test!("D", SignatureType::Double); setup_test!("F", SignatureType::Float); setup_test!("I", SignatureType::Int); setup_test!("J", SignatureType::Long); let v = Vec::from("Ljava/lang/Object;"); let v = Arc::new(v); setup_test!("Ljava/lang/Object;", SignatureType::Object(v, None, None)); setup_test!("S", SignatureType::Short); setup_test!("Z", SignatureType::Boolean); let v = Vec::from("[Ljava/lang/Object;"); let v = Arc::new(v); setup_test!("[Ljava/lang/Object;", SignatureType::Array(v)); let v = Vec::from("[[[D"); let v = Arc::new(v); setup_test!("[[[D", SignatureType::Array(v)); } #[test] fn t_class_signature() { let (_, cs) = ClassSignature::parse("Ljava/lang/Object;Lorg/testng/ITestContext;Lorg/testng/internal/ITestResultNotifier;Lorg/testng/internal/thread/graph/IThreadWorkerFactory;").unwrap(); let expected = ClassSignature { items: vec![ SignatureType::Object(Arc::new(Vec::from("Ljava/lang/Object;")), None, None), SignatureType::Object(Arc::new(Vec::from("Lorg/testng/ITestContext;")), None, None), SignatureType::Object( Arc::new(Vec::from("Lorg/testng/internal/ITestResultNotifier;")), None, None, ), SignatureType::Object( Arc::new(Vec::from( "Lorg/testng/internal/thread/graph/IThreadWorkerFactory;", )), Some(vec![SignatureType::Object( Arc::new(Vec::from("Lorg/testng/ITestNGMethod;")), None, None, )]), None, ), ], }; assert_eq!(cs.items, expected.items); } } ================================================ FILE: crates/class-verification/Cargo.toml ================================================ [package] name = "class-verification" version = "0.1.0" authors = ["Dou Chuan <1843657913@qq.com>"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ================================================ FILE: crates/class-verification/src/checker.rs ================================================ use crate::types::ConstantPool; #[derive(Debug, Clone, Copy, PartialEq)] pub enum Err { InvalidCpClassNameIdx, InvalidCpFieldRefClsIdx, InvalidCpFieldRefNameAndTypeIdx, InvalidCpMethodRefClsIdx, InvalidCpMethodRefNameAndTypeIdx, InvalidCpInterfaceMethodRefClsIdx, InvalidCpInterfaceMethodRefNameAndTypeIdx, InvalidCpStrStrIdx, InvalidCpNameAndTypeNameIdx, InvalidCpNameAndTypeDescIdx, InvalidCpMethodHandleRefKind, InvalidCpMethodHandleRefIdx, InvalidCpMethodTypeDescIdx, InvalidCpInvokeDynBootstrapMethodAttrIdx, InvalidCpInvokeDynNameAndTypeIdx, InvalidFieldAccFlags, InvalidFieldNameIdx, InvalidFieldDescIdx, InvalidMethodAccFlags, InvalidMethodNameIdx, InvalidMethodDescIdx, } pub type CheckResult = Result<(), Err>; pub trait Checker { fn check(&self, cp: &ConstantPool) -> CheckResult; } ================================================ FILE: crates/class-verification/src/lib.rs ================================================ ================================================ FILE: crates/classfile/Cargo.toml ================================================ [package] name = "classfile" version = "0.1.0" authors = ["Dou Chuan <1843657913@qq.com>"] edition = "2018" license = "MIT OR Apache-2.0" description = "Types for the class file format of the Java Virtual Machine." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] log = "0.4.0" lazy_static = "1.4.0" ================================================ FILE: crates/classfile/README.md ================================================ ## JVM class file This is the `classfile` crate, which contains definition of class file format of Java Virtual Machine. The spec based on `Java SE 8 Edition`. ================================================ FILE: crates/classfile/src/attributes.rs ================================================ use crate::{BytesRef, U1, U2, U4}; use std::sync::Arc; #[derive(Debug, Clone)] pub enum Type { ConstantValue { constant_value_index: U2, }, Code(Code), StackMapTable { entries: Vec, }, Exceptions { exceptions: Vec, }, InnerClasses { classes: Vec, }, EnclosingMethod { em: EnclosingMethod, }, Synthetic, Signature { signature_index: U2, }, SourceFile { source_file_index: U2, }, SourceDebugExtension { debug_extension: BytesRef, }, LineNumberTable { tables: Vec, }, LocalVariableTable { tables: Vec, }, LocalVariableTypeTable { tables: Vec, }, Deprecated, RuntimeVisibleAnnotations { raw: BytesRef, annotations: Vec, }, RuntimeInvisibleAnnotations { raw: BytesRef, annotations: Vec, }, RuntimeVisibleParameterAnnotations { raw: BytesRef, annotations: Vec, }, RuntimeInvisibleParameterAnnotations { raw: BytesRef, annotations: Vec, }, RuntimeVisibleTypeAnnotations { raw: BytesRef, annotations: Vec, }, RuntimeInvisibleTypeAnnotations { raw: BytesRef, annotations: Vec, }, AnnotationDefault { raw: BytesRef, default_value: ElementValueType, }, BootstrapMethods { n: U2, methods: Vec, }, MethodParameters { parameters: Vec, }, Unknown, } #[derive(Clone, Copy)] pub enum Tag { ConstantValue, Code, StackMapTable, Exceptions, InnerClasses, EnclosingMethod, Synthetic, Signature, SourceFile, SourceDebugExtension, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Deprecated, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations, AnnotationDefault, BootstrapMethods, MethodParameters, Unknown, } impl From<&[u8]> for Tag { fn from(raw: &[u8]) -> Self { match raw { b"ConstantValue" => Tag::ConstantValue, b"Code" => Tag::Code, b"StackMapTable" => Tag::StackMapTable, b"Exceptions" => Tag::Exceptions, b"InnerClasses" => Tag::InnerClasses, b"EnclosingMethod" => Tag::EnclosingMethod, b"Synthetic" => Tag::Synthetic, b"Signature" => Tag::Signature, b"SourceFile" => Tag::SourceFile, b"SourceDebugExtension" => Tag::SourceDebugExtension, b"LineNumberTable" => Tag::LineNumberTable, b"LocalVariableTable" => Tag::LocalVariableTable, b"LocalVariableTypeTable" => Tag::LocalVariableTypeTable, b"Deprecated" => Tag::Deprecated, b"RuntimeVisibleAnnotations" => Tag::RuntimeVisibleAnnotations, b"RuntimeInvisibleAnnotations" => Tag::RuntimeInvisibleAnnotations, b"RuntimeVisibleParameterAnnotations" => Tag::RuntimeVisibleParameterAnnotations, b"RuntimeInvisibleParameterAnnotations" => Tag::RuntimeInvisibleParameterAnnotations, b"RuntimeVisibleTypeAnnotations" => Tag::RuntimeVisibleTypeAnnotations, b"RuntimeInvisibleTypeAnnotations" => Tag::RuntimeInvisibleTypeAnnotations, b"AnnotationDefault" => Tag::AnnotationDefault, b"BootstrapMethods" => Tag::BootstrapMethods, b"MethodParameters" => Tag::MethodParameters, _ => { info!("Unknown attr {}", unsafe { std::str::from_utf8_unchecked(raw) }); // error!("Unknown attr {}", String::from_utf8_lossy(raw)); Tag::Unknown } } } } #[derive(Debug, Clone)] pub struct Code { pub max_stack: U2, pub max_locals: U2, pub code: Arc>, pub exceptions: Vec, pub attrs: Vec, } #[derive(Debug, Clone)] pub struct CodeException { pub start_pc: U2, pub end_pc: U2, pub handler_pc: U2, pub catch_type: U2, } impl CodeException { pub fn contains(&self, pc: U2) -> bool { (self.start_pc..self.end_pc + 1).contains(&pc) } pub fn is_finally(&self) -> bool { self.catch_type == 0 } } #[derive(Debug, Clone)] pub struct AttributeInfo { pub name_index: U2, pub length: U4, pub info: Vec, } pub enum NestedClassAccessPropertyFlag { AccPublic, AccPrivate, AccProtected, AccStatic, AccFinal, AccInterface, AccAbstract, AccSynthetic, AccAnnotation, AccEnum, } #[derive(Debug, Copy, Clone)] pub struct InnerClass { pub inner_class_info_index: U2, pub outer_class_info_index: U2, pub inner_name_index: U2, pub inner_class_access_flags: U2, } #[derive(Debug, Copy, Clone)] pub struct LineNumber { pub start_pc: U2, pub number: U2, } #[derive(Debug, Copy, Clone)] pub struct LocalVariable { pub start_pc: U2, pub length: U2, pub name_index: U2, pub signature_index: U2, pub index: U2, } #[derive(Debug, Clone, Copy)] pub enum ElementValueTag { Byte, Char, Double, Float, Int, Long, Short, Boolean, String, Enum, Class, Annotation, Array, Unknown, } impl From for ElementValueTag { fn from(v: u8) -> Self { match v { b'B' => ElementValueTag::Byte, b'C' => ElementValueTag::Char, b'D' => ElementValueTag::Double, b'F' => ElementValueTag::Float, b'I' => ElementValueTag::Int, b'J' => ElementValueTag::Long, b'S' => ElementValueTag::Short, b'Z' => ElementValueTag::Boolean, b's' => ElementValueTag::String, b'e' => ElementValueTag::Enum, b'c' => ElementValueTag::Class, b'@' => ElementValueTag::Annotation, b'[' => ElementValueTag::Array, _ => ElementValueTag::Unknown, } } } #[derive(Debug, Clone)] pub enum ElementValueType { Byte { val_index: U2 }, Char { val_index: U2 }, Double { val_index: U2 }, Float { val_index: U2 }, Int { val_index: U2 }, Long { val_index: U2 }, Short { val_index: U2 }, Boolean { val_index: U2 }, String { val_index: U2 }, Enum { type_index: U2, val_index: U2 }, Class { index: U2 }, Annotation(AnnotationElementValue), Array { values: Vec }, Unknown, } #[derive(Debug, Clone)] pub struct AnnotationElementValue { pub value: AnnotationEntry, } #[derive(Debug, Clone)] pub struct ElementValuePair { pub name_index: U2, pub value: ElementValueType, } #[derive(Debug, Clone)] pub struct AnnotationEntry { pub type_name: BytesRef, pub pairs: Vec, } #[derive(Debug, Clone)] pub struct BootstrapMethod { pub method_ref: U2, pub args: Vec, } #[derive(Debug)] pub enum MethodParameterAccessFlag { AccFinal = 0x0010, AccSynthetic = 0x1000, AccMandated = 0x8000, } #[derive(Debug, Copy, Clone)] pub struct MethodParameter { pub name_index: U2, pub acc_flags: U2, } #[derive(Debug, Clone)] pub enum VerificationTypeInfo { Top, Integer, Float, Long, Double, Null, UninitializedThis, Object { cpool_index: U2 }, Uninitialized { offset: U2 }, } #[derive(Debug, Clone)] pub enum StackMapFrame { Same { tag: U1, offset_delta: U2, }, SameLocals1StackItem { tag: U1, offset_delta: U2, stack: [VerificationTypeInfo; 1], }, SameLocals1StackItemExtended { tag: U1, offset_delta: U2, stack: [VerificationTypeInfo; 1], }, Chop { tag: U1, offset_delta: U2, }, SameExtended { tag: U1, offset_delta: U2, }, Append { tag: U1, offset_delta: U2, locals: Vec, }, Full { tag: U1, offset_delta: U2, locals: Vec, stack: Vec, }, Reserved(U1), } #[derive(Debug, Clone)] pub struct TypeAnnotation { pub target_info: TargetInfo, pub target_path: Vec, pub type_index: U2, pub pairs: Vec, } #[derive(Debug, Clone)] pub enum TargetInfo { TypeParameter { type_parameter_index: U1, }, SuperType { supertype_index: U2, }, TypeParameterBound { type_parameter_index: U1, bound_index: U1, }, Empty, FormalParameter { formal_parameter_index: U1, }, Throws { throws_type_index: U2, }, LocalVar { table: Vec, }, Catch { exception_table_index: U2, }, Offset { offset: U2, }, TypeArgument { offset: U2, type_argument_index: U1, }, } #[derive(Debug, Clone)] pub struct LocalVarTargetTable { pub start_pc: U2, pub length: U2, pub index: U2, } #[derive(Debug, Clone)] pub struct TypePath { pub type_path_kind: U1, pub type_argument_index: U1, } #[derive(Debug, Clone)] pub struct EnclosingMethod { pub class_index: U2, pub method_index: U2, } ================================================ FILE: crates/classfile/src/classfile.rs ================================================ use crate::attributes::InnerClass; use crate::{attributes::Type, field_info::FieldInfo, method_info::MethodInfo, version::Version}; use crate::{ConstantPool, U2}; #[derive(Debug)] pub struct ClassFile { pub version: Version, pub cp: ConstantPool, pub acc_flags: U2, pub this_class: U2, pub super_class: U2, pub interfaces: Vec, pub fields: Vec, pub methods: Vec, pub attrs: Vec, } impl ClassFile { pub fn inner_classes(&self) -> Option> { for it in self.attrs.iter() { if let Type::InnerClasses { classes } = it { return Some(classes.clone()); } } None } pub fn signature(&self) -> Option { for it in self.attrs.iter() { if let Type::Signature { signature_index } = it { return Some(*signature_index as usize); } } None } } ================================================ FILE: crates/classfile/src/constant_pool.rs ================================================ use crate::consts::{ CONSTANT_INTERFACE_METHOD_REF_TAG, CONSTANT_METHOD_REF_TAG, METHOD_NAME_CLINIT, METHOD_NAME_INIT, }; use crate::{BytesRef, ConstantPool}; use fmt::Debug; use std::fmt; use std::sync::Arc; pub fn get_class_name(cp: &ConstantPool, idx: usize) -> &BytesRef { match cp.get(idx) { Some(Type::Class { name_index }) => get_utf8(cp, *name_index as usize), _ => unreachable!(), } } pub fn get_field_ref(cp: &ConstantPool, idx: usize) -> (u16, u16) { match cp.get(idx) { Some(Type::FieldRef { class_index, name_and_type_index, }) => (*class_index, *name_and_type_index), _ => unreachable!(), } } pub fn get_method_ref(cp: &ConstantPool, idx: usize) -> (u8, u16, u16) { match cp.get(idx) { Some(Type::MethodRef { class_index, name_and_type_index, }) => (CONSTANT_METHOD_REF_TAG, *class_index, *name_and_type_index), Some(Type::InterfaceMethodRef { class_index, name_and_type_index, }) => ( CONSTANT_INTERFACE_METHOD_REF_TAG, *class_index, *name_and_type_index, ), _ => unreachable!(), } } pub fn get_name_and_type(cp: &ConstantPool, idx: usize) -> (&BytesRef, &BytesRef) { match cp.get(idx) { Some(Type::NameAndType { name_index, desc_index, }) => ( get_utf8(cp, *name_index as usize), get_utf8(cp, *desc_index as usize), ), _ => unreachable!(), } } pub fn get_utf8(cp: &ConstantPool, idx: usize) -> &BytesRef { match cp.get(idx) { Some(Type::Utf8 { bytes }) => bytes, _ => unreachable!(), } } pub fn get_string(cp: &ConstantPool, idx: usize) -> String { match cp.get(idx) { Some(Type::String { string_index }) => { let v = get_utf8(cp, *string_index as usize); let raw = construct_string_raw(v.as_slice()); String::from_utf16_lossy(raw.as_slice()) } _ => unreachable!(), } } pub fn construct_string_raw(bs: &[u8]) -> Vec { let length = bs.len(); let mut buffer: Vec = Vec::with_capacity(length); let mut pos = 0; while pos < length { if bs[pos] & 0x80 == 0 { let v = bs[pos] as u16; buffer.push(v); pos += 1; } else if bs[pos] & 0xE0 == 0xC0 && (bs[pos + 1] & 0xC0) == 0x80 { let x = bs[pos] as u16; let y = bs[pos + 1] as u16; let v = ((x & 0x1f) << 6) + (y & 0x3f); buffer.push(v); pos += 2; } else if bs[pos] & 0xF0 == 0xE0 && (bs[pos + 1] & 0xC0) == 0x80 && (bs[pos + 2] & 0xC0) == 0x80 { let x = bs[pos] as u16; let y = bs[pos + 1] as u16; let z = bs[pos + 2] as u16; let v = ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f); buffer.push(v); pos += 3; } else if bs[pos] == 0xED && (bs[pos + 1] & 0xF0 == 0xA0) && (bs[pos + 2] & 0xC0 == 0x80) && (bs[pos + 3] == 0xED) && (bs[pos + 4] & 0xF0 == 0xB0) && (bs[pos + 5] & 0xC0 == 0x80) { let v = bs[pos + 1] as u32; let w = bs[pos + 2] as u32; let y = bs[pos + 4] as u32; let z = bs[pos + 5] as u32; let vv = 0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f); buffer.push(vv as u16); pos += 6; } else { unreachable!() } } buffer } #[derive(Debug, Clone)] pub enum Type { Nop, Class { name_index: u16, }, FieldRef { class_index: u16, name_and_type_index: u16, }, MethodRef { class_index: u16, name_and_type_index: u16, }, InterfaceMethodRef { class_index: u16, name_and_type_index: u16, }, String { string_index: u16, }, Integer { v: [u8; 4], }, Float { v: [u8; 4], }, Long { v: [u8; 8], }, Double { v: [u8; 8], }, NameAndType { name_index: u16, desc_index: u16, }, Utf8 { bytes: BytesRef, }, MethodHandle { ref_kind: u8, ref_index: u16, }, MethodType { desc_index: u16, }, InvokeDynamic { bootstrap_method_attr_index: u16, name_and_type_index: u16, }, Unknown, } #[derive(Debug, Clone, Copy)] pub enum Tag { Class, FieldRef, MethodRef, InterfaceMethodRef, String, Integer, Float, Long, Double, NameAndType, Utf8, MethodHandle, MethodType, InvokeDynamic, } impl From for Tag { fn from(tag: u8) -> Self { match tag { 7 => Tag::Class, 9 => Tag::FieldRef, 10 => Tag::MethodRef, 11 => Tag::InterfaceMethodRef, 8 => Tag::String, 3 => Tag::Integer, 4 => Tag::Float, 5 => Tag::Long, 6 => Tag::Double, 12 => Tag::NameAndType, 1 => Tag::Utf8, 15 => Tag::MethodHandle, 16 => Tag::MethodType, 18 => Tag::InvokeDynamic, _ => unreachable!(), } } } impl Type { pub fn as_cp_item<'a, 'b>(&'a self, cp: &'b ConstantPool) -> ConstantPoolItem<'a, 'b> { ConstantPoolItem { cp, item: self } } } pub struct ConstantPoolItem<'item, 'cp> { cp: &'cp ConstantPool, item: &'item Type, } impl Debug for ConstantPoolItem<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.item { Type::Nop => f.debug_struct("Nop").finish(), Type::Class { name_index } => f .debug_struct("Class") .field("name_index", name_index) .field( "name", &self .cp .get(*name_index as usize) .map(|t| t.as_cp_item(self.cp)), ) .finish(), Type::Utf8 { bytes } => f .debug_struct("Utf8") .field("string", &std::str::from_utf8(bytes)) .finish(), _ => write!(f, "TODO debug for: {:?}", self.item), } } } ================================================ FILE: crates/classfile/src/consts.rs ================================================ use crate::U4; pub const MAGIC: U4 = 0xCAFEBABE; pub const METHOD_NAME_INIT: &[u8] = b""; pub const METHOD_NAME_CLINIT: &[u8] = b""; pub const MAX_CP_ENTRIES: u16 = 65535; pub const MAX_FIELDS_NUM: u16 = 65535; pub const MAX_METHODS_NUM: u16 = 65535; pub const MAX_DIRECT_SUPER_INTERFACES_NUM: u16 = 65535; pub const MAX_LOCAL_VARS_NUM: u16 = 65535; pub const MAX_OP_STACKS_SIZE: u16 = 65535; pub const MAX_METHOD_PARAMS_NUM: u16 = 255; pub const MAX_CONST_STR_LEN: u16 = 65535; pub const MAX_ARRAY_DIMENSIONS: u16 = 255; pub const J_OBJECT: &[u8] = b"java/lang/Object"; pub const J_CLONEABLE: &[u8] = b"java/lang/Cloneable"; pub const J_SERIALIZABLE: &[u8] = b"java/io/Serializable"; pub const J_CLASS: &[u8] = b"java/lang/Class"; pub const J_STRING: &[u8] = b"java/lang/String"; pub const J_THREAD: &[u8] = b"java/lang/Thread"; pub const J_THREAD_GROUP: &[u8] = b"java/lang/ThreadGroup"; pub const J_SYSTEM: &[u8] = b"java/lang/System"; pub const J_INPUT_STREAM: &[u8] = b"java/io/InputStream"; pub const J_PRINT_STREAM: &[u8] = b"java/io/PrintStream"; pub const J_SECURITY_MANAGER: &[u8] = b"java/lang/SecurityManager"; pub const J_FIELD: &[u8] = b"java/lang/reflect/Field"; pub const J_METHOD: &[u8] = b"java/lang/reflect/Method"; pub const J_METHOD_CTOR: &[u8] = b"java/lang/reflect/Constructor"; pub const J_ACCESSIBLE_OBJECT: &[u8] = b"java/lang/reflect/AccessibleObject"; pub const J_METHODHANDLE: &[u8] = b"java/lang/invoke/MethodHandle"; pub const J_METHODTYPE: &[u8] = b"java/lang/invoke/MethodType"; pub const J_INTERNAL_ERROR: &[u8] = b"java/lang/InternalError"; pub const J_NPE: &[u8] = b"java/lang/NullPointerException"; pub const J_IOEXCEPTION: &[u8] = b"java/io/IOException"; pub const J_ARRAY_INDEX_OUT_OF_BOUNDS: &[u8] = b"java/lang/ArrayIndexOutOfBoundsException"; pub const J_CLASS_NOT_FOUND: &[u8] = b"java/lang/ClassNotFoundException"; pub const J_ARITHMETIC_EX: &[u8] = b"java/lang/ArithmeticException"; pub const J_SOE: &[u8] = b"java/lang/StackOverflowError"; pub const J_NASE: &[u8] = b"java/lang/NegativeArraySizeException"; pub const J_CCE: &[u8] = b"java/lang/ClassCastException"; pub const J_THROWABLE: &[u8] = b"java/lang/Throwable"; pub const CONSTANT_METHOD_REF_TAG: u8 = 10; pub const CONSTANT_INTERFACE_METHOD_REF_TAG: u8 = 11; ================================================ FILE: crates/classfile/src/field_info.rs ================================================ use crate::attributes::Type; use crate::U2; #[derive(Debug)] pub struct FieldInfo { pub acc_flags: U2, pub name_index: U2, pub desc_index: U2, pub attrs: Vec, } ================================================ FILE: crates/classfile/src/flags.rs ================================================ use crate::U2; macro_rules! def_acc { ($name:ident, $v:expr) => { pub const $name: U2 = $v; }; } def_acc!(ACC_PUBLIC, 0x0001); def_acc!(ACC_PRIVATE, 0x0002); def_acc!(ACC_PROTECTED, 0x0004); def_acc!(ACC_STATIC, 0x0008); def_acc!(ACC_FINAL, 0x0010); def_acc!(ACC_SYNCHRONIZED, 0x0020); def_acc!(ACC_SUPER, 0x0020); def_acc!(ACC_VOLATILE, 0x0040); def_acc!(ACC_BRIDGE, 0x0040); def_acc!(ACC_VARARGS, 0x0080); def_acc!(ACC_TRANSIENT, 0x0080); def_acc!(ACC_NATIVE, 0x0100); def_acc!(ACC_INTERFACE, 0x0200); def_acc!(ACC_ABSTRACT, 0x0400); def_acc!(ACC_STRICT, 0x0800); def_acc!(ACC_SYNTHETIC, 0x1000); def_acc!(ACC_ANNOTATION, 0x2000); def_acc!(ACC_ENUM, 0x4000); def_acc!(ACC_MIRANDA, 0x8000); def_acc!(ACC_REFLECT_MASK, 0xffff); ================================================ FILE: crates/classfile/src/lib.rs ================================================ #![allow(unused)] //! Provides types for working with class file. //! //! The `classfile` crate provides types for describing the //! class file format of the Java Virtual Machine. //! //! It's not class file parser. #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; pub mod attributes; mod classfile; pub mod constant_pool; pub mod consts; mod field_info; pub mod flags; mod method_info; mod opcode; mod signature; mod version; pub type U1 = u8; pub type U2 = u16; pub type U4 = u32; pub type BytesRef = std::sync::Arc>; pub type ConstantPool = std::sync::Arc>; pub use crate::classfile::ClassFile; pub use attributes::Type as AttributeType; pub use constant_pool::Type as ConstantPoolType; pub use field_info::FieldInfo; pub use method_info::MethodInfo; pub use opcode::OpCode; pub use signature::Type as SignatureType; pub use version::Version; ================================================ FILE: crates/classfile/src/method_info.rs ================================================ use crate::attributes::{Code, CodeException, LineNumber, LocalVariable, StackMapFrame, Type}; use crate::constant_pool; use crate::{BytesRef, ConstantPool, U2}; use std::collections::HashMap; #[derive(Debug, Clone)] pub struct MethodInfo { pub acc_flags: U2, pub name_index: U2, pub desc_index: U2, pub attrs: Vec, } impl MethodInfo { pub fn get_code(&self) -> Option { for it in self.attrs.iter() { if let Type::Code(code) = it { return Some(code.clone()); } } None } pub fn get_line_number_table(&self) -> Vec { let mut line_num_table = Vec::new(); self.attrs.iter().for_each(|attr| { if let Type::Code(code) = attr { code.attrs.iter().for_each(|it| { if let Type::LineNumberTable { tables } = it { line_num_table.extend_from_slice(tables.as_slice()); } }); } }); line_num_table } pub fn get_throws(&self) -> Option> { for it in self.attrs.iter() { if let Type::Exceptions { exceptions } = it { return Some(exceptions.clone()); } } None } pub fn get_ex_table(&self) -> Option> { for it in self.attrs.iter() { if let Type::Code(code) = it { if !code.exceptions.is_empty() { return Some(code.exceptions.clone()); } } } None } pub fn get_stack_map_table(&self) -> Option> { if let Some(code) = self.get_code() { for it in code.attrs.iter() { if let Type::StackMapTable { entries } = it { return Some(entries.clone()); } } } None } pub fn get_local_variable_table(&self) -> Option> { if let Some(code) = self.get_code() { for it in code.attrs.iter() { if let Type::LocalVariableTable { tables } = it { return Some(tables.clone()); } } } None } pub fn get_local_variable_type_table(&self) -> Option> { if let Some(code) = self.get_code() { for it in code.attrs.iter() { if let Type::LocalVariableTypeTable { tables } = it { return Some(tables.clone()); } } } None } } ================================================ FILE: crates/classfile/src/opcode.rs ================================================ #![allow(non_camel_case_types)] #[repr(u8)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum OpCode { //Constants nop, aconst_null, iconst_m1, iconst_0, iconst_1, iconst_2, iconst_3, iconst_4, iconst_5, lconst_0, lconst_1, fconst_0, fconst_1, fconst_2, dconst_0, dconst_1, bipush, sipush, ldc, ldc_w, ldc2_w, //Loads iload, lload, fload, dload, aload, iload_0, iload_1, iload_2, iload_3, lload_0, lload_1, lload_2, lload_3, fload_0, fload_1, fload_2, fload_3, dload_0, dload_1, dload_2, dload_3, aload_0, aload_1, aload_2, aload_3, iaload, laload, faload, daload, aaload, baload, caload, saload, //Stores istore, lstore, fstore, dstore, astore, istore_0, istore_1, istore_2, istore_3, lstore_0, lstore_1, lstore_2, lstore_3, fstore_0, fstore_1, fstore_2, fstore_3, dstore_0, dstore_1, dstore_2, dstore_3, astore_0, astore_1, astore_2, astore_3, iastore, lastore, fastore, dastore, aastore, bastore, castore, sastore, //Stack pop, pop2, dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap, //Math iadd, ladd, fadd, dadd, isub, lsub, fsub, dsub, imul, lmul, fmul, dmul, idiv, ldiv, fdiv, ddiv, irem, lrem, frem, //deprecated drem, //deprecated ineg, lneg, fneg, //deprecated dneg, //deprecated ishl, lshl, ishr, lshr, iushr, lushr, iand, land, ior, lor, ixor, lxor, iinc, //Conversions i2l, i2f, i2d, l2i, l2f, l2d, f2i, f2l, f2d, d2i, d2l, d2f, i2b, i2c, i2s, //Comparisons lcmp, fcmpl, fcmpg, dcmpl, dcmpg, ifeq, ifne, iflt, ifge, ifgt, ifle, if_icmpeq, if_icmpne, if_icmplt, if_icmpge, if_icmpgt, if_icmple, if_acmpeq, if_acmpne, //Control goto, jsr, //deprecated ret, //deprecated tableswitch, lookupswitch, ireturn, lreturn, freturn, dreturn, areturn, return_void, //References getstatic, putstatic, getfield, putfield, invokevirtual, invokespecial, invokestatic, invokeinterface, invokedynamic, new, newarray, anewarray, arraylength, athrow, checkcast, instanceof, monitorenter, monitorexit, //Extended wide, //deprecated multianewarray, ifnull, ifnonnull, goto_w, //deprecated jsr_w, //deprecated reserved, breakpoint, impdep1, impdep2, } impl From for OpCode { #[inline] fn from(v: u8) -> Self { match v { 0 => OpCode::nop, 1 => OpCode::aconst_null, 2 => OpCode::iconst_m1, 3 => OpCode::iconst_0, 4 => OpCode::iconst_1, 5 => OpCode::iconst_2, 6 => OpCode::iconst_3, 7 => OpCode::iconst_4, 8 => OpCode::iconst_5, 9 => OpCode::lconst_0, 10 => OpCode::lconst_1, 11 => OpCode::fconst_0, 12 => OpCode::fconst_1, 13 => OpCode::fconst_2, 14 => OpCode::dconst_0, 15 => OpCode::dconst_1, 16 => OpCode::bipush, 17 => OpCode::sipush, 18 => OpCode::ldc, 19 => OpCode::ldc_w, 20 => OpCode::ldc2_w, 21 => OpCode::iload, 22 => OpCode::lload, 23 => OpCode::fload, 24 => OpCode::dload, 25 => OpCode::aload, 26 => OpCode::iload_0, 27 => OpCode::iload_1, 28 => OpCode::iload_2, 29 => OpCode::iload_3, 30 => OpCode::lload_0, 31 => OpCode::lload_1, 32 => OpCode::lload_2, 33 => OpCode::lload_3, 34 => OpCode::fload_0, 35 => OpCode::fload_1, 36 => OpCode::fload_2, 37 => OpCode::fload_3, 38 => OpCode::dload_0, 39 => OpCode::dload_1, 40 => OpCode::dload_2, 41 => OpCode::dload_3, 42 => OpCode::aload_0, 43 => OpCode::aload_1, 44 => OpCode::aload_2, 45 => OpCode::aload_3, 46 => OpCode::iaload, 47 => OpCode::laload, 48 => OpCode::faload, 49 => OpCode::daload, 50 => OpCode::aaload, 51 => OpCode::baload, 52 => OpCode::caload, 53 => OpCode::saload, 54 => OpCode::istore, 55 => OpCode::lstore, 56 => OpCode::fstore, 57 => OpCode::dstore, 58 => OpCode::astore, 59 => OpCode::istore_0, 60 => OpCode::istore_1, 61 => OpCode::istore_2, 62 => OpCode::istore_3, 63 => OpCode::lstore_0, 64 => OpCode::lstore_1, 65 => OpCode::lstore_2, 66 => OpCode::lstore_3, 67 => OpCode::fstore_0, 68 => OpCode::fstore_1, 69 => OpCode::fstore_2, 70 => OpCode::fstore_3, 71 => OpCode::dstore_0, 72 => OpCode::dstore_1, 73 => OpCode::dstore_2, 74 => OpCode::dstore_3, 75 => OpCode::astore_0, 76 => OpCode::astore_1, 77 => OpCode::astore_2, 78 => OpCode::astore_3, 79 => OpCode::iastore, 80 => OpCode::lastore, 81 => OpCode::fastore, 82 => OpCode::dastore, 83 => OpCode::aastore, 84 => OpCode::bastore, 85 => OpCode::castore, 86 => OpCode::sastore, 87 => OpCode::pop, 88 => OpCode::pop2, 89 => OpCode::dup, 90 => OpCode::dup_x1, 91 => OpCode::dup_x2, 92 => OpCode::dup2, 93 => OpCode::dup2_x1, 94 => OpCode::dup_x2, 95 => OpCode::swap, 96 => OpCode::iadd, 97 => OpCode::ladd, 98 => OpCode::fadd, 99 => OpCode::dadd, 100 => OpCode::isub, 101 => OpCode::lsub, 102 => OpCode::fsub, 103 => OpCode::dsub, 104 => OpCode::imul, 105 => OpCode::lmul, 106 => OpCode::fmul, 107 => OpCode::dmul, 108 => OpCode::idiv, 109 => OpCode::ldiv, 110 => OpCode::fdiv, 111 => OpCode::ddiv, 112 => OpCode::irem, 113 => OpCode::lrem, 114 => OpCode::frem, 115 => OpCode::drem, 116 => OpCode::ineg, 117 => OpCode::lneg, 118 => OpCode::fneg, 119 => OpCode::dneg, 120 => OpCode::ishl, 121 => OpCode::lshl, 122 => OpCode::ishr, 123 => OpCode::lshr, 124 => OpCode::iushr, 125 => OpCode::lushr, 126 => OpCode::iand, 127 => OpCode::land, 128 => OpCode::ior, 129 => OpCode::lor, 130 => OpCode::ixor, 131 => OpCode::lxor, 132 => OpCode::iinc, 133 => OpCode::i2l, 134 => OpCode::i2f, 135 => OpCode::i2d, 136 => OpCode::l2i, 137 => OpCode::l2f, 138 => OpCode::l2d, 139 => OpCode::f2i, 140 => OpCode::f2l, 141 => OpCode::f2d, 142 => OpCode::d2i, 143 => OpCode::d2l, 144 => OpCode::d2f, 145 => OpCode::i2b, 146 => OpCode::i2c, 147 => OpCode::i2s, 148 => OpCode::lcmp, 149 => OpCode::fcmpl, 150 => OpCode::fcmpg, 151 => OpCode::dcmpl, 152 => OpCode::dcmpg, 153 => OpCode::ifeq, 154 => OpCode::ifne, 155 => OpCode::iflt, 156 => OpCode::ifge, 157 => OpCode::ifgt, 158 => OpCode::ifle, 159 => OpCode::if_icmpeq, 160 => OpCode::if_icmpne, 161 => OpCode::if_icmplt, 162 => OpCode::if_icmpge, 163 => OpCode::if_icmpgt, 164 => OpCode::if_icmple, 165 => OpCode::if_acmpeq, 166 => OpCode::if_acmpne, 167 => OpCode::goto, 168 => OpCode::jsr, 169 => OpCode::ret, 170 => OpCode::tableswitch, 171 => OpCode::lookupswitch, 172 => OpCode::ireturn, 173 => OpCode::lreturn, 174 => OpCode::freturn, 175 => OpCode::dreturn, 176 => OpCode::areturn, 177 => OpCode::return_void, 178 => OpCode::getstatic, 179 => OpCode::putstatic, 180 => OpCode::getfield, 181 => OpCode::putfield, 182 => OpCode::invokevirtual, 183 => OpCode::invokespecial, 184 => OpCode::invokestatic, 185 => OpCode::invokeinterface, 186 => OpCode::invokedynamic, 187 => OpCode::new, 188 => OpCode::newarray, 189 => OpCode::anewarray, 190 => OpCode::arraylength, 191 => OpCode::athrow, 192 => OpCode::checkcast, 193 => OpCode::instanceof, 194 => OpCode::monitorenter, 195 => OpCode::monitorexit, 196 => OpCode::wide, 197 => OpCode::multianewarray, 198 => OpCode::ifnull, 199 => OpCode::ifnonnull, 200 => OpCode::goto_w, 201 => OpCode::jsr_w, 202 => OpCode::breakpoint, 254 => OpCode::impdep1, 255 => OpCode::impdep2, _ => OpCode::reserved, } } } impl Into<&'static str> for OpCode { fn into(self) -> &'static str { match self { OpCode::nop => "nop", OpCode::aconst_null => "aconst_null", OpCode::iconst_m1 => "iconst_m1", OpCode::iconst_0 => "iconst_0", OpCode::iconst_1 => "iconst_1", OpCode::iconst_2 => "iconst_2", OpCode::iconst_3 => "iconst_3", OpCode::iconst_4 => "iconst_4", OpCode::iconst_5 => "iconst_5", OpCode::lconst_0 => "lconst_0", OpCode::lconst_1 => "lconst_1", OpCode::fconst_0 => "fconst_0", OpCode::fconst_1 => "fconst_1", OpCode::fconst_2 => "fconst_2", OpCode::dconst_0 => "dconst_0", OpCode::dconst_1 => "dconst_1", OpCode::bipush => "bipush", OpCode::sipush => "sipush", OpCode::ldc => "ldc", OpCode::ldc_w => "ldc_w", OpCode::ldc2_w => "ldc2_w", OpCode::iload => "iload", OpCode::lload => "lload", OpCode::fload => "fload", OpCode::dload => "dload", OpCode::aload => "aload", OpCode::iload_0 => "iload_0", OpCode::iload_1 => "iload_1", OpCode::iload_2 => "iload_2", OpCode::iload_3 => "iload_3", OpCode::lload_0 => "lload_0", OpCode::lload_1 => "lload_1", OpCode::lload_2 => "lload_2", OpCode::lload_3 => "lload_3", OpCode::fload_0 => "fload_0", OpCode::fload_1 => "fload_1", OpCode::fload_2 => "fload_2", OpCode::fload_3 => "fload_3", OpCode::dload_0 => "dload_0", OpCode::dload_1 => "dload_1", OpCode::dload_2 => "dload_2", OpCode::dload_3 => "dload_3", OpCode::aload_0 => "aload_0", OpCode::aload_1 => "aload_1", OpCode::aload_2 => "aload_2", OpCode::aload_3 => "aload_3", OpCode::iaload => "iaload", OpCode::laload => "laload", OpCode::faload => "faload", OpCode::daload => "daload", OpCode::aaload => "aaload", OpCode::baload => "baload", OpCode::caload => "caload", OpCode::saload => "saload", OpCode::istore => "istore", OpCode::lstore => "lstore", OpCode::fstore => "fstore", OpCode::dstore => "dstore", OpCode::astore => "astore", OpCode::istore_0 => "istore_0", OpCode::istore_1 => "istore_1", OpCode::istore_2 => "istore_2", OpCode::istore_3 => "istore_3", OpCode::lstore_0 => "lstore_0", OpCode::lstore_1 => "lstore_1", OpCode::lstore_2 => "lstore_2", OpCode::lstore_3 => "lstore_3", OpCode::fstore_0 => "fstore_0", OpCode::fstore_1 => "fstore_1", OpCode::fstore_2 => "fstore_2", OpCode::fstore_3 => "fstore_3", OpCode::dstore_0 => "dstore_0", OpCode::dstore_1 => "dstore_1", OpCode::dstore_2 => "dstore_2", OpCode::dstore_3 => "dstore_3", OpCode::astore_0 => "astore_0", OpCode::astore_1 => "astore_1", OpCode::astore_2 => "astore_2", OpCode::astore_3 => "astore_3", OpCode::iastore => "iastore", OpCode::lastore => "lastore", OpCode::fastore => "fastore", OpCode::dastore => "dastore", OpCode::aastore => "aastore", OpCode::bastore => "bastore", OpCode::castore => "castore", OpCode::sastore => "sastore", OpCode::pop => "pop", OpCode::pop2 => "pop2", OpCode::dup => "dup", OpCode::dup_x1 => "dup_x1", OpCode::dup_x2 => "dup_x2", OpCode::dup2 => "dup2", OpCode::dup2_x1 => "dup2_x1", OpCode::dup2_x2 => "dup2_x2", OpCode::swap => "swap", OpCode::iadd => "iadd", OpCode::ladd => "ladd", OpCode::fadd => "fadd", OpCode::dadd => "dadd", OpCode::isub => "isub", OpCode::lsub => "lsub", OpCode::fsub => "fsub", OpCode::dsub => "dsub", OpCode::imul => "imul", OpCode::lmul => "lmul", OpCode::fmul => "fmul", OpCode::dmul => "dmul", OpCode::idiv => "idiv", OpCode::ldiv => "ldiv", OpCode::fdiv => "fdiv", OpCode::ddiv => "ddiv", OpCode::irem => "irem", OpCode::lrem => "lrem", OpCode::frem => "frem", OpCode::drem => "drem", OpCode::ineg => "ineg", OpCode::lneg => "lneg", OpCode::fneg => "fneg", OpCode::dneg => "dneg", OpCode::ishl => "ishl", OpCode::lshl => "lshl", OpCode::ishr => "ishr", OpCode::lshr => "lshr", OpCode::iushr => "iushr", OpCode::lushr => "lushr", OpCode::iand => "iand", OpCode::land => "land", OpCode::ior => "ior", OpCode::lor => "lor", OpCode::ixor => "ixor", OpCode::lxor => "lxor", OpCode::iinc => "iinc", OpCode::i2l => "i2l", OpCode::i2f => "i2f", OpCode::i2d => "i2d", OpCode::l2i => "l2i", OpCode::l2f => "l2f", OpCode::l2d => "l2d", OpCode::f2i => "f2i", OpCode::f2l => "f2l", OpCode::f2d => "f2d", OpCode::d2i => "d2i", OpCode::d2l => "d2l", OpCode::d2f => "d2f", OpCode::i2b => "i2b", OpCode::i2c => "i2c", OpCode::i2s => "i2s", OpCode::lcmp => "lcmp", OpCode::fcmpl => "fcmpl", OpCode::fcmpg => "fcmpg", OpCode::dcmpl => "dcmpl", OpCode::dcmpg => "dcmpg", OpCode::ifeq => "ifeq", OpCode::ifne => "ifne", OpCode::iflt => "iflt", OpCode::ifge => "ifge", OpCode::ifgt => "ifgt", OpCode::ifle => "ifle", OpCode::if_icmpeq => "if_icmpeq", OpCode::if_icmpne => "if_icmpne", OpCode::if_icmplt => "if_icmplt", OpCode::if_icmpge => "if_icmpge", OpCode::if_icmpgt => "if_icmpgt", OpCode::if_icmple => "if_icmple", OpCode::if_acmpeq => "if_acmpeq", OpCode::if_acmpne => "if_acmpne", OpCode::goto => "goto", OpCode::jsr => "jsr", OpCode::ret => "ret", OpCode::tableswitch => "tableswitch", OpCode::lookupswitch => "lookupswitch", OpCode::ireturn => "ireturn", OpCode::lreturn => "lreturn", OpCode::freturn => "freturn", OpCode::dreturn => "dreturn", OpCode::areturn => "areturn", OpCode::return_void => "return", OpCode::getstatic => "getstatic", OpCode::putstatic => "putstatic", OpCode::getfield => "getfield", OpCode::putfield => "putfield", OpCode::invokevirtual => "invokevirtual", OpCode::invokespecial => "invokespecial", OpCode::invokestatic => "invokestatic", OpCode::invokeinterface => "invokeinterface", OpCode::invokedynamic => "invokedynamic", OpCode::new => "new", OpCode::newarray => "newarray", OpCode::anewarray => "anewarray", OpCode::arraylength => "arraylength", OpCode::athrow => "athrow", OpCode::checkcast => "checkcast", OpCode::instanceof => "instanceof", OpCode::monitorenter => "monitorenter", OpCode::monitorexit => "monitorexit", OpCode::wide => "wide", OpCode::multianewarray => "multianewarray", OpCode::ifnull => "ifnull", OpCode::ifnonnull => "ifnonnull", OpCode::goto_w => "goto_w", OpCode::jsr_w => "jsr_w", _ => unreachable!(), } } } #[cfg(test)] mod tests { use super::OpCode; #[test] fn t_opcode() { assert_eq!(OpCode::nop, OpCode::from(0)); assert_eq!(OpCode::aconst_null, OpCode::from(1)); assert_eq!(OpCode::iconst_m1, OpCode::from(2)); assert_eq!(OpCode::iconst_0, OpCode::from(3)); assert_eq!(OpCode::iconst_1, OpCode::from(4)); assert_eq!(OpCode::iconst_2, OpCode::from(5)); assert_eq!(OpCode::iconst_3, OpCode::from(6)); assert_eq!(OpCode::iconst_4, OpCode::from(7)); assert_eq!(OpCode::iconst_5, OpCode::from(8)); assert_eq!(OpCode::lconst_0, OpCode::from(9)); assert_eq!(OpCode::lconst_1, OpCode::from(10)); assert_eq!(OpCode::fconst_0, OpCode::from(11)); assert_eq!(OpCode::fconst_1, OpCode::from(12)); assert_eq!(OpCode::fconst_2, OpCode::from(13)); assert_eq!(OpCode::dconst_0, OpCode::from(14)); assert_eq!(OpCode::dconst_1, OpCode::from(15)); assert_eq!(OpCode::bipush, OpCode::from(16)); assert_eq!(OpCode::sipush, OpCode::from(17)); assert_eq!(OpCode::ldc, OpCode::from(18)); assert_eq!(OpCode::ldc_w, OpCode::from(19)); assert_eq!(OpCode::ldc2_w, OpCode::from(20)); assert_eq!(OpCode::iload, OpCode::from(21)); assert_eq!(OpCode::lload, OpCode::from(22)); assert_eq!(OpCode::fload, OpCode::from(23)); assert_eq!(OpCode::dload, OpCode::from(24)); assert_eq!(OpCode::aload, OpCode::from(25)); assert_eq!(OpCode::iload_0, OpCode::from(26)); assert_eq!(OpCode::iload_1, OpCode::from(27)); assert_eq!(OpCode::iload_2, OpCode::from(28)); assert_eq!(OpCode::iload_3, OpCode::from(29)); assert_eq!(OpCode::lload_0, OpCode::from(30)); assert_eq!(OpCode::lload_1, OpCode::from(31)); assert_eq!(OpCode::lload_2, OpCode::from(32)); assert_eq!(OpCode::lload_3, OpCode::from(33)); assert_eq!(OpCode::fload_0, OpCode::from(34)); assert_eq!(OpCode::fload_1, OpCode::from(35)); assert_eq!(OpCode::fload_2, OpCode::from(36)); assert_eq!(OpCode::fload_3, OpCode::from(37)); assert_eq!(OpCode::dload_0, OpCode::from(38)); assert_eq!(OpCode::dload_1, OpCode::from(39)); assert_eq!(OpCode::dload_2, OpCode::from(40)); assert_eq!(OpCode::dload_3, OpCode::from(41)); assert_eq!(OpCode::aload_0, OpCode::from(42)); assert_eq!(OpCode::aload_1, OpCode::from(43)); assert_eq!(OpCode::aload_2, OpCode::from(44)); assert_eq!(OpCode::aload_3, OpCode::from(45)); assert_eq!(OpCode::iaload, OpCode::from(46)); assert_eq!(OpCode::laload, OpCode::from(47)); assert_eq!(OpCode::faload, OpCode::from(48)); assert_eq!(OpCode::daload, OpCode::from(49)); assert_eq!(OpCode::aaload, OpCode::from(50)); assert_eq!(OpCode::baload, OpCode::from(51)); assert_eq!(OpCode::caload, OpCode::from(52)); assert_eq!(OpCode::saload, OpCode::from(53)); assert_eq!(OpCode::istore, OpCode::from(54)); assert_eq!(OpCode::lstore, OpCode::from(55)); assert_eq!(OpCode::fstore, OpCode::from(56)); assert_eq!(OpCode::dstore, OpCode::from(57)); assert_eq!(OpCode::astore, OpCode::from(58)); assert_eq!(OpCode::istore_0, OpCode::from(59)); assert_eq!(OpCode::istore_1, OpCode::from(60)); assert_eq!(OpCode::istore_2, OpCode::from(61)); assert_eq!(OpCode::istore_3, OpCode::from(62)); assert_eq!(OpCode::lstore_0, OpCode::from(63)); assert_eq!(OpCode::lstore_1, OpCode::from(64)); assert_eq!(OpCode::lstore_2, OpCode::from(65)); assert_eq!(OpCode::lstore_3, OpCode::from(66)); assert_eq!(OpCode::fstore_0, OpCode::from(67)); assert_eq!(OpCode::fstore_1, OpCode::from(68)); assert_eq!(OpCode::fstore_2, OpCode::from(69)); assert_eq!(OpCode::fstore_3, OpCode::from(70)); assert_eq!(OpCode::dstore_0, OpCode::from(71)); assert_eq!(OpCode::dstore_1, OpCode::from(72)); assert_eq!(OpCode::dstore_2, OpCode::from(73)); assert_eq!(OpCode::dstore_3, OpCode::from(74)); assert_eq!(OpCode::astore_0, OpCode::from(75)); assert_eq!(OpCode::astore_1, OpCode::from(76)); assert_eq!(OpCode::astore_2, OpCode::from(77)); assert_eq!(OpCode::astore_3, OpCode::from(78)); assert_eq!(OpCode::iastore, OpCode::from(79)); assert_eq!(OpCode::lastore, OpCode::from(80)); assert_eq!(OpCode::fastore, OpCode::from(81)); assert_eq!(OpCode::dastore, OpCode::from(82)); assert_eq!(OpCode::aastore, OpCode::from(83)); assert_eq!(OpCode::bastore, OpCode::from(84)); assert_eq!(OpCode::castore, OpCode::from(85)); assert_eq!(OpCode::sastore, OpCode::from(86)); assert_eq!(OpCode::pop, OpCode::from(87)); assert_eq!(OpCode::pop2, OpCode::from(88)); assert_eq!(OpCode::dup, OpCode::from(89)); assert_eq!(OpCode::dup_x1, OpCode::from(90)); assert_eq!(OpCode::dup_x2, OpCode::from(91)); assert_eq!(OpCode::dup2, OpCode::from(92)); assert_eq!(OpCode::dup2_x1, OpCode::from(93)); assert_eq!(OpCode::dup2_x2, OpCode::from(94)); assert_eq!(OpCode::swap, OpCode::from(95)); assert_eq!(OpCode::iadd, OpCode::from(96)); assert_eq!(OpCode::ladd, OpCode::from(97)); assert_eq!(OpCode::fadd, OpCode::from(98)); assert_eq!(OpCode::dadd, OpCode::from(99)); assert_eq!(OpCode::isub, OpCode::from(100)); assert_eq!(OpCode::lsub, OpCode::from(101)); assert_eq!(OpCode::fsub, OpCode::from(102)); assert_eq!(OpCode::dsub, OpCode::from(103)); assert_eq!(OpCode::imul, OpCode::from(104)); assert_eq!(OpCode::lmul, OpCode::from(105)); assert_eq!(OpCode::fmul, OpCode::from(106)); assert_eq!(OpCode::dmul, OpCode::from(107)); assert_eq!(OpCode::idiv, OpCode::from(108)); assert_eq!(OpCode::ldiv, OpCode::from(109)); assert_eq!(OpCode::fdiv, OpCode::from(110)); assert_eq!(OpCode::ddiv, OpCode::from(111)); assert_eq!(OpCode::irem, OpCode::from(112)); assert_eq!(OpCode::lrem, OpCode::from(113)); assert_eq!(OpCode::frem, OpCode::from(114)); assert_eq!(OpCode::drem, OpCode::from(115)); assert_eq!(OpCode::ineg, OpCode::from(116)); assert_eq!(OpCode::lneg, OpCode::from(117)); assert_eq!(OpCode::fneg, OpCode::from(118)); assert_eq!(OpCode::dneg, OpCode::from(119)); assert_eq!(OpCode::ishl, OpCode::from(120)); assert_eq!(OpCode::lshl, OpCode::from(121)); assert_eq!(OpCode::ishr, OpCode::from(122)); assert_eq!(OpCode::lshr, OpCode::from(123)); assert_eq!(OpCode::iushr, OpCode::from(124)); assert_eq!(OpCode::lushr, OpCode::from(125)); assert_eq!(OpCode::iand, OpCode::from(126)); assert_eq!(OpCode::land, OpCode::from(127)); assert_eq!(OpCode::ior, OpCode::from(128)); assert_eq!(OpCode::lor, OpCode::from(129)); assert_eq!(OpCode::ixor, OpCode::from(130)); assert_eq!(OpCode::lxor, OpCode::from(131)); assert_eq!(OpCode::iinc, OpCode::from(132)); assert_eq!(OpCode::i2l, OpCode::from(133)); assert_eq!(OpCode::i2f, OpCode::from(134)); assert_eq!(OpCode::i2d, OpCode::from(135)); assert_eq!(OpCode::l2i, OpCode::from(136)); assert_eq!(OpCode::l2f, OpCode::from(137)); assert_eq!(OpCode::l2d, OpCode::from(138)); assert_eq!(OpCode::f2i, OpCode::from(139)); assert_eq!(OpCode::f2l, OpCode::from(140)); assert_eq!(OpCode::f2d, OpCode::from(141)); assert_eq!(OpCode::d2i, OpCode::from(142)); assert_eq!(OpCode::d2l, OpCode::from(143)); assert_eq!(OpCode::d2f, OpCode::from(144)); assert_eq!(OpCode::i2b, OpCode::from(145)); assert_eq!(OpCode::i2c, OpCode::from(146)); assert_eq!(OpCode::i2s, OpCode::from(147)); assert_eq!(OpCode::lcmp, OpCode::from(148)); assert_eq!(OpCode::fcmpl, OpCode::from(149)); assert_eq!(OpCode::fcmpg, OpCode::from(150)); assert_eq!(OpCode::dcmpl, OpCode::from(151)); assert_eq!(OpCode::dcmpg, OpCode::from(152)); assert_eq!(OpCode::ifeq, OpCode::from(153)); assert_eq!(OpCode::ifne, OpCode::from(154)); assert_eq!(OpCode::iflt, OpCode::from(155)); assert_eq!(OpCode::ifge, OpCode::from(156)); assert_eq!(OpCode::ifgt, OpCode::from(157)); assert_eq!(OpCode::ifle, OpCode::from(158)); assert_eq!(OpCode::if_icmpeq, OpCode::from(159)); assert_eq!(OpCode::if_icmpne, OpCode::from(160)); assert_eq!(OpCode::if_icmplt, OpCode::from(161)); assert_eq!(OpCode::if_icmpge, OpCode::from(162)); assert_eq!(OpCode::if_icmpgt, OpCode::from(163)); assert_eq!(OpCode::if_icmple, OpCode::from(164)); assert_eq!(OpCode::if_acmpeq, OpCode::from(165)); assert_eq!(OpCode::if_acmpne, OpCode::from(166)); assert_eq!(OpCode::goto, OpCode::from(167)); assert_eq!(OpCode::jsr, OpCode::from(168)); assert_eq!(OpCode::ret, OpCode::from(169)); assert_eq!(OpCode::tableswitch, OpCode::from(170)); assert_eq!(OpCode::lookupswitch, OpCode::from(171)); assert_eq!(OpCode::ireturn, OpCode::from(172)); assert_eq!(OpCode::lreturn, OpCode::from(173)); assert_eq!(OpCode::freturn, OpCode::from(174)); assert_eq!(OpCode::dreturn, OpCode::from(175)); assert_eq!(OpCode::areturn, OpCode::from(176)); assert_eq!(OpCode::return_void, OpCode::from(177)); assert_eq!(OpCode::getstatic, OpCode::from(178)); assert_eq!(OpCode::putstatic, OpCode::from(179)); assert_eq!(OpCode::getfield, OpCode::from(180)); assert_eq!(OpCode::putfield, OpCode::from(181)); assert_eq!(OpCode::invokevirtual, OpCode::from(182)); assert_eq!(OpCode::invokespecial, OpCode::from(183)); assert_eq!(OpCode::invokestatic, OpCode::from(184)); assert_eq!(OpCode::invokeinterface, OpCode::from(185)); assert_eq!(OpCode::invokedynamic, OpCode::from(186)); assert_eq!(OpCode::new, OpCode::from(187)); assert_eq!(OpCode::newarray, OpCode::from(188)); assert_eq!(OpCode::anewarray, OpCode::from(189)); assert_eq!(OpCode::arraylength, OpCode::from(190)); assert_eq!(OpCode::athrow, OpCode::from(191)); assert_eq!(OpCode::checkcast, OpCode::from(192)); assert_eq!(OpCode::instanceof, OpCode::from(193)); assert_eq!(OpCode::monitorenter, OpCode::from(194)); assert_eq!(OpCode::monitorexit, OpCode::from(195)); assert_eq!(OpCode::wide, OpCode::from(196)); assert_eq!(OpCode::multianewarray, OpCode::from(197)); assert_eq!(OpCode::ifnull, OpCode::from(198)); assert_eq!(OpCode::ifnonnull, OpCode::from(199)); assert_eq!(OpCode::goto_w, OpCode::from(200)); assert_eq!(OpCode::jsr_w, OpCode::from(201)); assert_eq!(OpCode::breakpoint, OpCode::from(202)); // assert_eq!(OpCode::, OpCode::from(203)); // assert_eq!(OpCode::, OpCode::from(204)); // assert_eq!(OpCode::, OpCode::from(205)); // assert_eq!(OpCode::, OpCode::from(206)); // assert_eq!(OpCode::, OpCode::from(207)); // assert_eq!(OpCode::, OpCode::from(208)); // assert_eq!(OpCode::, OpCode::from(209)); // assert_eq!(OpCode::, OpCode::from(210)); // assert_eq!(OpCode::, OpCode::from(211)); // assert_eq!(OpCode::, OpCode::from(212)); // assert_eq!(OpCode::, OpCode::from(213)); // assert_eq!(OpCode::, OpCode::from(214)); // assert_eq!(OpCode::, OpCode::from(215)); // assert_eq!(OpCode::, OpCode::from(216)); // assert_eq!(OpCode::, OpCode::from(217)); // assert_eq!(OpCode::, OpCode::from(218)); // assert_eq!(OpCode::, OpCode::from(219)); // assert_eq!(OpCode::, OpCode::from(220)); // assert_eq!(OpCode::, OpCode::from(221)); // assert_eq!(OpCode::, OpCode::from(222)); // assert_eq!(OpCode::, OpCode::from(223)); // assert_eq!(OpCode::, OpCode::from(224)); // assert_eq!(OpCode::, OpCode::from(225)); // assert_eq!(OpCode::, OpCode::from(226)); // assert_eq!(OpCode::, OpCode::from(227)); // assert_eq!(OpCode::, OpCode::from(228)); // assert_eq!(OpCode::, OpCode::from(229)); // assert_eq!(OpCode::, OpCode::from(230)); // assert_eq!(OpCode::, OpCode::from(231)); // assert_eq!(OpCode::, OpCode::from(232)); // assert_eq!(OpCode::, OpCode::from(233)); // assert_eq!(OpCode::, OpCode::from(234)); // assert_eq!(OpCode::, OpCode::from(235)); // assert_eq!(OpCode::, OpCode::from(236)); // assert_eq!(OpCode::, OpCode::from(237)); // assert_eq!(OpCode::, OpCode::from(238)); // assert_eq!(OpCode::, OpCode::from(239)); // assert_eq!(OpCode::, OpCode::from(240)); // assert_eq!(OpCode::, OpCode::from(241)); // assert_eq!(OpCode::, OpCode::from(242)); // assert_eq!(OpCode::, OpCode::from(243)); // assert_eq!(OpCode::, OpCode::from(244)); // assert_eq!(OpCode::, OpCode::from(245)); // assert_eq!(OpCode::, OpCode::from(246)); // assert_eq!(OpCode::, OpCode::from(247)); // assert_eq!(OpCode::, OpCode::from(248)); // assert_eq!(OpCode::, OpCode::from(249)); // assert_eq!(OpCode::, OpCode::from(250)); // assert_eq!(OpCode::, OpCode::from(251)); // assert_eq!(OpCode::, OpCode::from(252)); // assert_eq!(OpCode::, OpCode::from(253)); assert_eq!(OpCode::impdep1, OpCode::from(254)); assert_eq!(OpCode::impdep2, OpCode::from(255)); // assert_eq!(OpCode::, OpCode::from(256)); } } ================================================ FILE: crates/classfile/src/signature.rs ================================================ use crate::BytesRef; use std::fmt::Formatter; #[derive(Clone, PartialEq)] pub enum Type { Byte, Char, Double, Float, Int, Long, //the 1st, container class //the 2nd, generic class's arg //the 3rd, if there is a '+' // Ljava/util/List;) // => java.util.List // Ljava/lang/Class<+Lcom/google/inject/Module;>; // => java.lang.Class Object(BytesRef, Option>, Option), Short, Boolean, Array(BytesRef), Void, } impl std::fmt::Debug for Type { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Type::Byte => write!(f, "B"), Type::Char => write!(f, "C"), Type::Double => write!(f, "D"), Type::Float => write!(f, "F"), Type::Int => write!(f, "I"), Type::Long => write!(f, "J"), Type::Object(container, args, prefix) => { write!(f, "Object("); write!(f, "\"{}\",", String::from_utf8_lossy(container.as_slice())); write!(f, "{:?},", args); write!(f, "{:?}", prefix); write!(f, ")") } Type::Short => write!(f, "S"), Type::Boolean => write!(f, "Z"), Type::Array(desc) => write!(f, "Array({})", String::from_utf8_lossy(desc.as_slice())), Type::Void => write!(f, "V"), } } } ================================================ FILE: crates/classfile/src/version.rs ================================================ use crate::U2; #[derive(Debug)] pub struct Version { pub minor: U2, pub major: U2, } ================================================ FILE: crates/vm/Cargo.toml ================================================ [package] name = "vm" version = "0.1.0" authors = ["Dou Chuan <1843657913@qq.com>"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] chrono = "0.4" classfile = { path = "../classfile", version = "0.1.0" } class-parser = { path="../class-parser", version="0.1.0" } dirs = "3.0.1" lazy_static = "1.4.0" libc = "0.2.85" log = "0.4" nix = "0.19.1" rustc-hash = "1.1.0" zip = "0.5.9" ================================================ FILE: crates/vm/README.md ================================================ ## Java Virtual Machine This is the `vm` crate, which contains implementation of Java Virtual Machine. The implementation based on `Java SE 8 Edition`. ================================================ FILE: crates/vm/src/lib.rs ================================================ #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; //contains macros, must be here #[macro_use] pub mod util; pub mod native; pub mod oop; pub mod runtime; pub mod types; pub fn init_vm() { oop::init(); runtime::init(); native::init(); } #[inline] pub fn new_br(s: &str) -> classfile::BytesRef { std::sync::Arc::new(Vec::from(s)) } ================================================ FILE: crates/vm/src/native/common/check_format.rs ================================================ // pub fn is_valid_class_name(s: &String) -> bool { // if s.contains("/") { // return false; // } // // true // } ================================================ FILE: crates/vm/src/native/common/mod.rs ================================================ mod check_format; pub mod reflect; ================================================ FILE: crates/vm/src/native/common/reflect.rs ================================================ #![allow(non_snake_case)] use crate::native::java_lang_Class; use crate::oop::{self, Class, Oop, OopPtr}; use crate::runtime::{self, require_class3}; use crate::types::*; use crate::util; use class_parser::{FieldSignature, MethodSignature}; use classfile::consts as cls_const; use classfile::SignatureType; use std::sync::Arc; pub fn new_field(fir: FieldIdRef) -> Oop { let field_cls = runtime::require_class3(None, cls_const::J_FIELD).unwrap(); let clazz = fir.field.class.get_class().get_mirror(); let field_sig = FieldSignature::new(fir.field.desc.as_slice()); let typ_mirror = create_value_type(field_sig.field_type); let desc = unsafe { std::str::from_utf8_unchecked(fir.field.desc.as_slice()) }; let signature = util::oop::new_java_lang_string2(desc); let field_name = unsafe { std::str::from_utf8_unchecked(fir.field.name.as_slice()) }; let mut desc = Vec::new(); desc.push(b'('); let mut args: Vec = vec![ ("clazz", "Ljava/lang/Class;", clazz), ( "name", "Ljava/lang/String;", util::oop::new_java_lang_string2(field_name), ), ("type", "Ljava/lang/Class;", typ_mirror), ("modifiers", "I", Oop::new_int(fir.field.acc_flags as i32)), ("slot", "I", Oop::new_int(fir.offset as i32)), ("signature", "Ljava/lang/String;", signature), ("annotations", "[B", Oop::Null), ] .iter() .map(|(_, t, v)| { desc.extend_from_slice(t.as_bytes()); v.clone() }) .collect(); desc.extend_from_slice(b")V"); let oop = Oop::new_inst(field_cls.clone()); args.insert(0, oop.clone()); runtime::invoke::invoke_ctor(field_cls, Arc::new(desc), args); oop } pub fn new_method_ctor(mir: MethodIdRef) -> Oop { let ctor_cls = require_class3(None, cls_const::J_METHOD_CTOR).unwrap(); //declaringClass let declaring_cls = mir.method.class.get_class().get_mirror(); //parameterTypes let signature = MethodSignature::new(mir.method.desc.as_slice()); let params: Vec = signature .args .iter() .map(|t| create_value_type(t.clone())) .collect(); let cls = require_class3(None, b"[Ljava/lang/Class;").unwrap(); let parameter_types = Oop::new_ref_ary2(cls, params); //fixme: checkedExceptions let cls = require_class3(None, b"[Ljava/lang/Class;").unwrap(); let checked_exceptions = Oop::new_ref_ary2(cls, vec![]); //modifiers let modifiers = mir.method.acc_flags; //slot let slot = mir.offset; //signature let desc = unsafe { std::str::from_utf8_unchecked(mir.method.desc.as_slice()) }; let signature = util::oop::new_java_lang_string2(desc); let annotations = { let raw = mir.method.get_annotation(); match raw { Some(raw) => Oop::new_byte_ary2(raw.to_vec()), None => Oop::Null, } }; let parameter_annotations = { let raw = mir.method.get_param_annotation(); match raw { Some(raw) => Oop::new_byte_ary2(raw.to_vec()), None => Oop::Null, } }; let mut desc = Vec::new(); desc.push(b'('); let mut args: Vec = vec![ ("declaringClass", "Ljava/lang/Class;", declaring_cls), ("parameterTypes", "[Ljava/lang/Class;", parameter_types), ( "checkedExceptions", "[Ljava/lang/Class;", checked_exceptions, ), ("modifiers", "I", Oop::new_int(modifiers as i32)), ("slot", "I", Oop::new_int(slot as i32)), ("signature", "Ljava/lang/String;", signature), ("annotations", "[B", annotations), ("parameterAnnotations", "[B", parameter_annotations), ] .iter() .map(|(_, t, v)| { desc.extend_from_slice(t.as_bytes()); v.clone() }) .collect(); desc.extend_from_slice(b")V"); let oop = Oop::new_inst(ctor_cls.clone()); args.insert(0, oop.clone()); runtime::invoke::invoke_ctor(ctor_cls, Arc::new(desc), args); oop } pub fn new_method_normal(mir: MethodIdRef) -> Oop { let ctor_cls = require_class3(None, cls_const::J_METHOD).unwrap(); //declaringClass let declaring_cls = mir.method.class.get_class().get_mirror(); //name let name = { let name = unsafe { std::str::from_utf8_unchecked(mir.method.name.as_slice()) }; util::oop::new_java_lang_string2(name) }; //parameterTypes let signature = MethodSignature::new(mir.method.desc.as_slice()); let params: Vec = signature .args .iter() .map(|t| create_value_type(t.clone())) .collect(); let cls = require_class3(None, b"[Ljava/lang/Class;").unwrap(); let parameter_types = Oop::new_ref_ary2(cls, params); //returnType let return_type = create_value_type(signature.retype); //fixme: checkedExceptions let cls = require_class3(None, b"[Ljava/lang/Class;").unwrap(); let checked_exceptions = Oop::new_ref_ary2(cls, vec![]); //modifiers let modifiers = mir.method.acc_flags; //slot let slot = mir.offset; //signature let signature = { let desc = unsafe { std::str::from_utf8_unchecked(mir.method.desc.as_slice()) }; util::oop::new_java_lang_string2(desc) }; let annotations = { let raw = mir.method.get_annotation(); match raw { Some(raw) => Oop::new_byte_ary2(raw.to_vec()), None => Oop::Null, } }; let parameter_annotations = { let raw = mir.method.get_param_annotation(); match raw { Some(raw) => Oop::new_byte_ary2(raw.to_vec()), None => Oop::Null, } }; let annotation_default = { let raw = mir.method.get_annotation_default(); match raw { Some(raw) => Oop::new_byte_ary2(raw.to_vec()), None => Oop::Null, } }; let mut desc = Vec::new(); desc.push(b'('); let mut args: Vec = vec![ ("declaringClass", "Ljava/lang/Class;", declaring_cls), ("name", "Ljava/lang/String;", name), ("parameterTypes", "[Ljava/lang/Class;", parameter_types), ("returnType", "Ljava/lang/Class;", return_type), ( "checkedExceptions", "[Ljava/lang/Class;", checked_exceptions, ), ("modifiers", "I", Oop::new_int(modifiers as i32)), ("slot", "I", Oop::new_int(slot as i32)), ("signature", "Ljava/lang/String;", signature), ("annotations", "[B", annotations), ("parameterAnnotations", "[B", parameter_annotations), ("annotationDefault", "[B", annotation_default), ] .iter() .map(|(_, t, v)| { desc.extend_from_slice(t.as_bytes()); v.clone() }) .collect(); desc.extend_from_slice(b")V"); let oop = Oop::new_inst(ctor_cls.clone()); args.insert(0, oop.clone()); runtime::invoke::invoke_ctor(ctor_cls, Arc::new(desc), args); oop } pub fn get_Constructor_clazz(ctor: &Oop) -> Oop { //todo: optimize, avoid obtain class let cls = { let rf = ctor.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; //todo: optimize, avoid obtain id let cls = cls.get_class(); let id = cls.get_field_id(&util::S_CLAZZ, &util::S_JAVA_LANG_CLASS, false); Class::get_field_value(ctor.extract_ref(), id) } /* pub fn get_Constructor_slot(ctor: &Oop) -> i32 { let cls = { let v = util::oop::extract_ref(ctor); let v = v.read().unwrap(); match &v.v { oop::RefKind::Inst(inst) => inst.class.clone(), _ => unreachable!(), } }; let cls = cls.read().unwrap(); let id = cls.get_field_id(b"slot", b"I", false); let v = cls.get_field_value(ctor, id); util::oop::extract_int(&v) } */ pub fn get_Constructor_signature(ctor: &Oop) -> String { //todo: optimisze, cache Constructor cls, avoid obtain class let cls = { let rf = ctor.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; //todo: optimize, cache id let cls = cls.get_class(); let id = cls.get_field_id(&util::S_SIGNATURE, &util::S_JAVA_LANG_STRING, false); let v = Class::get_field_value(ctor.extract_ref(), id); OopPtr::java_lang_string(v.extract_ref()) } fn create_value_type(t: SignatureType) -> Oop { match t { SignatureType::Byte => java_lang_Class::get_primitive_class_mirror("B").unwrap(), SignatureType::Char => java_lang_Class::get_primitive_class_mirror("C").unwrap(), SignatureType::Int => java_lang_Class::get_primitive_class_mirror("I").unwrap(), SignatureType::Double => java_lang_Class::get_primitive_class_mirror("D").unwrap(), SignatureType::Float => java_lang_Class::get_primitive_class_mirror("F").unwrap(), SignatureType::Long => java_lang_Class::get_primitive_class_mirror("J").unwrap(), SignatureType::Object(desc, _, _) => { let len = desc.len(); let name = &desc.as_slice()[1..len - 1]; let cls = require_class3(None, name).unwrap(); let cls = cls.get_class(); cls.get_mirror() } SignatureType::Short => java_lang_Class::get_primitive_class_mirror("S").unwrap(), SignatureType::Boolean => java_lang_Class::get_primitive_class_mirror("Z").unwrap(), SignatureType::Array(desc) => { let cls = require_class3(None, desc.as_slice()).unwrap(); let cls = cls.get_class(); cls.get_mirror() } SignatureType::Void => java_lang_Class::get_primitive_class_mirror("V").unwrap(), } } ================================================ FILE: crates/vm/src/native/java_io_FileDescriptor.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![new_fn("initIDs", "()V", Box::new(jvm_initIDs))] } fn jvm_initIDs(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } ================================================ FILE: crates/vm/src/native/java_io_FileInputStream.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{Class, Oop, OopPtr}; use crate::runtime::{self, require_class3}; use crate::util; use classfile::consts as cls_consts; static mut FILE_INPUT_STREAM_FD: usize = 0; static mut FILE_DESCRIPTOR_FD: usize = 0; pub fn get_native_methods() -> Vec { vec![ new_fn("initIDs", "()V", Box::new(jvm_initIDs)), new_fn("open0", "(Ljava/lang/String;)V", Box::new(jvm_open0)), new_fn("readBytes", "([BII)I", Box::new(jvm_readBytes)), //available0 used by zulu8 jdk new_fn("available0", "()I", Box::new(jvm_available0)), new_fn("available", "()I", Box::new(jvm_available0)), new_fn("close0", "()V", Box::new(jvm_close0)), ] } fn jvm_initIDs(_env: JNIEnv, _args: &[Oop]) -> JNIResult { //setup: java.io.FileInputStream fd let cls = require_class3(None, b"java/io/FileInputStream").unwrap(); let cls = cls.get_class(); let id = cls.get_field_id(&util::S_FD, &util::S_JAVA_IO_FD, false); unsafe { FILE_INPUT_STREAM_FD = id.offset; } //setup: java.io.FileDescriptor fd let cls = require_class3(None, b"java/io/FileDescriptor").unwrap(); let cls = cls.get_class(); let id = cls.get_field_id(&util::S_FD, &util::S_I, false); unsafe { FILE_DESCRIPTOR_FD = id.offset; } Ok(None) } fn jvm_open0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let name = { let v = args.get(1).unwrap(); OopPtr::java_lang_string(v.extract_ref()) }; let fd = unsafe { use std::ffi::CString; let name = CString::new(name).unwrap(); libc::open(name.as_ptr(), libc::O_RDONLY) }; set_file_descriptor_fd(this, fd); Ok(None) } fn jvm_readBytes(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let fd = get_file_descriptor_fd(this); let byte_ary = args.get(1).unwrap(); let off = args.get(2).unwrap().extract_int(); let len = args.get(3).unwrap().extract_int(); let n = { let rf = byte_ary.extract_ref(); let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_bytes(); let (_, ptr) = ary.split_at_mut(off as usize); let ptr = ptr.as_mut_ptr() as *mut libc::c_void; let n = unsafe { libc::read(fd, ptr, len as usize) }; // error!("readBytes n = {}", n); if n > 0 { n as i32 } else if n == -1 { let ex = runtime::exception::new( cls_consts::J_IOEXCEPTION, Some(String::from("Read Error")), ); error!("jvm_readBytes read error"); return Err(ex); } else { -1 } }; Ok(Some(Oop::new_int(n))) } fn jvm_available0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let fd = get_file_descriptor_fd(this); if fd == -1 { unimplemented!("Stream Closed"); } let mut size = -1i64; unsafe { let mut stat: libc::stat = std::mem::zeroed(); if libc::fstat(fd, &mut stat) != -1 { let mode = stat.st_mode; if (mode & libc::S_IFIFO == libc::S_IFIFO) || (mode & libc::S_IFCHR == libc::S_IFCHR) || (mode & libc::S_IFSOCK == libc::S_IFSOCK) { let mut n = 0; if libc::ioctl(fd, libc::FIONREAD, &mut n) >= 0 { return Ok(Some(Oop::new_int(n))); } } else if mode & libc::S_IFREG == libc::S_IFREG { size = stat.st_size; } } let current = libc::lseek(fd, 0, libc::SEEK_CUR); if current == -1 { return Ok(Some(Oop::new_int(0))); } if size < current { size = libc::lseek(fd, 0, libc::SEEK_END); if size == -1 { return Ok(Some(Oop::new_int(0))); } if libc::lseek(fd, current, libc::SEEK_SET) == -1 { return Ok(Some(Oop::new_int(0))); } } Ok(Some(Oop::new_int((size - current) as i32))) } } fn jvm_close0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let fd = get_file_descriptor_fd(this); unsafe { libc::close(fd); } Ok(None) } fn set_file_descriptor_fd(fin: &Oop, fd: i32) { let offset = unsafe { FILE_INPUT_STREAM_FD }; let fd_this = Class::get_field_value2(fin.extract_ref(), offset); let offset = unsafe { FILE_DESCRIPTOR_FD }; Class::put_field_value2(fd_this.extract_ref(), offset, Oop::new_int(fd)); } fn get_file_descriptor_fd(fin: &Oop) -> i32 { let offset = unsafe { FILE_INPUT_STREAM_FD }; let fd_this = Class::get_field_value2(fin.extract_ref(), offset); let offset = unsafe { FILE_DESCRIPTOR_FD }; let fd = Class::get_field_value2(fd_this.extract_ref(), offset); fd.extract_int() } ================================================ FILE: crates/vm/src/native/java_io_FileOutputStream.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{Class, Oop, OopPtr}; use crate::runtime::require_class3; use crate::util; static mut FILE_OUTPUT_STREAM_FD: usize = 0; static mut FILE_DESCRIPTOR_FD: usize = 0; pub fn get_native_methods() -> Vec { vec![ new_fn("initIDs", "()V", Box::new(jvm_initIDs)), new_fn("writeBytes", "([BIIZ)V", Box::new(jvm_writeBytes)), new_fn("open0", "(Ljava/lang/String;Z)V", Box::new(jvm_open0)), ] } fn jvm_initIDs(_env: JNIEnv, _args: &[Oop]) -> JNIResult { //setup: java.io.FileOutputStream fd let cls = require_class3(None, b"java/io/FileOutputStream").unwrap(); let cls = cls.get_class(); let id = cls.get_field_id(&util::S_FD, &util::S_JAVA_IO_FD, false); unsafe { FILE_OUTPUT_STREAM_FD = id.offset; } //setup: java.io.FileDescriptor fd let cls = require_class3(None, b"java/io/FileDescriptor").unwrap(); let cls = cls.get_class(); let id = cls.get_field_id(&util::S_FD, &util::S_I, false); unsafe { FILE_DESCRIPTOR_FD = id.offset; } Ok(None) } fn jvm_writeBytes(_env: JNIEnv, args: &[Oop]) -> JNIResult { let os = args.get(0).unwrap(); let fd = get_file_descriptor_fd(os); let byte_ary = args.get(1).unwrap(); let off = args.get(2).unwrap().extract_int(); let len = args.get(3).unwrap().extract_int(); let append = args.get(4).unwrap().extract_int(); let rf = byte_ary.extract_ref(); let ary = rf.extract_type_array(); let ary = ary.extract_bytes(); let (_, ary) = ary.split_at(off as usize); let len = len as usize; unsafe { if append == 1 { libc::lseek(fd, 0, libc::SEEK_END); } if -1 == libc::write(fd, ary.as_ptr() as *const libc::c_void, len) { panic!("write failed"); } } Ok(None) } fn jvm_open0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let name = args.get(1).unwrap(); let name = OopPtr::java_lang_string(name.extract_ref()); let append = { let v = args.get(2).unwrap().extract_int(); v == 1 }; let fd = unsafe { use std::ffi::CString; let name = CString::new(name).unwrap(); let mut flag = libc::O_WRONLY | libc::O_CREAT; if append { flag |= libc::O_APPEND; } else { flag |= libc::O_TRUNC; } libc::open(name.as_ptr(), flag) }; set_file_descriptor_fd(this, fd); Ok(None) } fn get_file_descriptor_fd(fos: &Oop) -> i32 { let fd_this = { let offset = unsafe { FILE_OUTPUT_STREAM_FD }; Class::get_field_value2(fos.extract_ref(), offset) }; let fd = { let offset = unsafe { FILE_DESCRIPTOR_FD }; Class::get_field_value2(fd_this.extract_ref(), offset) }; fd.extract_int() } fn set_file_descriptor_fd(fos: &Oop, fd: i32) { let fd_this = { let offset = unsafe { FILE_OUTPUT_STREAM_FD }; Class::get_field_value2(fos.extract_ref(), offset) }; let offset = unsafe { FILE_DESCRIPTOR_FD }; Class::put_field_value2(fd_this.extract_ref(), offset, Oop::new_int(fd)); } ================================================ FILE: crates/vm/src/native/java_io_UnixFileSystem.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{Class, Oop, OopPtr}; use crate::runtime::require_class3; use crate::{new_br, util}; use std::fs; static mut FILE_PATH: usize = 0; //FileSystem.java define const BA_EXISTS: i32 = 0x01; const BA_REGULAR: i32 = 0x02; const BA_DIRECTORY: i32 = 0x04; const _BA_HIDDEN: i32 = 0x08; const ACCESS_READ: i32 = 0x04; const ACCESS_WRITE: i32 = 0x02; const ACCESS_EXECUTE: i32 = 0x01; pub fn get_native_methods() -> Vec { vec![ new_fn("initIDs", "()V", Box::new(jvm_initIDs)), new_fn( "getBooleanAttributes0", "(Ljava/io/File;)I", Box::new(jvm_getBooleanAttributes0), ), new_fn( "checkAccess", "(Ljava/io/File;I)Z", Box::new(jvm_checkAccess), ), new_fn( "canonicalize0", "(Ljava/lang/String;)Ljava/lang/String;", Box::new(jvm_canonicalize0), ), new_fn( "createFileExclusively", "(Ljava/lang/String;)Z", Box::new(jvm_createFileExclusively), ), ] } fn jvm_initIDs(_env: JNIEnv, _args: &[Oop]) -> JNIResult { let cls = require_class3(None, b"java/io/File").unwrap(); let cls = cls.get_class(); let fir = cls.get_field_id(&new_br("path"), &util::S_JAVA_LANG_STRING, false); unsafe { FILE_PATH = fir.offset; } Ok(None) } fn jvm_getBooleanAttributes0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let file = args.get(1).unwrap(); let path = get_File_path(file); let mut r = 0; if let Ok(attr) = fs::metadata(path) { r |= BA_EXISTS; if attr.is_file() { r |= BA_REGULAR; } if attr.is_dir() { r |= BA_DIRECTORY; } } Ok(Some(Oop::new_int(r))) } fn jvm_checkAccess(_env: JNIEnv, args: &[Oop]) -> JNIResult { let file = args.get(1).unwrap(); let path = get_File_path(file); let access = args.get(2).unwrap().extract_int(); let mut amode = 0; if (access & ACCESS_READ) == ACCESS_READ { amode |= libc::R_OK; } if (access & ACCESS_WRITE) == ACCESS_WRITE { amode |= libc::W_OK; } if (access & ACCESS_EXECUTE) == ACCESS_EXECUTE { amode |= libc::X_OK; } let r = unsafe { use std::ffi::CString; let path = CString::new(path).unwrap(); if libc::access(path.as_ptr(), amode) == 0 { 1 } else { 0 } }; Ok(Some(Oop::new_int(r))) } fn jvm_canonicalize0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let path = args.get(1).unwrap(); let path = OopPtr::java_lang_string(path.extract_ref()); let path = std::path::Path::new(&path); let path = path.canonicalize().expect("path canonicalize failed"); let path = path.to_str().expect("path to_str failed"); let path = util::oop::new_java_lang_string2(path); Ok(Some(path)) } fn jvm_createFileExclusively(_env: JNIEnv, args: &[Oop]) -> JNIResult { let path = args.get(1).unwrap(); let path = OopPtr::java_lang_string(path.extract_ref()); let v = match std::fs::OpenOptions::new() .read(true) .write(true) .create(true) .open(&path) { Ok(_) => 1, Err(e) => { error!("open {}, error = {:?}", path, e); 0 } }; Ok(Some(Oop::new_int(v))) } fn get_File_path(file: &Oop) -> String { let offset = unsafe { FILE_PATH }; let path = Class::get_field_value2(file.extract_ref(), offset); OopPtr::java_lang_string(path.extract_ref()) } ================================================ FILE: crates/vm/src/native/java_lang_Class.rs ================================================ #![allow(non_snake_case)] use crate::native::{common, new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Class, ClassKind, Oop, OopPtr, ValueType}; use crate::runtime::{self, require_class2, require_class3}; use crate::types::{ClassRef, MethodIdRef}; use crate::util; use classfile::{constant_pool, consts as cls_consts, flags as acc}; use rustc_hash::FxHashMap; use std::sync::{Arc, RwLock}; pub fn get_primitive_class_mirror(key: &str) -> Option { //todo: avoid mutex lock, it's only read let mirrors = PRIM_MIRROS.read().unwrap(); mirrors.get(key).cloned() } pub fn get_native_methods() -> Vec { vec![ new_fn("registerNatives", "()V", Box::new(jvm_registerNatives)), new_fn( "desiredAssertionStatus0", "(Ljava/lang/Class;)Z", Box::new(jvm_desiredAssertionStatus0), ), new_fn( "getPrimitiveClass", "(Ljava/lang/String;)Ljava/lang/Class;", Box::new(jvm_getPrimitiveClass), ), new_fn( "getDeclaredFields0", "(Z)[Ljava/lang/reflect/Field;", Box::new(jvm_getDeclaredFields0), ), new_fn("getName0", "()Ljava/lang/String;", Box::new(jvm_getName0)), new_fn( "forName0", "(Ljava/lang/String;ZLjava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;", Box::new(jvm_forName0), ), new_fn("isPrimitive", "()Z", Box::new(jvm_isPrimitive)), new_fn( "isAssignableFrom", "(Ljava/lang/Class;)Z", Box::new(jvm_isAssignableFrom), ), new_fn("isInterface", "()Z", Box::new(jvm_isInterface)), new_fn( "getDeclaredConstructors0", "(Z)[Ljava/lang/reflect/Constructor;", Box::new(jvm_getDeclaredConstructors0), ), new_fn("getModifiers", "()I", Box::new(jvm_getModifiers)), new_fn( "getSuperclass", "()Ljava/lang/Class;", Box::new(jvm_getSuperclass), ), new_fn("isArray", "()Z", Box::new(jvm_isArray)), new_fn( "getComponentType", "()Ljava/lang/Class;", Box::new(jvm_getComponentType), ), new_fn( "getEnclosingMethod0", "()[Ljava/lang/Object;", Box::new(jvm_getEnclosingMethod0), ), new_fn( "getDeclaringClass0", "()Ljava/lang/Class;", Box::new(jvm_getDeclaringClass0), ), new_fn( "isInstance", "(Ljava/lang/Object;)Z", Box::new(jvm_isInstance), ), new_fn( "getDeclaredMethods0", "(Z)[Ljava/lang/reflect/Method;", Box::new(jvm_getDeclaredMethods0), ), new_fn( "getInterfaces0", "()[Ljava/lang/Class;", Box::new(jvm_getInterfaces0), ), new_fn("getRawAnnotations", "()[B", Box::new(jvm_getRawAnnotations)), new_fn( "getConstantPool", "()Lsun/reflect/ConstantPool;", Box::new(jvm_getConstantPool), ), new_fn( "getDeclaredClasses0", "()[Ljava/lang/Class;", Box::new(jvm_getDeclaredClasses0), ), new_fn( "getGenericSignature0", "()Ljava/lang/String;", Box::new(jvm_getGenericSignature0), ), ] } #[derive(Copy, Clone, PartialEq)] enum ClassMirrorState { NotFixed, Fixed, } lazy_static! { static ref MIRROR_STATE: RwLock = RwLock::new(ClassMirrorState::NotFixed); static ref PRIM_MIRROS: RwLock> = { let hm = FxHashMap::default(); RwLock::new(hm) }; static ref SIGNATURE_DIC: FxHashMap<&'static str, &'static str> = { let dic: FxHashMap<&'static str, &'static str> = [ ("byte", "B"), ("boolean", "Z"), ("char", "C"), ("short", "S"), ("int", "I"), ("float", "F"), ("long", "J"), ("double", "D"), ("void", "V"), ] .iter() .cloned() .collect(); dic }; static ref DELAYED_MIRROS: RwLock> = { let v = vec![ "I", "Z", "B", "C", "S", "F", "J", "D", "V", "[I", "[Z", "[B", "[C", "[S", "[F", "[J", "[D", ]; let v: Vec = v.iter().map(|it| it.to_string()).collect(); RwLock::new(v) }; static ref DELAYED_ARY_MIRROS: RwLock> = { let v = vec![]; RwLock::new(v) }; } pub fn init() { lazy_static::initialize(&MIRROR_STATE); lazy_static::initialize(&SIGNATURE_DIC); lazy_static::initialize(&PRIM_MIRROS); lazy_static::initialize(&DELAYED_MIRROS); lazy_static::initialize(&DELAYED_ARY_MIRROS); } pub fn create_mirror(cls: ClassRef) { let is_fixed = { let s = MIRROR_STATE.write().unwrap(); *s == ClassMirrorState::Fixed }; if is_fixed { let mirror = Oop::new_mirror(cls.clone()); let cls = cls.get_mut_class(); trace!("mirror created: {}", unsafe { std::str::from_utf8_unchecked(cls.name.as_slice()) }); cls.set_mirror(mirror); } else { let cls_back = cls.clone(); let cls = cls.get_class(); let name = unsafe { std::str::from_utf8_unchecked(cls.name.as_slice()) }; warn!("mirror create delayed: {}", name); match cls.kind { oop::class::ClassKind::Instance(_) => { let mut mirrors = DELAYED_MIRROS.write().unwrap(); mirrors.push(String::from(name)); } _ => { let mut mirrors = DELAYED_ARY_MIRROS.write().unwrap(); mirrors.push(cls_back); } } } } /* called after 'java/lang/Class' inited in init_vm.rs */ pub fn create_delayed_mirrors() { let names: Vec = { let mirros = DELAYED_MIRROS.read().unwrap(); mirros.clone() }; { let mut s = MIRROR_STATE.write().unwrap(); *s = ClassMirrorState::Fixed; } for name in names { if name.len() > 2 { //java.lang.XXX let target = require_class3(None, name.as_bytes()).unwrap(); create_mirror(target); } else { let is_prim_ary = name.as_bytes()[0] == b'['; let (vt, target) = if is_prim_ary { let vt = ValueType::from(&name.as_bytes()[1]); let target = require_class3(None, name.as_bytes()).unwrap(); (vt, Some(target)) } else { (ValueType::from(&name.as_bytes()[0]), None) }; let mirror = Oop::new_prim_mirror(vt, target.clone()); if is_prim_ary { let target = target.unwrap(); let cls = target.get_mut_class(); // warn!("set_mirror name={}", String::from_utf8_lossy(cls.name.as_slice())); cls.set_mirror(mirror.clone()); } let mut mirrors = PRIM_MIRROS.write().unwrap(); mirrors.insert(name.to_string(), mirror); } } } /* called after 'java/lang/Class' inited in init_vm.rs */ pub fn create_delayed_ary_mirrors() { let classes: Vec = { let mirros = DELAYED_ARY_MIRROS.read().unwrap(); mirros.clone() }; for cls in classes { let value_type = { let cls = cls.get_class(); match &cls.kind { oop::class::ClassKind::ObjectArray(obj_ary) => obj_ary.value_type, oop::class::ClassKind::TypeArray(typ_ary) => typ_ary.value_type, _ => unreachable!(), } }; let mirror = Oop::new_ary_mirror(cls.clone(), value_type); let cls = cls.get_mut_class(); cls.set_mirror(mirror); } } fn jvm_registerNatives(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } fn jvm_desiredAssertionStatus0(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(0))) } fn jvm_getPrimitiveClass(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let v = OopPtr::java_lang_string(v.extract_ref()); match SIGNATURE_DIC.get(v.as_str()) { Some(&s) => Ok(get_primitive_class_mirror(s)), _ => unreachable!("Unknown primitive type: {}", v), } } fn jvm_getDeclaredFields0(_env: JNIEnv, args: &[Oop]) -> JNIResult { //parse args let mirror_target = { let arg0 = args.get(0).unwrap(); extract_mirror_target(arg0) }; let public_only = { let arg1 = args.get(1).unwrap(); arg1.extract_int() == 1 }; //fixme: super fields //obtain inst&static fields let (inst_fields, static_fields) = { let inst = mirror_target.extract_inst(); (inst.inst_fields.clone(), inst.static_fields.clone()) }; //build fields ary let mut fields = Vec::new(); for (_, it) in inst_fields { if public_only && !it.field.is_public() { continue; } let v = common::reflect::new_field(it); fields.push(v); } for (_, it) in static_fields { if public_only && !it.field.is_public() { continue; } let v = common::reflect::new_field(it); fields.push(v); } //build oop field ar let ary_cls = require_class3(None, b"[Ljava/lang/reflect/Field;").unwrap(); Ok(Some(Oop::new_ref_ary2(ary_cls, fields))) } fn jvm_getName0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let (target, vt) = { let arg0 = args.get(0).unwrap(); let rf = arg0.extract_ref(); let mirror = rf.extract_mirror(); (mirror.target.clone(), mirror.value_type) }; let name = { match target { Some(target) => { let cls = target.get_class(); Vec::from(cls.name.as_slice()) } None => { let v = vt.get_primitive_name(); Vec::from(v) } } }; let name = unsafe { std::str::from_utf8_unchecked(name.as_slice()) }; let name = name.replace("/", "."); let v = util::oop::new_java_lang_string2(&name); Ok(Some(v)) } fn jvm_forName0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let java_name = { let rf = arg0.extract_ref(); OopPtr::java_lang_string(rf) }; let initialize = { let arg1 = args.get(1).unwrap(); arg1.extract_int() != 0 }; let java_cls_loader = args.get(2).unwrap(); { match java_cls_loader { Oop::Null => (), _ => unimplemented!("app class loader, unimpl"), } } let _caller_mirror = args.get(3).unwrap(); if java_name.contains('/') { let msg = Some(java_name); let ex = runtime::exception::new(cls_consts::J_CLASS_NOT_FOUND, msg); return Err(ex); } let java_name = java_name.replace(".", "/"); let cls = { if java_name == "sun/nio/cs/ext/ExtendedCharsets" { //fixme: skip, cause jvm start very slow None } else { require_class3(None, java_name.as_bytes()) } }; match cls { Some(cls) => { oop::class::init_class(&cls); if initialize { oop::class::init_class_fully(&cls); } let mirror = cls.get_class().get_mirror(); Ok(Some(mirror)) } None => { // error!("forName0, NotFound: {}", java_name); let msg = Some(java_name); let ex = runtime::exception::new(cls_consts::J_CLASS_NOT_FOUND, msg); Err(ex) } } } fn jvm_isPrimitive(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let v = { let rf = v.extract_ref(); let mirror = rf.extract_mirror(); if mirror.target.is_none() { 1 } else { 0 } }; Ok(Some(Oop::new_int(v))) } fn jvm_isAssignableFrom(_env: JNIEnv, args: &[Oop]) -> JNIResult { let l = args.get(0).unwrap(); let r = args.get(1).unwrap(); let (lt, ltyp) = { let rf = l.extract_ref(); let mirror = rf.extract_mirror(); (mirror.target.clone(), mirror.value_type) }; let (rt, rtyp) = { let rf = r.extract_ref(); let mirror = rf.extract_mirror(); (mirror.target.clone(), mirror.value_type) }; let v = if lt.is_none() && rt.is_none() { if ltyp == rtyp { 1 } else { 0 } } else { let lt = lt.unwrap(); let rt = rt.unwrap(); if runtime::cmp::instance_of(rt, lt) { 1 } else { 0 } }; Ok(Some(Oop::new_int(v))) } fn jvm_isInterface(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let v = { let rf = v.extract_ref(); let mirror = rf.extract_mirror(); match &mirror.target { Some(target) => { if target.get_class().is_interface() { 1 } else { 0 } } None => 0, } }; Ok(Some(Oop::new_int(v))) } fn jvm_getDeclaredConstructors0(_env: JNIEnv, args: &[Oop]) -> JNIResult { //parse args let mirror_target = { let arg0 = args.get(0).unwrap(); extract_mirror_target(arg0) }; let public_only = args.get(1).unwrap().extract_int() == 1; get_declared_method_helper(mirror_target, public_only, true) } pub fn jvm_getModifiers(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let v = { let rf = v.extract_ref(); let mirror = rf.extract_mirror(); match &mirror.target { Some(target) => { let mut acc_flags = target.get_class().acc_flags; //use access_flags in InnerClasses Attribute //don't know why. just read the JDK codes let inst = target.extract_inst(); let this_class = inst.class_file.this_class; match &inst.inner_classes { Some(inner_classes) => { for it in inner_classes { if it.inner_class_info_index == 0 { continue; } if it.inner_class_info_index == this_class { acc_flags = it.inner_class_access_flags; } } } None => {} } acc_flags } None => acc::ACC_ABSTRACT | acc::ACC_FINAL | acc::ACC_PUBLIC, } }; Ok(Some(Oop::new_int(v as i32))) } fn jvm_getSuperclass(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let rf = arg0.extract_ref(); let mirror = rf.extract_mirror(); match &mirror.target { Some(target) => { let cls = target.get_class(); match &cls.super_class { Some(super_cls) => { let cls = super_cls.get_class(); let mirror = cls.get_mirror(); Ok(Some(mirror)) } None => Ok(Some(Oop::Null)), } } None => Ok(Some(Oop::Null)), } } fn jvm_isArray(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let mirror_cls = { let rf = arg0.extract_ref(); let mirror = rf.extract_mirror(); match &mirror.target { Some(target) => target.clone(), None => return Ok(Some(Oop::new_int(0))), } }; let cls = mirror_cls.get_class(); let v = match cls.get_class_kind_type() { oop::class::ClassKindType::Instance => 0, _ => 1, }; Ok(Some(Oop::new_int(v))) } fn jvm_getComponentType(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let cls = { let rf = arg0.extract_ref(); let mirror = rf.extract_mirror(); mirror.target.clone().unwrap() }; let cls = cls.get_class(); let v = match &cls.kind { oop::class::ClassKind::TypeArray(type_ary_cls) => { let vt = type_ary_cls.value_type.into(); let key = unsafe { std::str::from_utf8_unchecked(vt) }; let mirrors = PRIM_MIRROS.read().unwrap(); mirrors.get(key).cloned() } oop::class::ClassKind::ObjectArray(obj_ary_cls) => { let component = obj_ary_cls.component.clone().unwrap(); let cls = component.get_class(); Some(cls.get_mirror()) } _ => unreachable!(), }; Ok(v) } fn jvm_getEnclosingMethod0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let target = { let rf = arg0.extract_ref(); let ptr = rf.get_raw_ptr(); unsafe { match &(*ptr).v { oop::RefKind::Mirror(mirror) => mirror.target.clone(), _ => return Ok(Some(Oop::Null)), } } }; let (cls_file, em) = match target { Some(target) => { let cls = target.get_class(); match &cls.kind { ClassKind::Instance(cls) => match &cls.enclosing_method { Some(em) => (cls.class_file.clone(), em.clone()), None => return Ok(Some(Oop::Null)), }, _ => return Ok(Some(Oop::Null)), } } None => return Ok(Some(Oop::Null)), }; //push EnclosingMethod class mirror if em.class_index == 0 { panic!(); } let em_class = require_class2(em.class_index, &cls_file.cp).unwrap(); let em_class_mirror = { let cls = em_class.get_class(); cls.get_mirror() }; let mut elms = Vec::with_capacity(3); elms.push(em_class_mirror); //push EnclosingMethod name&desc if em.method_index != 0 { let (name, desc) = constant_pool::get_name_and_type(&cls_file.cp, em.method_index as usize); let name = unsafe { std::str::from_utf8_unchecked(name.as_slice()) }; let desc = unsafe { std::str::from_utf8_unchecked(desc.as_slice()) }; elms.push(util::oop::new_java_lang_string2(name)); elms.push(util::oop::new_java_lang_string2(desc)); } else { elms.push(Oop::Null); elms.push(Oop::Null); } let ary = require_class3(None, b"[Ljava/lang/Object;").unwrap(); let ary = Oop::new_ref_ary2(ary, elms); Ok(Some(ary)) } fn jvm_getDeclaringClass0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let target = { let rf = arg0.extract_ref(); let ptr = rf.get_raw_ptr(); unsafe { match &(*ptr).v { oop::RefKind::Mirror(mirror) => mirror.target.clone(), _ => return Ok(Some(Oop::Null)), } } }; let (cls_file, target, inner_classes) = match target { Some(target) => { let cls = target.get_class(); match &cls.kind { ClassKind::Instance(cls) => match &cls.inner_classes { Some(inner_classes) => ( cls.class_file.clone(), target.clone(), inner_classes.clone(), ), None => return Ok(Some(Oop::Null)), }, _ => return Ok(Some(Oop::Null)), } } None => return Ok(Some(Oop::Null)), }; for it in inner_classes.iter() { if it.inner_class_info_index == 0 { continue; } let inner_class = require_class2(it.inner_class_info_index, &cls_file.cp).unwrap(); if Arc::ptr_eq(&inner_class, &target) { return if it.outer_class_info_index == 0 { Ok(Some(Oop::Null)) } else { let outer_class = require_class2(it.outer_class_info_index, &cls_file.cp).unwrap(); let v = outer_class.get_class(); Ok(Some(v.get_mirror())) }; } } Ok(Some(Oop::Null)) } fn jvm_isInstance(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg0 = args.get(0).unwrap(); let arg1 = args.get(1).unwrap(); let target_cls = { let rf = arg0.extract_ref(); let ptr = rf.get_raw_ptr(); unsafe { match &(*ptr).v { oop::RefKind::Inst(inst) => inst.class.clone(), oop::RefKind::Mirror(mirror) => mirror.target.clone().unwrap(), _ => unreachable!(), } } }; let obj_cls = { let rf = arg1.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let v = if runtime::cmp::instance_of(obj_cls, target_cls) { 1 } else { 0 }; Ok(Some(Oop::new_int(v))) } fn jvm_getDeclaredMethods0(_env: JNIEnv, args: &[Oop]) -> JNIResult { //parse args let mirror_target = { let arg0 = args.get(0).unwrap(); extract_mirror_target(arg0) }; let public_only = args.get(1).unwrap().extract_int() == 1; get_declared_method_helper(mirror_target, public_only, false) } fn jvm_getInterfaces0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let mirror = { let arg0 = args.get(0).unwrap(); extract_mirror_target(arg0) }; let v = mirror.get_class(); let elms = match &v.kind { oop::ClassKind::Instance(inst) => { let mut elms = Vec::with_capacity(inst.class_file.interfaces.len()); let cp = &inst.class_file.cp; inst.class_file.interfaces.iter().for_each(|it| { let cls = require_class2(*it, cp).unwrap(); let cls = cls.get_class(); elms.push(cls.get_mirror()); }); elms } ClassKind::ObjectArray(_ary) => { let cls_cloneable = require_class3(None, cls_consts::J_CLONEABLE).unwrap(); let cls_serializable = require_class3(None, cls_consts::J_SERIALIZABLE).unwrap(); let mut elms = Vec::with_capacity(2); { let cls = cls_cloneable.get_class(); elms.push(cls.get_mirror()); } { let cls = cls_serializable.get_class(); elms.push(cls.get_mirror()); } elms } ClassKind::TypeArray(_) => unimplemented!("type array getInterfaces0"), }; let clazz = require_class3(None, b"[Ljava/lang/Class;").unwrap(); let ary = Oop::new_ref_ary2(clazz, elms); Ok(Some(ary)) } fn jvm_getRawAnnotations(_env: JNIEnv, args: &[Oop]) -> JNIResult { let cls = args.get(0).unwrap(); let annotations = match cls { Oop::Ref(rf) => { let mirror = rf.extract_mirror(); let cls = mirror.target.clone().unwrap(); let cls = cls.get_class(); let raw = cls.get_annotation(); match raw { Some(raw) => Oop::new_byte_ary2(raw.to_vec()), None => Oop::Null, } } _ => Oop::Null, }; Ok(Some(annotations)) } fn jvm_getConstantPool(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let cp_oop = match this { Oop::Ref(_rf) => { let cp_cls = require_class3(None, b"sun/reflect/ConstantPool").unwrap(); let cp_oop = Oop::new_inst(cp_cls.clone()); let cls = cp_cls.get_class(); let fid = cls.get_field_id(&util::S_CONSTANT_POOL_OOP, &util::S_JAVA_LANG_OBJECT, false); //todo: reimpl maybe, create one JNIHandles, like jdk Class::put_field_value(cp_oop.extract_ref(), fid, this.clone()); cp_oop } _ => Oop::Null, }; Ok(Some(cp_oop)) } fn jvm_getDeclaredClasses0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let target_clz = extract_mirror_target(this); let array_class = require_class3(None, b"[Ljava/lang/Class;").unwrap(); let target_class = target_clz.get_class(); let v = match &target_class.kind { ClassKind::Instance(inst) => { let cp = &inst.class_file.cp; match &inst.inner_classes { Some(inner_classes) => { let this_class = inst.class_file.this_class; let mut inners = Vec::with_capacity(inner_classes.len()); for it in inner_classes { if it.outer_class_info_index == this_class { let inner_clz = require_class2(it.inner_class_info_index, cp).unwrap(); let v = inner_clz.get_class().get_mirror(); inners.push(v); } } Oop::new_ref_ary2(array_class, inners) } _ => Oop::new_ref_ary(array_class, 0), } } _ => Oop::new_ref_ary(array_class, 0), }; Ok(Some(v)) } fn jvm_getGenericSignature0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let v = match this { Oop::Ref(rf) => { let ptr = rf.get_raw_ptr(); unsafe { let mirror = (*ptr).v.extract_mirror(); let vt = mirror.value_type; if vt == ValueType::OBJECT { let target = mirror.target.clone().unwrap(); let cls = target.get_class(); let sig = cls.get_attr_signatrue(); sig.map_or(Oop::Null, |v| { let sig = std::str::from_utf8_unchecked(v.as_slice()); util::oop::new_java_lang_string2(sig) }) } else { Oop::Null } } } _ => unreachable!(), }; Ok(Some(v)) } fn get_declared_method_helper( mirror_target: ClassRef, public_only: bool, want_constructor: bool, ) -> JNIResult { //fixme: super methods let selected_methods = { let cls = mirror_target.get_class(); match &cls.kind { oop::class::ClassKind::Instance(inst) => { fn chooser1(want_constructor: bool, name: &[u8]) -> bool { if want_constructor { name == b"" } else { name != b"" } } fn chooser2(want_constructor: bool, m: &MethodIdRef) -> bool { if want_constructor { m.method.name.as_slice() == b"" && !m.method.is_static() } else { m.method.name.as_slice() != b"" } } let mut selected_methods = Vec::new(); for (k, m) in inst.all_methods.iter() { if !chooser1(want_constructor, k.0.as_slice()) { continue; } if chooser2(want_constructor, &m) && (!public_only || m.method.is_public()) { selected_methods.push(m.clone()); } } selected_methods } oop::class::ClassKind::ObjectArray(_ary) => vec![], _ => unreachable!("{:?}", mirror_target), } }; //build methods ary let mut methods = Vec::with_capacity(selected_methods.len()); for m in selected_methods { let v = if want_constructor { common::reflect::new_method_ctor(m) } else { common::reflect::new_method_normal(m) }; methods.push(v); } //build oop methods ary let ary_cls = if want_constructor { require_class3(None, b"[Ljava/lang/reflect/Constructor;").unwrap() } else { require_class3(None, b"[Ljava/lang/reflect/Method;").unwrap() }; Ok(Some(Oop::new_ref_ary2(ary_cls, methods))) } fn extract_mirror_target(v: &Oop) -> ClassRef { let rf = v.extract_ref(); let mirror = rf.extract_mirror(); mirror.target.clone().unwrap() } ================================================ FILE: crates/vm/src/native/java_lang_ClassLoader.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop, OopPtr}; use crate::runtime; use crate::util; pub fn get_native_methods() -> Vec { vec![ new_fn("registerNatives", "()V", Box::new(jvm_registerNatives)), new_fn( "findBuiltinLib", "(Ljava/lang/String;)Ljava/lang/String;", Box::new(jvm_findBuiltinLib), ), new_fn( "findLoadedClass0", "(Ljava/lang/String;)Ljava/lang/Class;", Box::new(jvm_findLoadedClass0), ), new_fn( "findBootstrapClass", "(Ljava/lang/String;)Ljava/lang/Class;", Box::new(jvm_findBootstrapClass), ), ] } fn jvm_registerNatives(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } fn jvm_findBuiltinLib(_env: JNIEnv, args: &[Oop]) -> JNIResult { let name = args.get(0).unwrap(); let name = OopPtr::java_lang_string(name.extract_ref()); info!("findBuiltinLib: {}", name); Ok(None) } fn jvm_findLoadedClass0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let name = args.get(1).unwrap(); let name = OopPtr::java_lang_string(name.extract_ref()); info!("findLoadedClass0: {}", name); let name = name.replace(".", util::FILE_SEP); let v = match runtime::sys_dic_find(name.as_bytes()) { Some(cls) => { let cls = cls.get_class(); cls.get_mirror() } None => Oop::Null, }; Ok(Some(v)) } // fixme: Is this correct? uncertain fn jvm_findBootstrapClass(_env: JNIEnv, args: &[Oop]) -> JNIResult { info!("findBootstrapClass"); jvm_findLoadedClass0(_env, args) } ================================================ FILE: crates/vm/src/native/java_lang_Double.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![ new_fn( "doubleToRawLongBits", "(D)J", Box::new(jvm_doubleToRawLongBits), ), new_fn("longBitsToDouble", "(J)D", Box::new(jvm_longBitsToDouble)), ] } fn jvm_doubleToRawLongBits(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap().extract_double(); let v = v.to_bits().to_be_bytes(); let v = i64::from_be_bytes(v); Ok(Some(Oop::new_long(v))) } fn jvm_longBitsToDouble(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap().extract_long(); let v = v.to_be_bytes(); let v = f64::from_be_bytes(v); Ok(Some(Oop::new_double(v))) } ================================================ FILE: crates/vm/src/native/java_lang_Float.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![new_fn( "floatToRawIntBits", "(F)I", Box::new(jvm_floatToRawIntBits), )] } fn jvm_floatToRawIntBits(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap().extract_float(); let v = v.to_bits().to_be_bytes(); let v = i32::from_be_bytes(v); Ok(Some(Oop::new_int(v))) } ================================================ FILE: crates/vm/src/native/java_lang_Object.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop}; use std::time::Duration; pub fn get_native_methods() -> Vec { vec![ new_fn("registerNatives", "()V", Box::new(jvm_registerNatives)), new_fn("hashCode", "()I", Box::new(jvm_hashCode)), new_fn("clone", "()Ljava/lang/Object;", Box::new(jvm_clone)), new_fn("getClass", "()Ljava/lang/Class;", Box::new(jvm_getClass)), new_fn("notifyAll", "()V", Box::new(jvm_notifyAll)), new_fn("wait", "(J)V", Box::new(jvm_wait)), ] } fn jvm_registerNatives(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } pub fn jvm_hashCode(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let v = v.hash_code(); let v = Oop::new_int(v); Ok(Some(v)) } fn jvm_clone(_env: JNIEnv, args: &[Oop]) -> JNIResult { // let java_lang_Cloneable = require_class3(None, b"java/lang/Cloneable").unwrap(); let this_obj = args.get(0).unwrap(); Ok(Some(this_obj.clone())) } fn jvm_getClass(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let mirror = { let rf = v.extract_ref(); let ptr = rf.get_raw_ptr(); unsafe { match &(*ptr).v { oop::RefKind::Inst(inst) => { let cls = inst.class.get_class(); cls.get_mirror() } oop::RefKind::Array(ary) => ary.class.get_class().get_mirror(), oop::RefKind::Mirror(_mirror) => v.clone(), t => unimplemented!("t = {:?}", t), } } }; Ok(Some(mirror)) } fn jvm_notifyAll(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let rf = this.extract_ref(); rf.notify_all(); Ok(None) } fn jvm_wait(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let millis = args.get(1).unwrap().extract_long(); let rf = this.extract_ref(); if millis == 0 { rf.wait(); } else { rf.wait_timeout(Duration::from_millis(millis as u64)); } Ok(None) } ================================================ FILE: crates/vm/src/native/java_lang_Runtime.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![ new_fn( "availableProcessors", "()I", Box::new(jvm_availableProcessors), ), new_fn("gc", "()V", Box::new(jvm_gc)), ] } //fixme: fn jvm_availableProcessors(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(1))) } fn jvm_gc(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } ================================================ FILE: crates/vm/src/native/java_lang_String.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![new_fn( "intern", "()Ljava/lang/String;", Box::new(jvm_intern), )] } fn jvm_intern(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); Ok(Some(v.clone())) } ================================================ FILE: crates/vm/src/native/java_lang_System.rs ================================================ #![allow(non_snake_case)] use crate::native::{self, new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop, OopPtr}; use crate::runtime::{self, thread, JavaCall}; use crate::{new_br, util}; use std::sync::Arc; use std::time::SystemTime; pub fn get_native_methods() -> Vec { vec![ new_fn( "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", Box::new(jvm_arraycopy), ), new_fn("registerNatives", "()V", Box::new(jvm_registerNatives)), new_fn( "initProperties", "(Ljava/util/Properties;)Ljava/util/Properties;", Box::new(jvm_initProperties), ), new_fn("setIn0", "(Ljava/io/InputStream;)V", Box::new(jvm_setIn0)), new_fn("setOut0", "(Ljava/io/PrintStream;)V", Box::new(jvm_setOut0)), new_fn("setErr0", "(Ljava/io/PrintStream;)V", Box::new(jvm_setErr0)), new_fn( "mapLibraryName", "(Ljava/lang/String;)Ljava/lang/String;", Box::new(jvm_mapLibraryName), ), new_fn( "loadLibrary", "(Ljava/lang/String;)V", Box::new(jvm_loadLibrary), ), new_fn( "identityHashCode", "(Ljava/lang/Object;)I", Box::new(jvm_identityHashCode), ), new_fn("nanoTime", "()J", Box::new(jvm_nanoTime)), new_fn("currentTimeMillis", "()J", Box::new(jvm_currentTimeMillis)), //Note: just for debug // new_fn("getProperty", "(Ljava/lang/String;)Ljava/lang/String;", Box::new(jvm_getProperty)), ] } fn jvm_registerNatives(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } fn jvm_arraycopy(_env: JNIEnv, args: &[Oop]) -> JNIResult { let src = args.get(0).unwrap(); let src_pos = args.get(1).unwrap().extract_int(); let dest = args.get(2).unwrap(); let dest_pos = args.get(3).unwrap().extract_int(); let length = args.get(4).unwrap().extract_int(); //todo: do check & throw exception if length == 0 { return Ok(None); } let is_same_obj = OopPtr::is_eq(src, dest); if is_same_obj { arraycopy_same_obj( src.extract_ref(), src_pos as usize, dest_pos as usize, length as usize, ); } else { arraycopy_diff_obj( src.extract_ref(), src_pos as usize, dest.extract_ref(), dest_pos as usize, length as usize, ); } Ok(None) } fn jvm_initProperties(_env: JNIEnv, args: &[Oop]) -> JNIResult { //fixme: let props = vec![ ("file.encoding.pkg", "sun.io"), ("file.encoding", "US-ASCII"), ("file.separator", util::FILE_SEP), ("java.class.path", "."), ("java.class.version", "52.0"), ("java.security.egd", "file:/dev/random"), // ("java.security.debug", "all"), // ("java.security.auth.debug", "all"), ("java.specification.version", "1.8"), ("java.specification.name", "Java Platform API Specification"), ("java.specification.vendor", "Oracle Corporation"), ("java.vendor", "Chuan"), ("java.vendor.url", "https://github.com/douchuan/jvm"), ("java.vendor.url.bug", "https://github.com/douchuan/jvm"), ("java.version", "1.8"), ("line.separator", util::LINE_SEP), ("os.arch", "x86_64"), ("os.name", "Mac OS X"), ("os.version", "18.7.0"), ("path.separator", util::PATH_SEP), ("sun.arch.data.model", "64"), ("sun.cpu.endian", "little"), ("sun.cpu.isalist", ""), // ("sun.misc.URLClassPath.debug", "true"), // ("sun.misc.URLClassPath.debugLookupCache", "true"), ("sun.stdout.encoding", "UTF-8"), ("sun.stderr.encoding", "UTF-8"), ("user.language", "en"), ("user.name", "chuan"), ("user.region", "US"), // ("java.security.manager", ""), // ("sun.jnu.encoding", "UTF-8"), // ("sun.io.unicode.encoding", "UnicodeBig"), ]; let props_oop = args.get(0).unwrap(); for (k, v) in props.iter() { put_props_kv(props_oop, k, v); } //user.dir let v = std::env::current_dir().expect("current_dir failed"); let v = v.to_str().expect("current_dir to_str faield"); put_props_kv(props_oop, "user.dir", v); //java.io.tmpdir let v = std::env::temp_dir(); let v = v.to_str().expect("temp_dir to_str failed"); put_props_kv(props_oop, "java.io.tmpdir", v); //user.home let v = dirs::home_dir().expect("get home_dir failed"); let v = v.to_str().expect("home_dir to_str failed"); put_props_kv(props_oop, "user.home", v); //JAVA_HOME let v = std::env::var("JAVA_HOME").expect("Please Setup JAVA_HOME env"); put_props_kv(props_oop, "java.home", v.as_str()); //test.src for jdk/test/java/lang/Character/CheckProp.java if let Ok(v) = std::env::var("TEST_SRC") { put_props_kv(props_oop, "test.src", v.as_str()); } if thread::is_meet_ex() { unreachable!("jvm_initProperties meet ex"); } Ok(Some(props_oop.clone())) } fn put_props_kv(props: &Oop, k: &str, v: &str) { //todo: optimize me let cls = { let rf = props.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let mir = { let cls = cls.get_class(); cls.get_virtual_method( &new_br("put"), &new_br("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"), ) .unwrap() }; let k = util::oop::new_java_lang_string2(k); let v = util::oop::new_java_lang_string2(v); let args = vec![props.clone(), k, v]; let mut jc = JavaCall::new_with_args(mir, args); let area = runtime::DataArea::new(1); jc.invoke(Some(&area), false); } fn jvm_setIn0(env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let cls = env.read().unwrap().class.clone(); let cls = cls.get_mut_class(); let id = cls.get_field_id(&util::S_IN, &util::S_JAVA_IO_INPUT_STREAM, true); cls.put_static_field_value(id, v.clone()); Ok(None) } fn jvm_setOut0(env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let cls = env.read().unwrap().class.clone(); let cls = cls.get_mut_class(); let id = cls.get_field_id(&util::S_OUT, &util::S_JAVA_IO_PRINT_STREAM, true); cls.put_static_field_value(id, v.clone()); Ok(None) } fn jvm_setErr0(env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let cls = env.read().unwrap().class.clone(); let cls = cls.get_mut_class(); let id = cls.get_field_id(&util::S_ERR, &util::S_JAVA_IO_PRINT_STREAM, true); cls.put_static_field_value(id, v.clone()); Ok(None) } fn jvm_mapLibraryName(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let s = OopPtr::java_lang_string(v.extract_ref()); trace!("mapLibraryName libname = {}", s); let mut name = String::new(); if cfg!(target_os = "macos") { name.push_str("lib"); name.push_str(&s); name.push_str(".dylib"); } else if cfg!(target_os = "windows") { unimplemented!(); // name.extend_from_slice(s.as_bytes()); // name.extend_from_slice(".dll".as_bytes()); } else if cfg!(target_os = "linux") { unimplemented!(); // name.extend_from_slice("lib".as_bytes()); // name.extend_from_slice(s.as_bytes()); // name.extend_from_slice(".so".as_bytes()); } else { unimplemented!() } trace!("mapLibraryName name = {}", name); let v = util::oop::new_java_lang_string2(&name); Ok(Some(v)) } fn jvm_loadLibrary(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } fn jvm_identityHashCode(env: JNIEnv, args: &[Oop]) -> JNIResult { native::java_lang_Object::jvm_hashCode(env, args) } /* fn jvm_getProperty(jt: &mut JavaThread, env: JNIEnv, args: Vec) -> JNIResult { let key = args.get(0).unwrap(); let str_key = util::oop::extract_str(key.clone()); warn!("xxxx jvm_getProperty key = {}", str_key); let cls = require_class3(None, b"java/lang/System").unwrap(); let props = { let cls = cls.lock().unwrap(); let id = cls.get_field_id(b"props", b"Ljava/util/Properties;", true); cls.get_static_field_value(id) }; let prop_cls = require_class3(None, b"java/util/Properties").unwrap(); let mir = { let cls = prop_cls.lock().unwrap(); let id = util::new_method_id(b"getProperty", b"(Ljava/lang/String;)Ljava/lang/String;"); cls.get_class_method(id).unwrap() }; let args = vec![props, key.clone()]; let mut stack = Stack::new(1); let mut jc = runtime::java_call::JavaCall::new_with_args(jt, mir, args); jc.invoke(jt, &mut stack, false); let v = stack.pop_ref(); // trace!("xxxxx 1, str_key = {}", String::from_utf8_lossy(str_key.as_slice())); // let str_v = util::oop::extract_str(v.clone()); // warn!("xxxx jvm_getProperty v = {}", String::from_utf8_lossy(str_v.as_slice())); // trace!("xxxxx 2"); Ok(Some(v)) } */ fn arraycopy_same_obj(buf: Arc, src_pos: usize, dest_pos: usize, length: usize) { let is_type_ary = { let ptr = buf.get_raw_ptr(); unsafe { match &(*ptr).v { oop::RefKind::TypeArray(_) => true, oop::RefKind::Array(_) => false, _ => unreachable!(), } } }; if is_type_ary { let ptr = buf.get_mut_raw_ptr(); unsafe { match &mut (*ptr).v { oop::RefKind::TypeArray(ary) => match ary { oop::TypeArrayDesc::Char(ary) => { ary.copy_within(src_pos..(src_pos + length), dest_pos) } oop::TypeArrayDesc::Byte(ary) => { ary.copy_within(src_pos..(src_pos + length), dest_pos) } t => unreachable!("t = {:?}", t), }, _ => unreachable!(), } } } else { let tmp = { let ary = buf.extract_array(); let mut tmp = vec![Oop::Null; length]; let (_, ary) = ary.elements.split_at(src_pos); tmp.clone_from_slice(&ary[..length]); tmp }; let ary = buf.extract_mut_array(); let (_, ary) = ary.elements.split_at_mut(dest_pos); ary[..length].clone_from_slice(&tmp[..]); } } pub fn arraycopy_diff_obj( src: Arc, src_pos: usize, dest: Arc, dest_pos: usize, length: usize, ) { let is_type_ary = { let ptr = src.get_raw_ptr(); unsafe { match &(*ptr).v { oop::RefKind::TypeArray(_) => true, oop::RefKind::Array(_) => false, _ => unreachable!(), } } }; // error!("src={}, dest={}, length={}, is_type_ary={}", src_pos, dest_pos, length, is_type_ary); let src_ptr = src.get_raw_ptr(); if is_type_ary { unsafe { match &(*src_ptr).v { oop::RefKind::TypeArray(src_ary) => match src_ary { oop::TypeArrayDesc::Byte(src_ary) => { let dest_ary = dest.extract_mut_type_array(); let dest_ary = dest_ary.extract_mut_bytes(); let (_, dest_ptr) = dest_ary.split_at_mut(dest_pos); let (_, src_ptr) = src_ary.split_at(src_pos); dest_ptr[..length].copy_from_slice(&src_ptr[..length]); } oop::TypeArrayDesc::Char(src_ary) => { let dest_ary = dest.extract_mut_type_array(); let dest_ary = dest_ary.extract_mut_chars(); let (_, dest_ptr) = dest_ary.split_at_mut(dest_pos); let (_, src_ptr) = src_ary.split_at(src_pos); dest_ptr[..length].copy_from_slice(&src_ptr[..length]); } oop::TypeArrayDesc::Int(src_ary) => { let dest_ary = dest.extract_mut_type_array(); let dest_ary = dest_ary.extract_mut_ints(); let (_, dest_ptr) = dest_ary.split_at_mut(dest_pos); let (_, src_ptr) = src_ary.split_at(src_pos); dest_ptr[..length].copy_from_slice(&src_ptr[..length]); } t => unreachable!("t = {:?}", t), }, _ => unreachable!(), } } } else { let src = src.extract_array(); let dest = dest.extract_mut_array(); let (_, src_ptr) = src.elements.split_at(src_pos); let (_, dest_ptr) = dest.elements.split_at_mut(dest_pos); dest_ptr[..length].clone_from_slice(&src_ptr[..length]); } } fn jvm_nanoTime(_env: JNIEnv, _args: &[Oop]) -> JNIResult { let v = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(n) => n.as_nanos(), Err(_) => panic!("SystemTime before UNIX EPOCH!"), }; // let v = chrono::Utc::now().timestamp_nanos(); Ok(Some(Oop::new_long(v as i64))) } fn jvm_currentTimeMillis(_env: JNIEnv, _args: &[Oop]) -> JNIResult { let v = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(n) => n.as_millis(), Err(_) => panic!("SystemTime before UNIX EPOCH!"), }; Ok(Some(Oop::new_long(v as i64))) } ================================================ FILE: crates/vm/src/native/java_lang_Thread.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::new_br; use crate::oop::{Class, Oop, OopPtr}; use crate::runtime::vm::get_vm; use crate::runtime::{self, vm, JavaCall, JavaThread}; pub fn get_native_methods() -> Vec { vec![ new_fn("registerNatives", "()V", Box::new(jvm_registerNatives)), new_fn( "currentThread", "()Ljava/lang/Thread;", Box::new(jvm_currentThread), ), new_fn("setPriority0", "(I)V", Box::new(jvm_setPriority0)), new_fn("isAlive", "()Z", Box::new(jvm_isAlive)), new_fn("start0", "()V", Box::new(jvm_start0)), new_fn("isInterrupted", "(Z)Z", Box::new(jvm_isInterrupted)), ] } fn jvm_registerNatives(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } fn jvm_currentThread(_env: JNIEnv, _args: &[Oop]) -> JNIResult { let jt = runtime::thread::current_java_thread(); let obj = jt.read().unwrap().java_thread_obj.clone(); Ok(obj) } fn jvm_setPriority0(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } //'_jt' is caller's thread context, can't be used here //should find by 'eetop' in thread pool fn jvm_isAlive(_env: JNIEnv, args: &[Oop]) -> JNIResult { let this = args.get(0).unwrap(); let eetop = OopPtr::java_lang_thread_eetop(this.extract_ref()); let vm = get_vm(); let r = match vm.threads.find_java_thread(eetop) { Some(jt) => { info!("native thread tag = {}", jt.read().unwrap().tag); if jt.read().unwrap().is_alive { 1 } else { 0 } } None => 0, }; Ok(Some(Oop::new_int(r))) } fn jvm_start0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let thread_oop = args.get(0).unwrap().clone(); let clazz = { let rf = thread_oop.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let cls = clazz.get_class(); if cls.name.as_slice() == "java/lang/ref/Reference$ReferenceHandler".as_bytes() { Ok(None) } else { let vm = vm::get_vm(); let jt = JavaThread::new(None, vm.threads.next_id()); vm.threads.attach_java_thread(jt.clone()); let args = vec![thread_oop.clone()]; vm.threads.spawn_java_thread(move || { //setup current thread let current_thread = jt.clone(); runtime::thread::THREAD.with(|t| { *t.borrow_mut() = current_thread; }); let cls = clazz.get_class(); let mir = { //setup eetop let eetop = jt.read().unwrap().eetop; let fid = cls.get_field_id(&new_br("eetop"), &new_br("J"), false); Class::put_field_value(thread_oop.extract_ref(), fid, Oop::new_long(eetop)); //obtain 'run' method cls.get_virtual_method(&new_br("run"), &new_br("()V")) .unwrap() }; //invoke 'run' let mut jc = JavaCall::new_with_args(mir, args); jt.write().unwrap().is_alive = true; jt.write().unwrap().java_thread_obj = Some(thread_oop.clone()); jc.invoke(None, false); jt.write().unwrap().is_alive = false; //notify thread that invoke 'join' let v = thread_oop.extract_ref(); v.notify_all(); vm.threads.detach_current_thread(); }); Ok(None) } } fn jvm_isInterrupted(_env: JNIEnv, _args: &[Oop]) -> JNIResult { //todo: fix me let v = Oop::new_int(0); Ok(Some(v)) } ================================================ FILE: crates/vm/src/native/java_lang_Throwable.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Class, Oop}; use crate::runtime::{self, require_class3}; use crate::{new_br, util}; use std::sync::atomic::Ordering; pub fn get_native_methods() -> Vec { vec![ new_fn( "fillInStackTrace", "(I)Ljava/lang/Throwable;", Box::new(jvm_fillInStackTrace), ), new_fn( "getStackTraceDepth", "()I", Box::new(jvm_getStackTraceDepth), ), new_fn( "getStackTraceElement", "(I)Ljava/lang/StackTraceElement;", Box::new(jvm_getStackTraceElement), ), ] } fn jvm_fillInStackTrace(_env: JNIEnv, args: &[Oop]) -> JNIResult { let jt = runtime::thread::current_java_thread(); let elm_cls = oop::class::load_and_init(b"java/lang/StackTraceElement"); let ary_cls = require_class3(None, b"[Ljava/lang/StackTraceElement;").unwrap(); let throwable_oop = args.get(0).unwrap(); let mut backtrace = Vec::with_capacity(jt.read().unwrap().frames.len()); let mut found_ex_here = false; let jth = jt.read().unwrap(); for it in jth.frames.iter() { let ex_here = { let it = it.try_read().unwrap(); it.ex_here.load(Ordering::Relaxed) }; backtrace.push(it.clone()); if ex_here { found_ex_here = true; break; } } drop(jth); /* todo: how handle throw better? if no ex_here found, it's: throw new AnnotationFormatError("Unexpected end of annotations."); new Throwable Throwable.fillInStackTrace invoked, and come here there are stacktraces for build 'Throwable' obj, not necessary for user, need discard Exception in thread "main" java.lang.annotation.AnnotationFormatError: Unexpected end of annotations. at java.lang.Throwable.fillInStackTrace(Throwable.java) at java.lang.Throwable.fillInStackTrace(Throwable.java:783) at java.lang.Throwable.(Throwable.java:265) at java.lang.Error.(Error.java:70) */ if !found_ex_here { backtrace.pop(); backtrace.pop(); backtrace.pop(); backtrace.pop(); } let mut traces = Vec::new(); for caller in backtrace.iter().rev() { let (mir, pc) = { let caller = caller.try_read().unwrap(); let pc = caller.pc.load(Ordering::Relaxed); (caller.mir.clone(), pc) }; let cls = mir.method.class.get_class(); let cls_name = unsafe { std::str::from_utf8_unchecked(cls.name.as_slice()) }; let cls_name = cls_name.replace("/", "."); let method_name = unsafe { std::str::from_utf8_unchecked(mir.method.name.as_slice()) }; let src_file = { let cls = mir.method.class.get_class(); cls.get_source_file() }; let src_file = match src_file { Some(name) => { let name = unsafe { std::str::from_utf8_unchecked(name.as_slice()) }; util::oop::new_java_lang_string2(name) } None => util::oop::new_java_lang_string2(""), }; let line_num = mir.method.get_line_num((pc - 1) as u16); let elm = Oop::new_inst(elm_cls.clone()); let args = vec![ elm.clone(), util::oop::new_java_lang_string2(&cls_name), util::oop::new_java_lang_string2(method_name), src_file, Oop::new_int(line_num), ]; runtime::invoke::invoke_ctor( elm_cls.clone(), new_br("(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"), args, ); traces.push(elm); } let stack_trace_ary = Oop::new_ref_ary2(ary_cls, traces); let throwable_cls = require_class3(None, b"java/lang/Throwable").unwrap(); { let cls = throwable_cls.get_class(); let id = cls.get_field_id( &new_br("stackTrace"), &new_br("[Ljava/lang/StackTraceElement;"), false, ); Class::put_field_value(throwable_oop.extract_ref(), id, Oop::Null); let id = cls.get_field_id(&new_br("backtrace"), &new_br("Ljava/lang/Object;"), false); Class::put_field_value(throwable_oop.extract_ref(), id, stack_trace_ary); } Ok(Some(throwable_oop.clone())) } fn jvm_getStackTraceDepth(_env: JNIEnv, args: &[Oop]) -> JNIResult { let throwable = args.get(0).unwrap(); let cls = { let rf = throwable.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let backtrace = { let cls = cls.get_class(); let id = cls.get_field_id(&new_br("backtrace"), &new_br("Ljava/lang/Object;"), false); Class::get_field_value(throwable.extract_ref(), id) }; let v = match backtrace { Oop::Null => Oop::new_int(0), Oop::Ref(rf) => { let ary = rf.extract_array(); let len = ary.elements.len(); Oop::new_int(len as i32) } _ => unreachable!(), }; Ok(Some(v)) } fn jvm_getStackTraceElement(_env: JNIEnv, args: &[Oop]) -> JNIResult { let throwable = args.get(0).unwrap(); let index = args.get(1).unwrap().extract_int(); let cls = { let rf = throwable.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let backtrace = { let cls = cls.get_class(); let id = cls.get_field_id(&new_br("backtrace"), &new_br("Ljava/lang/Object;"), false); Class::get_field_value(throwable.extract_ref(), id) }; let v = { let rf = backtrace.extract_ref(); let ary = rf.extract_array(); if index >= 0 && (index as usize) < ary.elements.len() { ary.elements[index as usize].clone() } else { Oop::Null } }; Ok(Some(v)) } ================================================ FILE: crates/vm/src/native/java_lang_reflect_Array.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop, ValueType}; use crate::runtime::require_class3; use crate::types::ClassRef; pub fn get_native_methods() -> Vec { vec![ new_fn( "newArray", "(Ljava/lang/Class;I)Ljava/lang/Object;", Box::new(jvm_newArray), ), new_fn( "getLength", "(Ljava/lang/Object;)I", Box::new(jvm_getLength), ), ] } fn jvm_newArray(_env: JNIEnv, args: &[Oop]) -> JNIResult { let mirror = args.get(0).unwrap(); //todo: throw NegativeArraySizeException let length = args.get(1).unwrap().extract_int(); let (vt, component_cls) = { let rf = mirror.extract_ref(); let mirror = rf.extract_mirror(); (mirror.value_type, mirror.target.clone()) }; let name = build_ary_name(vt, component_cls); let ary_cls = require_class3(None, name.as_slice()).unwrap(); let v = Oop::new_ref_ary(ary_cls, length as usize); Ok(Some(v)) } fn jvm_getLength(_env: JNIEnv, args: &[Oop]) -> JNIResult { let ary = args.get(0).unwrap(); let rf = ary.extract_ref(); let ptr = rf.get_raw_ptr(); let len = unsafe { match &(*ptr).v { oop::RefKind::TypeArray(ary) => ary.len(), oop::RefKind::Array(ary) => ary.elements.len(), _ => unreachable!(), } }; let v = Oop::new_int(len as i32); Ok(Some(v)) } fn build_ary_name(vt: ValueType, component_cls: Option) -> Vec { let mut name = Vec::from("["); match vt { ValueType::BYTE | ValueType::BOOLEAN | ValueType::CHAR | ValueType::SHORT | ValueType::INT | ValueType::LONG | ValueType::FLOAT | ValueType::DOUBLE => name.extend_from_slice(vt.into()), ValueType::OBJECT | ValueType::ARRAY => { let cls = component_cls.unwrap(); let cls = cls.get_class(); match cls.get_class_kind_type() { oop::class::ClassKindType::Instance => { name.extend_from_slice(b"L"); name.extend_from_slice(cls.name.as_slice()); name.extend_from_slice(b";"); } oop::class::ClassKindType::ObjectAry => { name.extend_from_slice(cls.name.as_slice()); name.extend_from_slice(b";"); } oop::class::ClassKindType::TypAry => unimplemented!(), } } ValueType::VOID => unreachable!(), } name } ================================================ FILE: crates/vm/src/native/java_lang_reflect_Proxy.rs ================================================ #![allow(non_snake_case)] use crate::native; use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::class::ClassPtr; use crate::oop::{self, Class, Oop, OopPtr}; use crate::runtime; use crate::types::ClassRef; use class_parser::parse_class; use std::sync::Arc; pub fn get_native_methods() -> Vec { vec![new_fn( "defineClass0", "(Ljava/lang/ClassLoader;Ljava/lang/String;[BII)Ljava/lang/Class;", Box::new(jvm_defineClass0), )] } fn jvm_defineClass0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _loader = args.get(0).unwrap(); let name = args.get(1).unwrap(); let name = OopPtr::java_lang_string(name.extract_ref()); let b = args.get(2).unwrap(); let off = args.get(3).unwrap().extract_int(); let len = args.get(4).unwrap().extract_int(); let name = name.replace(".", "/"); //parse bytes => class, put in sys_dic let class = do_parse_class(b, off as usize, len as usize); runtime::sys_dic_put(name.as_bytes(), class.clone()); { let this_ref = class.clone(); let cls = class.get_mut_class(); cls.set_class_state(oop::class::State::Loaded); cls.link_class(this_ref); } native::java_lang_Class::create_mirror(class.clone()); let v = Oop::new_mirror(class); Ok(Some(v)) } fn do_parse_class(v: &Oop, off: usize, len: usize) -> ClassRef { let rf = v.extract_ref(); let ary = rf.extract_type_array(); let ary = ary.extract_bytes(); match parse_class(&ary[off..(off + len)]) { Ok(r) => { let cfr = Arc::new(Box::new(r.1)); //fixme: setup classloader let class = Class::new_class(cfr, None); ClassPtr::new(class) } Err(e) => unreachable!("e = {:?}", e), } } ================================================ FILE: crates/vm/src/native/java_security_AccessController.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop}; use crate::runtime::{self, exception, thread, JavaCall}; use crate::util; use classfile::consts as cls_consts; pub fn get_native_methods() -> Vec { vec![ new_fn( "doPrivileged", "(Ljava/security/PrivilegedAction;)Ljava/lang/Object;", Box::new(jvm_doPrivileged), ), new_fn( "doPrivileged", "(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;", Box::new(jvm_doPrivileged2), ), new_fn( "getStackAccessControlContext", "()Ljava/security/AccessControlContext;", Box::new(jvm_getStackAccessControlContext), ), new_fn("doPrivileged", "(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", Box::new(jvm_doPrivileged3)), ] } fn jvm_doPrivileged(_env: JNIEnv, args: &[Oop]) -> JNIResult { let v = args.get(0).unwrap(); let mir = { match v { Oop::Null => { let ex = exception::new(cls_consts::J_NPE, None); return Err(ex); } Oop::Ref(v) => { let inst = v.extract_inst(); let cls = inst.class.get_class(); cls.get_virtual_method(&util::S_RUN, &util::S_RUN_SIG) .unwrap() } _ => unreachable!(), } }; let args = vec![v.clone()]; let mut jc = JavaCall::new_with_args(mir, args); let area = runtime::DataArea::new(1); jc.invoke(Some(&area), false); if !thread::is_meet_ex() { let mut stack = area.stack.borrow_mut(); let r = stack.pop_ref(); Ok(Some(r)) } else { Ok(None) } } //todo: re impl fn jvm_doPrivileged2(env: JNIEnv, args: &[Oop]) -> JNIResult { jvm_doPrivileged(env, args) } //todo: re impl fn jvm_doPrivileged3(env: JNIEnv, args: &[Oop]) -> JNIResult { jvm_doPrivileged(env, args) } fn jvm_getStackAccessControlContext(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::Null)) } ================================================ FILE: crates/vm/src/native/java_util_concurrent_atomic_AtomicLong.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![new_fn("VMSupportsCS8", "()Z", Box::new(jvm_VMSupportsCS8))] } fn jvm_VMSupportsCS8(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(0))) } ================================================ FILE: crates/vm/src/native/mod.rs ================================================ #![allow(non_snake_case)] use crate::oop::Oop; use crate::types::ClassRef; use rustc_hash::FxHashMap; use std::sync::{Arc, RwLock}; mod common; mod java_io_FileDescriptor; mod java_io_FileInputStream; mod java_io_FileOutputStream; mod java_io_UnixFileSystem; pub mod java_lang_Class; mod java_lang_ClassLoader; mod java_lang_Double; mod java_lang_Float; mod java_lang_Object; mod java_lang_Runtime; mod java_lang_String; mod java_lang_System; mod java_lang_Thread; mod java_lang_Throwable; mod java_lang_reflect_Array; mod java_lang_reflect_Proxy; mod java_security_AccessController; mod java_util_concurrent_atomic_AtomicLong; mod sun_misc_Signal; mod sun_misc_URLClassPath; mod sun_misc_Unsafe; mod sun_misc_VM; mod sun_nio_cs_StreamEncoder; mod sun_reflect_ConstantPool; mod sun_reflect_NativeConstructorAccessorImpl; mod sun_reflect_NativeMethodAccessorImpl; mod sun_reflect_Reflection; pub type JNIEnv = Arc>>; pub type JNIResult = Result, Oop>; pub type NativeMethodPtr = Box JNIResult + Send + Sync>; pub type JNINativeMethod = Arc; pub struct JNINativeMethodStruct { name: &'static str, signature: &'static str, fnptr: NativeMethodPtr, } pub struct JNIEnvStruct { pub class: ClassRef, } lazy_static! { //(class name, method name, method signature) -> JNINativeMethod static ref NATIVES: FxHashMap<(&'static str, &'static str, &'static str), JNINativeMethod> = { create_native_fn_tables() }; } pub fn new_fn( name: &'static str, signature: &'static str, fnptr: NativeMethodPtr, ) -> JNINativeMethod { Arc::new(JNINativeMethodStruct { name, signature, fnptr, }) } pub fn new_jni_env(class: ClassRef) -> JNIEnv { Arc::new(RwLock::new(Box::new(JNIEnvStruct { class }))) } pub fn find_symbol(package: &[u8], name: &[u8], desc: &[u8]) -> Option { let package = unsafe { std::str::from_utf8_unchecked(package) }; let name = unsafe { std::str::from_utf8_unchecked(name) }; let desc = unsafe { std::str::from_utf8_unchecked(desc) }; let k = (package, name, desc); NATIVES.get(&k).cloned() } pub fn init() { lazy_static::initialize(&NATIVES); java_lang_Class::init(); } impl JNINativeMethodStruct { pub fn invoke(&self, jni: JNIEnv, args: &[Oop]) -> JNIResult { (self.fnptr)(jni, args) } } fn create_native_fn_tables( ) -> FxHashMap<(&'static str, &'static str, &'static str), JNINativeMethod> { let mut dict = FxHashMap::default(); let natives = vec![ ( "java/io/FileDescriptor", java_io_FileDescriptor::get_native_methods(), ), ( "java/io/FileInputStream", java_io_FileInputStream::get_native_methods(), ), ( "java/io/FileOutputStream", java_io_FileOutputStream::get_native_methods(), ), ( "java/io/UnixFileSystem", java_io_UnixFileSystem::get_native_methods(), ), ("java/lang/Class", java_lang_Class::get_native_methods()), ( "java/lang/ClassLoader", java_lang_ClassLoader::get_native_methods(), ), ("java/lang/Double", java_lang_Double::get_native_methods()), ("java/lang/Float", java_lang_Float::get_native_methods()), ("java/lang/Object", java_lang_Object::get_native_methods()), ( "java/lang/reflect/Array", java_lang_reflect_Array::get_native_methods(), ), ( "java/lang/reflect/Proxy", java_lang_reflect_Proxy::get_native_methods(), ), ("java/lang/Runtime", java_lang_Runtime::get_native_methods()), ("java/lang/String", java_lang_String::get_native_methods()), ("java/lang/System", java_lang_System::get_native_methods()), ("java/lang/Thread", java_lang_Thread::get_native_methods()), ( "java/lang/Throwable", java_lang_Throwable::get_native_methods(), ), ( "java/security/AccessController", java_security_AccessController::get_native_methods(), ), ( "java/util/concurrent/atomic/AtomicLong", java_util_concurrent_atomic_AtomicLong::get_native_methods(), ), ("sun/misc/Signal", sun_misc_Signal::get_native_methods()), ("sun/misc/Unsafe", sun_misc_Unsafe::get_native_methods()), ( "sun/misc/URLClassPath", sun_misc_URLClassPath::get_native_methods(), ), ("sun/misc/VM", sun_misc_VM::get_native_methods()), ( "sun/nio/cs/StreamEncoder", sun_nio_cs_StreamEncoder::get_native_methods(), ), ( "sun/reflect/ConstantPool", sun_reflect_ConstantPool::get_native_methods(), ), ( "sun/reflect/NativeConstructorAccessorImpl", sun_reflect_NativeConstructorAccessorImpl::get_native_methods(), ), ( "sun/reflect/NativeMethodAccessorImpl", sun_reflect_NativeMethodAccessorImpl::get_native_methods(), ), ( "sun/reflect/Reflection", sun_reflect_Reflection::get_native_methods(), ), ]; { natives.iter().for_each(|(package, methods)| { methods.iter().for_each(|it| { let k = (*package, it.name, it.signature); dict.insert(k, it.clone()); }); }); } dict } ================================================ FILE: crates/vm/src/native/sun_misc_Signal.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![ new_fn( "findSignal", "(Ljava/lang/String;)I", Box::new(jvm_findSignal), ), new_fn("handle0", "(IJ)J", Box::new(jvm_handle0)), ] } //todo: impl me fn jvm_findSignal(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(1))) } //todo: impl me fn jvm_handle0(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_long(0))) } ================================================ FILE: crates/vm/src/native/sun_misc_URLClassPath.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop}; pub fn get_native_methods() -> Vec { vec![new_fn( "getLookupCacheURLs", "(Ljava/lang/ClassLoader;)[Ljava/net/URL;", Box::new(jvm_getLookupCacheURLs), )] } fn jvm_getLookupCacheURLs(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::Null)) } ================================================ FILE: crates/vm/src/native/sun_misc_Unsafe.rs ================================================ #![allow(non_snake_case)] use crate::native::{java_lang_System, new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop; use crate::oop::{Class, Oop, OopPtr}; use crate::runtime::require_class3; use crate::util; use classfile::flags::ACC_STATIC; use std::os::raw::c_void; use std::time::{Duration, SystemTime, UNIX_EPOCH}; pub fn get_native_methods() -> Vec { vec![ new_fn("registerNatives", "()V", Box::new(jvm_registerNatives)), new_fn( "arrayBaseOffset", "(Ljava/lang/Class;)I", Box::new(jvm_arrayBaseOffset), ), new_fn( "arrayIndexScale", "(Ljava/lang/Class;)I", Box::new(jvm_arrayIndexScale), ), new_fn("addressSize", "()I", Box::new(jvm_addressSize)), new_fn( "objectFieldOffset", "(Ljava/lang/reflect/Field;)J", Box::new(jvm_objectFieldOffset), ), new_fn( "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", Box::new(jvm_compareAndSwapObject), ), new_fn( "getIntVolatile", "(Ljava/lang/Object;J)I", Box::new(jvm_getIntVolatile), ), new_fn( "compareAndSwapInt", "(Ljava/lang/Object;JII)Z", Box::new(jvm_compareAndSwapInt), ), new_fn("allocateMemory", "(J)J", Box::new(jvm_allocateMemory)), new_fn("freeMemory", "(J)V", Box::new(jvm_freeMemory)), new_fn("putLong", "(JJ)V", Box::new(jvm_putLong)), new_fn("getByte", "(J)B", Box::new(jvm_getByte)), new_fn( "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z", Box::new(jvm_compareAndSwapLong), ), new_fn( "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;", Box::new(jvm_getObjectVolatile), ), new_fn("pageSize", "()I", Box::new(jvm_pageSize)), new_fn( "getLongVolatile", "(Ljava/lang/Object;J)J", Box::new(jvm_getLongVolatile), ), new_fn( "setMemory", "(Ljava/lang/Object;JJB)V", Box::new(jvm_setMemory), ), new_fn("putChar", "(JC)V", Box::new(jvm_putChar)), new_fn( "copyMemory", "(Ljava/lang/Object;JLjava/lang/Object;JJ)V", Box::new(jvm_copyMemory), ), new_fn("getChar", "(J)C", Box::new(jvm_getChar)), new_fn( "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V", Box::new(jvm_putObject), ), new_fn( "ensureClassInitialized", "(Ljava/lang/Class;)V", Box::new(jvm_ensureClassInitialized), ), new_fn( "staticFieldOffset", "(Ljava/lang/reflect/Field;)J", Box::new(jvm_staticFieldOffset), ), new_fn( "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", Box::new(jvm_staticFieldBase), ), new_fn("putByte", "(Ljava/lang/Object;JB)V", Box::new(jvm_putByte)), new_fn("getByte", "(Ljava/lang/Object;J)B", Box::new(jvm_getByte2)), new_fn("park", "(ZJ)V", Box::new(jvm_park)), ] } fn jvm_registerNatives(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } fn jvm_arrayBaseOffset(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(0))) } fn jvm_arrayIndexScale(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(1))) } fn jvm_addressSize(_env: JNIEnv, _args: &[Oop]) -> JNIResult { let v = std::mem::size_of::<*mut u8>(); Ok(Some(Oop::new_int(v as i32))) } fn jvm_objectFieldOffset(_env: JNIEnv, args: &[Oop]) -> JNIResult { let field = args.get(1).unwrap(); objectFieldOffset(field, false) } // fixme: The semantic requirement here is atomic operation, which needs to be re-implemented here fn jvm_compareAndSwapObject(_env: JNIEnv, args: &[Oop]) -> JNIResult { let owner = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long(); let old_data = args.get(3).unwrap(); let new_data = args.get(4).unwrap(); let v_at_offset = Class::get_field_value2(owner.extract_ref(), offset as usize); if OopPtr::is_eq(&v_at_offset, old_data) { Class::put_field_value2(owner.extract_ref(), offset as usize, new_data.clone()); Ok(Some(Oop::new_int(1))) } else { Ok(Some(Oop::new_int(0))) } } fn jvm_getIntVolatile(_env: JNIEnv, args: &[Oop]) -> JNIResult { let owner = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long(); let v = Class::get_field_value2(owner.extract_ref(), offset as usize); Ok(Some(v)) } fn jvm_compareAndSwapInt(_env: JNIEnv, args: &[Oop]) -> JNIResult { let owner = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long(); let old_data = args.get(3).unwrap().extract_int(); let new_data = args.get(4).unwrap(); let v_at_offset = { let v = Class::get_field_value2(owner.extract_ref(), offset as usize); v.extract_int() }; if v_at_offset == old_data { Class::put_field_value2(owner.extract_ref(), offset as usize, new_data.clone()); Ok(Some(Oop::new_int(1))) } else { Ok(Some(Oop::new_int(0))) } } fn jvm_allocateMemory(_env: JNIEnv, args: &[Oop]) -> JNIResult { let size = args.get(1).unwrap().extract_long() as usize; let arr = unsafe { libc::malloc(std::mem::size_of::() * size) }; let v = arr as i64; Ok(Some(Oop::new_long(v))) } fn jvm_freeMemory(_env: JNIEnv, args: &[Oop]) -> JNIResult { let ptr = args.get(1).unwrap().extract_long() as *mut libc::c_void; unsafe { libc::free(ptr); } Ok(None) } fn jvm_putLong(_env: JNIEnv, args: &[Oop]) -> JNIResult { let ptr = args.get(1).unwrap().extract_long() as *mut libc::c_void; let l = args.get(2).unwrap().extract_long(); let v = l.to_be_bytes(); unsafe { libc::memcpy(ptr, v.as_ptr() as *const c_void, 8); } Ok(None) } fn jvm_getByte(_env: JNIEnv, args: &[Oop]) -> JNIResult { let ptr = args.get(1).unwrap().extract_long() as *const u8; let v = unsafe { *ptr }; Ok(Some(Oop::new_int(v as i32))) } fn jvm_compareAndSwapLong(_env: JNIEnv, args: &[Oop]) -> JNIResult { let owner = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long(); let old_data = args.get(3).unwrap().extract_long(); let new_data = args.get(4).unwrap(); let v_at_offset = { let v = Class::get_field_value2(owner.extract_ref(), offset as usize); v.extract_long() }; if v_at_offset == old_data { Class::put_field_value2(owner.extract_ref(), offset as usize, new_data.clone()); Ok(Some(Oop::new_int(1))) } else { Ok(Some(Oop::new_int(0))) } } fn jvm_getObjectVolatile(_env: JNIEnv, args: &[Oop]) -> JNIResult { let owner = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long(); let v_at_offset = Class::get_field_value2(owner.extract_ref(), offset as usize); Ok(Some(v_at_offset)) } fn jvm_pageSize(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(Some(Oop::new_int(4 * 1024))) } fn jvm_getLongVolatile(_env: JNIEnv, args: &[Oop]) -> JNIResult { let owner = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long(); let v_at_offset = Class::get_field_value2(owner.extract_ref(), offset as usize); Ok(Some(v_at_offset)) } fn jvm_setMemory(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let obj = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long() as usize; let size = args.get(3).unwrap().extract_long() as usize; let value = args.get(4).unwrap().extract_int(); match obj { Oop::Null => { let dest = offset as *mut libc::c_void; unsafe { libc::memset(dest, value, size as usize); } } Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let bytes = ary.extract_mut_bytes(); unsafe { let addr = bytes.as_mut_ptr(); let addr = addr.add(offset); std::ptr::write_bytes(addr, value as u8, size); } } _ => unimplemented!(), } Ok(None) } fn jvm_putChar(_env: JNIEnv, args: &[Oop]) -> JNIResult { let dest = args.get(1).unwrap().extract_long() as *mut libc::c_void; let value = args.get(2).unwrap().extract_int(); unsafe { libc::memset(dest, value, 1); } Ok(None) } fn jvm_copyMemory(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let src_obj = args.get(1).unwrap(); let src_offset = args.get(2).unwrap().extract_long() as usize; let dest_obj = args.get(3).unwrap(); let dest_offset = args.get(4).unwrap().extract_long() as usize; let size = args.get(5).unwrap().extract_long() as usize; match src_obj { Oop::Null => { match dest_obj { //raw -> raw Oop::Null => { let src_ptr = src_offset as *const u8; let dest_ptr = dest_offset as *mut u8; unsafe { std::ptr::copy(src_ptr, dest_ptr, size); } } //raw -> byte[] Oop::Ref(dest) => { let dest = dest.extract_mut_type_array(); let dest = dest.extract_mut_bytes(); let dest = &mut dest[dest_offset..]; let src_ptr = src_offset as *const u8; unsafe { for i in 0..size { let p = src_ptr.add(i); dest[i] = *p; } } } _ => unimplemented!(), } } Oop::Ref(src) => { match dest_obj { //byte[] -> raw Oop::Null => { let src = src.extract_type_array(); let src = src.extract_bytes(); let ptr = dest_offset as *mut u8; unsafe { for i in 0..size { let p = ptr.add(i); *p = src[src_offset + i]; } } } //byte[] -> byte[] Oop::Ref(dest) => { java_lang_System::arraycopy_diff_obj( src.clone(), src_offset, dest.clone(), dest_offset, size, ); } _ => unimplemented!(), } } _ => unreachable!(), } Ok(None) } fn jvm_getChar(_env: JNIEnv, args: &[Oop]) -> JNIResult { let arg1 = args.get(1).unwrap(); let ptr = arg1.extract_long() as *const u16; let v = unsafe { *ptr }; Ok(Some(Oop::new_int(v as i32))) } fn jvm_putObject(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let o = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long() as usize; let x = args.get(3).unwrap(); let rf = o.extract_ref(); Class::put_field_value2(rf, offset, x.clone()); Ok(None) } fn jvm_ensureClassInitialized(_env: JNIEnv, args: &[Oop]) -> JNIResult { let clazz = args.get(1).unwrap(); let rf = clazz.extract_ref(); let mirror = rf.extract_mirror(); let target = mirror.target.clone().unwrap(); oop::class::init_class(&target); oop::class::init_class_fully(&target); Ok(None) } fn jvm_staticFieldOffset(_env: JNIEnv, args: &[Oop]) -> JNIResult { let field = args.get(1).unwrap(); objectFieldOffset(field, true) } fn jvm_staticFieldBase(_env: JNIEnv, args: &[Oop]) -> JNIResult { let field = args.get(1).unwrap(); let cls = require_class3(None, b"java/lang/reflect/Field").unwrap(); let cls = cls.get_class(); let id = cls.get_field_id(&util::S_CLAZZ, &util::S_JAVA_LANG_CLASS, false); let v = Class::get_field_value(field.extract_ref(), id); Ok(Some(v)) } fn jvm_putByte(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let obj = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long() as usize; let x = args.get(3).unwrap().extract_int(); match obj { Oop::Null => { let dest = offset as *mut libc::c_void; unsafe { libc::memset(dest, x, 1); } } Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let bytes = ary.extract_mut_bytes(); bytes[offset] = x as u8; } t => unimplemented!("{:?}", t), } Ok(None) } fn jvm_getByte2(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let obj = args.get(1).unwrap(); let offset = args.get(2).unwrap().extract_long() as usize; let v = match obj { Oop::Null => { let ptr = offset as *const u8; let v = unsafe { *ptr }; Oop::new_int(v as i32) } Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let bytes = ary.extract_bytes(); let v = bytes[offset]; Oop::new_int(v as i32) } t => unimplemented!("{:?}", t), }; Ok(Some(v)) } fn jvm_park(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let is_absolute = args.get(1).unwrap().extract_int() != 0; let time = args.get(2).unwrap().extract_long() as u64; if is_absolute { let epoch_duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let diff = Duration::from_millis(time) - epoch_duration; std::thread::park_timeout(diff); } else { if time != 0 { std::thread::park_timeout(Duration::from_nanos(time)); } else { std::thread::park(); } } Ok(None) } ////////helper fn objectFieldOffset(field: &Oop, is_static: bool) -> JNIResult { let cls = require_class3(None, b"java/lang/reflect/Field").unwrap(); if is_static { let modifier = { let cls = cls.get_class(); let id = cls.get_field_id(&util::S_MODIFIERS, &util::S_I, false); let v = Class::get_field_value(field.extract_ref(), id); v.extract_int() as u16 }; assert_eq!(modifier & ACC_STATIC, ACC_STATIC); } let slot = { let cls = cls.get_class(); let id = cls.get_field_id(&util::S_SLOT, &util::S_I, false); let v = Class::get_field_value(field.extract_ref(), id); v.extract_int() }; Ok(Some(Oop::new_long(slot as i64))) } ================================================ FILE: crates/vm/src/native/sun_misc_VM.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; pub fn get_native_methods() -> Vec { vec![new_fn("initialize", "()V", Box::new(jvm_initialize))] } fn jvm_initialize(_env: JNIEnv, _args: &[Oop]) -> JNIResult { Ok(None) } ================================================ FILE: crates/vm/src/native/sun_nio_cs_StreamEncoder.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::new_br; use crate::oop::Oop; use crate::runtime::{self, require_class3}; pub fn get_native_methods() -> Vec { vec![new_fn( "forOutputStreamWriter", "(Ljava/io/OutputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamEncoder;", Box::new(jvm_forOutputStreamWriter), )] } fn jvm_forOutputStreamWriter(_env: JNIEnv, args: &[Oop]) -> JNIResult { let os = args.get(0).unwrap(); let obj = args.get(1).unwrap(); let _charset_name = args.get(2).unwrap(); let charset_cls = require_class3(None, b"java/nio/charset/Charset").unwrap(); let default_charset_oop = { let cls = charset_cls.get_class(); let id = cls.get_field_id( &new_br("defaultCharset"), &new_br("Ljava/nio/charset/Charset;"), true, ); cls.get_static_field_value(id) }; //check defaultCharset match default_charset_oop { Oop::Ref(_) => (), _ => unreachable!(), } let encoder = require_class3(None, b"sun/nio/cs/StreamEncoder").unwrap(); let encoder_oop = Oop::new_inst(encoder.clone()); let args = vec![ encoder_oop.clone(), os.clone(), obj.clone(), default_charset_oop, ]; runtime::invoke::invoke_ctor( encoder, new_br("(Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)V"), args, ); Ok(Some(encoder_oop)) } ================================================ FILE: crates/vm/src/native/sun_reflect_ConstantPool.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::{self, Oop}; use crate::util; use classfile::constant_pool; pub fn get_native_methods() -> Vec { vec![new_fn( "getUTF8At0", "(Ljava/lang/Object;I)Ljava/lang/String;", Box::new(jvm_getUTF8At0), )] } fn jvm_getUTF8At0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let _this = args.get(0).unwrap(); let cp_oop = args.get(1).unwrap(); let index = { let index = args.get(2).unwrap(); index.extract_int() }; let rf = cp_oop.extract_ref(); let mirror = rf.extract_mirror(); let target = mirror.target.clone().unwrap(); let cls = target.get_class(); match &cls.kind { oop::class::ClassKind::Instance(inst) => { let cp = &inst.class_file.cp; let s = constant_pool::get_utf8(cp, index as usize); let s = unsafe { std::str::from_utf8_unchecked(s.as_slice()) }; let r = util::oop::new_java_lang_string2(s); Ok(Some(r)) } _ => unimplemented!(), } } ================================================ FILE: crates/vm/src/native/sun_reflect_NativeConstructorAccessorImpl.rs ================================================ #![allow(non_snake_case)] use crate::native::{common, new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; use crate::{new_br, runtime}; pub fn get_native_methods() -> Vec { vec![new_fn( "newInstance0", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;", Box::new(jvm_newInstance0), )] } fn jvm_newInstance0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let ctor = args.get(0).unwrap(); let arguments = args.get(1).unwrap(); let clazz = common::reflect::get_Constructor_clazz(ctor); let target_cls = { let rf = clazz.extract_ref(); let mirror = rf.extract_mirror(); mirror.target.clone().unwrap() }; let signature = common::reflect::get_Constructor_signature(ctor); let cls = target_cls.get_class(); let name = unsafe { std::str::from_utf8_unchecked(cls.name.as_slice()) }; info!("newInstance0 {}:{}", name, signature); let mut ctor_args = Vec::new(); { match arguments { Oop::Null => (), Oop::Ref(rf) => { let ary = rf.extract_array(); ctor_args.extend_from_slice(ary.elements.as_slice()); } _ => unreachable!(), } } let oop = Oop::new_inst(target_cls.clone()); ctor_args.insert(0, oop.clone()); runtime::invoke::invoke_ctor(target_cls, new_br(signature.as_str()), ctor_args); Ok(Some(oop)) } ================================================ FILE: crates/vm/src/native/sun_reflect_NativeMethodAccessorImpl.rs ================================================ #![allow(non_snake_case)] use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::new_br; use crate::oop::{self, Class, Oop, OopPtr}; use crate::runtime::{self, require_class3}; use crate::util; use classfile::{consts as cls_consts, SignatureType}; pub fn get_native_methods() -> Vec { vec![new_fn( "invoke0", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", Box::new(jvm_invoke0), )] } fn jvm_invoke0(_env: JNIEnv, args: &[Oop]) -> JNIResult { let method = args.get(0).unwrap(); let obj = args.get(1).unwrap(); let args = args.get(2).unwrap(); let cls = require_class3(None, cls_consts::J_METHOD).unwrap(); let (m_clazz, m_name, m_signature) = { let cls = cls.get_class(); let fid = cls.get_field_id(&util::S_CLAZZ, &util::S_JAVA_LANG_CLASS, false); let method_clazz = Class::get_field_value(method.extract_ref(), fid); let fid = cls.get_field_id(&util::S_NAME, &util::S_JAVA_LANG_STRING, false); let method_name = Class::get_field_value(method.extract_ref(), fid); let method_name = OopPtr::java_lang_string(method_name.extract_ref()); let method_name = new_br(method_name.as_str()); let fid = cls.get_field_id(&util::S_SIGNATURE, &util::S_JAVA_LANG_STRING, false); let signature = Class::get_field_value(method.extract_ref(), fid); let signature = OopPtr::java_lang_string(signature.extract_ref()); let signature = new_br(signature.as_str()); (method_clazz, method_name, signature) }; let clz = { let rf = m_clazz.extract_ref(); let mirror = rf.extract_mirror(); mirror.target.clone().unwrap() }; let mir = { let clz = clz.get_class(); clz.get_class_method(&m_name, &m_signature).unwrap() }; // { // let cls = clz.read().unwrap(); // let cls_name = cls.name.clone(); // error!( // "invoke0 {}:{}:{} static={}, native={}", // String::from_utf8_lossy(cls_name.as_slice()), // String::from_utf8_lossy(mir.method.name.as_slice()), // String::from_utf8_lossy(mir.method.desc.as_slice()), // mir.method.is_static(), // mir.method.is_native(), // ); // } let mut args = { let rf = args.extract_ref(); let ary = rf.extract_array(); ary.elements.to_vec() }; if !mir.method.is_static() { args.insert(0, obj.clone()); } let force_no_resolve = mir.method.name.as_slice() == b"" || mir.method.is_static(); let mut jc = runtime::invoke::JavaCall::new_with_args(mir, args); let area = runtime::DataArea::new(0); jc.invoke(Some(&area), force_no_resolve); let r = { let mut stack = area.stack.borrow_mut(); match jc.mir.method.signature.retype { SignatureType::Byte | SignatureType::Char | SignatureType::Boolean | SignatureType::Short | SignatureType::Int => { let v = stack.pop_int(); Some(oop::Oop::new_int(v)) } SignatureType::Double => { let v = stack.pop_double(); Some(oop::Oop::new_double(v)) } SignatureType::Float => { let v = stack.pop_float(); Some(oop::Oop::new_float(v)) } SignatureType::Long => { let v = stack.pop_long(); Some(oop::Oop::new_long(v)) } SignatureType::Object(_, _, _) | SignatureType::Array(_) => Some(stack.pop_ref()), SignatureType::Void => Some(Oop::Null), } }; Ok(r) } ================================================ FILE: crates/vm/src/native/sun_reflect_Reflection.rs ================================================ #![allow(non_snake_case)] use crate::native::{self, new_fn, JNIEnv, JNINativeMethod, JNIResult}; use crate::oop::Oop; use crate::runtime; pub fn get_native_methods() -> Vec { vec![ new_fn( "getCallerClass", "()Ljava/lang/Class;", Box::new(jvm_getCallerClass), ), new_fn( "getClassAccessFlags", "(Ljava/lang/Class;)I", Box::new(jvm_getClassAccessFlags), ), ] } fn jvm_getCallerClass(_env: JNIEnv, _args: &[Oop]) -> JNIResult { let jt = runtime::thread::current_java_thread(); let mut callers = { jt.read().unwrap().frames.clone() }; { let cur = { let cur = callers.pop().unwrap(); //pop cur method let cur = cur.try_read().unwrap(); cur.mir.clone() }; debug_assert_eq!(cur.method.name.as_slice(), b"getCallerClass"); } loop { let caller = { let caller = callers.pop().unwrap(); let caller = caller.try_read().unwrap(); caller.mir.clone() }; if caller .method .check_annotation(b"Lsun/reflect/CallerSensitive;") { continue; } let cls = caller.method.class.get_class(); // error!("getCallerClass name = {}", String::from_utf8_lossy(cls.name.as_slice())); return Ok(Some(cls.get_mirror())); } } fn jvm_getClassAccessFlags(env: JNIEnv, args: &[Oop]) -> JNIResult { native::java_lang_Class::jvm_getModifiers(env, args) } ================================================ FILE: crates/vm/src/oop/ary.rs ================================================ use crate::oop::{class, Oop}; use crate::types::*; #[derive(Debug, Clone)] pub struct ArrayOopDesc { pub class: ClassRef, pub elements: Vec, } #[derive(Debug, Clone)] pub enum TypeArrayDesc { Byte(ByteAry), Bool(BoolAry), Char(CharAry), Short(ShortAry), Float(FloatAry), Double(DoubleAry), Int(IntAry), Long(LongAry), } pub enum TypeArrayEnum { Boolean, Char, Float, Double, Byte, Short, Int, Long, } impl ArrayOopDesc { pub fn new(class: ClassRef, elements: Vec) -> Self { { debug_assert!(class.get_class().is_array()); } Self { class, elements } } pub fn get_dimension(&self) -> usize { let class = self.class.get_class(); match &class.kind { class::ClassKind::ObjectArray(ary_class_obj) => ary_class_obj.get_dimension(), class::ClassKind::TypeArray(ary_class_obj) => ary_class_obj.get_dimension(), _ => unreachable!(), } } } impl TypeArrayDesc { pub fn len(&self) -> usize { match self { TypeArrayDesc::Char(ary) => ary.len(), TypeArrayDesc::Byte(ary) => ary.len(), TypeArrayDesc::Bool(ary) => ary.len(), TypeArrayDesc::Short(ary) => ary.len(), TypeArrayDesc::Float(ary) => ary.len(), TypeArrayDesc::Double(ary) => ary.len(), TypeArrayDesc::Int(ary) => ary.len(), TypeArrayDesc::Long(ary) => ary.len(), } } } impl TypeArrayDesc { pub fn extract_chars(&self) -> &CharAry { match self { TypeArrayDesc::Char(v) => v, _ => unreachable!(), } } pub fn extract_mut_chars(&mut self) -> &mut CharAry { match self { TypeArrayDesc::Char(v) => v, _ => unreachable!(), } } pub fn extract_bytes(&self) -> &ByteAry { match self { TypeArrayDesc::Byte(v) => v, _ => unreachable!(), } } pub fn extract_mut_bytes(&mut self) -> &mut ByteAry { match self { TypeArrayDesc::Byte(v) => v, _ => unreachable!(), } } pub fn extract_bools(&self) -> &BoolAry { match self { TypeArrayDesc::Bool(v) => v, _ => unreachable!(), } } pub fn extract_mut_bools(&mut self) -> &mut BoolAry { match self { TypeArrayDesc::Bool(v) => v, _ => unreachable!(), } } pub fn extract_shorts(&self) -> &ShortAry { match self { TypeArrayDesc::Short(v) => v, _ => unreachable!(), } } pub fn extract_mut_shorts(&mut self) -> &mut ShortAry { match self { TypeArrayDesc::Short(v) => v, _ => unreachable!(), } } pub fn extract_floats(&self) -> &FloatAry { match self { TypeArrayDesc::Float(v) => v, _ => unreachable!(), } } pub fn extract_mut_floats(&mut self) -> &mut FloatAry { match self { TypeArrayDesc::Float(v) => v, _ => unreachable!(), } } pub fn extract_doubles(&self) -> &DoubleAry { match self { TypeArrayDesc::Double(v) => v, _ => unreachable!(), } } pub fn extract_mut_doubles(&mut self) -> &mut DoubleAry { match self { TypeArrayDesc::Double(v) => v, _ => unreachable!(), } } pub fn extract_ints(&self) -> &IntAry { match self { TypeArrayDesc::Int(v) => v, _ => unreachable!(), } } pub fn extract_mut_ints(&mut self) -> &mut IntAry { match self { TypeArrayDesc::Int(v) => v, _ => unreachable!(), } } pub fn extract_longs(&self) -> &LongAry { match self { TypeArrayDesc::Long(v) => v, _ => unreachable!(), } } pub fn extract_mut_longs(&mut self) -> &mut LongAry { match self { TypeArrayDesc::Long(v) => v, _ => unreachable!(), } } } impl From for TypeArrayEnum { fn from(b: u8) -> Self { match b { 4 => Self::Boolean, 5 => Self::Char, 6 => Self::Float, 7 => Self::Double, 8 => Self::Byte, 9 => Self::Short, 10 => Self::Int, 11 => Self::Long, _ => unreachable!(), } } } ================================================ FILE: crates/vm/src/oop/class.rs ================================================ use std::fmt::{self, Debug, Error, Formatter}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use rustc_hash::FxHashMap; use classfile::{ attributes::EnclosingMethod, attributes::InnerClass, constant_pool, constant_pool::get_utf8 as get_cp_utf8, consts, flags::*, AttributeType, BytesRef, U2, }; use crate::oop::{self, consts as oop_consts, field, Oop, OopPtr, RefKindDesc, ValueType}; use crate::runtime::method::MethodId; use crate::runtime::thread::ReentrantMutex; use crate::runtime::{ self, method, require_class2, ClassLoader, ConstantPoolCache, JavaCall, JavaThread, }; use crate::types::*; use crate::{native, util}; pub struct ClassPtr(u64); impl ClassPtr { pub fn new(v: Class) -> ClassRef { let v = Box::new(v); let ptr = Box::into_raw(v) as u64; Arc::new(ClassPtr(ptr)) } } impl Drop for ClassPtr { fn drop(&mut self) { let _v = unsafe { Box::from_raw(self.0 as *mut Class) }; } } impl ClassPtr { fn raw_ptr(&self) -> *const Class { self.0 as *const Class } fn raw_mut_ptr(&self) -> *mut Class { self.0 as *mut Class } } impl ClassPtr { pub fn name(&self) -> BytesRef { let ptr = self.raw_ptr(); unsafe { (*ptr).name.clone() } } pub fn get_class(&self) -> &Class { let ptr = self.raw_ptr(); unsafe { &(*ptr) } } pub fn get_mut_class(&self) -> &mut Class { let ptr = self.raw_mut_ptr(); unsafe { &mut (*ptr) } } pub fn extract_inst(&self) -> &ClassObject { let class = self.get_class(); match &class.kind { oop::ClassKind::Instance(cls_obj) => cls_obj, _ => unreachable!(), } } } impl Debug for ClassPtr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let cls = self.get_class(); let cls_name = unsafe { std::str::from_utf8_unchecked(cls.name.as_slice()) }; let cls_name = cls_name.to_string(); let cls_kind_type = format!("{:?}", cls.get_class_kind_type()); let cls_state = format!("{:?}", cls.get_class_state()); f.debug_struct("ClassPtr") .field("name", &cls_name) .field("state", &cls_state) .field("kind", &cls_kind_type) .finish() } } ///////////////////////////////////////////// pub struct Class { clinit_mutex: Arc>, mutex: ReentrantMutex, state: std::sync::atomic::AtomicU8, pub name: BytesRef, pub acc_flags: U2, // None for java.lang.Object pub super_class: Option, // None for the "bootstrap" loader pub class_loader: Option, pub kind: ClassKind, } pub enum ClassKind { Instance(ClassObject), ObjectArray(ArrayClassObject), TypeArray(ArrayClassObject), } #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] pub enum ClassKindType { Instance, ObjectAry, TypAry, } #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] pub enum State { Allocated, Loaded, Linked, BeingIni, FullyIni, IniErr, } impl From for State { fn from(v: u8) -> Self { match v { 0 => State::Allocated, 1 => State::Loaded, 2 => State::Linked, 3 => State::BeingIni, 4 => State::FullyIni, 5 => State::IniErr, _ => unreachable!(), } } } impl Into for State { fn into(self) -> u8 { match self { State::Allocated => 0, State::Loaded => 1, State::Linked => 2, State::BeingIni => 3, State::FullyIni => 4, State::IniErr => 5, } } } pub struct ClassObject { pub class_file: ClassFileRef, pub n_inst_fields: usize, // FxHashMap<(name, desc), MethodIdRef> pub all_methods: FxHashMap<(BytesRef, BytesRef), MethodIdRef>, v_table: FxHashMap<(BytesRef, BytesRef), MethodIdRef>, // FxHashMap<(package, name, desc), FieldIdRef> pub static_fields: FxHashMap<(BytesRef, BytesRef, BytesRef), FieldIdRef>, pub inst_fields: FxHashMap<(BytesRef, BytesRef, BytesRef), FieldIdRef>, static_field_values: Vec, interfaces: FxHashMap, mirror: Option, pub signature: Option, pub source_file: Option, pub enclosing_method: Option, pub inner_classes: Option>, pub cp_cache: ConstantPoolCache, } pub struct ArrayClassObject { pub value_type: ValueType, //valid when dimension > 1 pub down_type: Option, //valid when it's not TypeArray pub component: Option, pub mirror: Option, } pub fn init_class(class: &ClassRef) { let need = { class.get_class().get_class_state() == State::Linked }; if need { let mut cls = class.get_mut_class(); let clinit_mutex = cls.clinit_mutex.clone(); let l = clinit_mutex.lock().unwrap(); cls.set_class_state(State::BeingIni); if let Some(super_class) = &cls.super_class { init_class(super_class); init_class_fully(super_class); } match &mut cls.kind { ClassKind::Instance(class_obj) => { class_obj.init_static_fields(); } _ => cls.set_class_state(State::FullyIni), } } } //invoke "" pub fn init_class_fully(class: &ClassRef) { let need = { class.get_class().get_class_state() == State::BeingIni }; if need { let l = class.get_class().clinit_mutex.lock(); let (mir, name) = { let mut class = class.get_mut_class(); class.set_class_state(State::FullyIni); let mir = class.get_this_class_method(&util::S_CLINIT, &util::S_CLINIT_SIG); (mir, class.name.clone()) }; if let Ok(mir) = mir { info!("call {}:", unsafe { std::str::from_utf8_unchecked(name.as_slice()) }); let mut jc = JavaCall::new_with_args(mir, vec![]); jc.invoke(None, true); } } } pub fn load_and_init(name: &[u8]) -> ClassRef { // trace!("load_and_init 1 name={}", String::from_utf8_lossy(name)); let cls_name = unsafe { std::str::from_utf8_unchecked(name) }; let class = runtime::require_class3(None, name) .unwrap_or_else(|| panic!("Class not found: {}", cls_name)); init_class(&class); init_class_fully(&class); class } impl Class { pub fn get_class_state(&self) -> State { let v = self.state.load(Ordering::Relaxed); State::from(v) } pub fn set_class_state(&mut self, s: State) { self.state.store(s.into(), Ordering::Relaxed); } pub fn get_name(&self) -> BytesRef { self.name.clone() } pub fn get_super_class(&self) -> Option { self.super_class.clone() } pub fn is_public(&self) -> bool { (self.acc_flags & ACC_PUBLIC) == ACC_PUBLIC } pub fn is_private(&self) -> bool { (self.acc_flags & ACC_PRIVATE) == ACC_PRIVATE } pub fn is_protected(&self) -> bool { (self.acc_flags & ACC_PROTECTED) == ACC_PROTECTED } pub fn is_final(&self) -> bool { (self.acc_flags & ACC_FINAL) == ACC_FINAL } pub fn is_static(&self) -> bool { (self.acc_flags & ACC_STATIC) == ACC_STATIC } pub fn is_abstract(&self) -> bool { (self.acc_flags & ACC_ABSTRACT) == ACC_ABSTRACT } pub fn is_interface(&self) -> bool { (self.acc_flags & ACC_INTERFACE) == ACC_INTERFACE } pub fn monitor_enter(&self) { unsafe { self.mutex.lock(); } } pub fn monitor_exit(&self) { unsafe { self.mutex.unlock(); } } pub fn link_class(&mut self, self_ref: ClassRef) { match &mut self.kind { ClassKind::Instance(class_obj) => { self.super_class = class_obj.link_super_class(self.name.clone(), self.class_loader); let n = match &self.super_class { Some(super_cls) => { let super_cls = super_cls.get_class(); match &super_cls.kind { ClassKind::Instance(cls) => cls.n_inst_fields, _ => 0, } } None => 0, }; class_obj.link_fields(self_ref.clone(), self.name.clone(), n); class_obj.link_interfaces(); class_obj.link_methods(self_ref, self.name.clone()); class_obj.link_attributes(); } ClassKind::ObjectArray(ary_class_obj) => { let super_class = runtime::require_class3(None, consts::J_OBJECT).unwrap(); self.super_class = Some(super_class); } ClassKind::TypeArray(ary_class_obj) => { let super_class = runtime::require_class3(None, consts::J_OBJECT).unwrap(); self.super_class = Some(super_class); } } self.set_class_state(State::Linked); } pub fn get_class_kind_type(&self) -> ClassKindType { match &self.kind { ClassKind::Instance(_) => ClassKindType::Instance, ClassKind::ObjectArray(_) => ClassKindType::ObjectAry, ClassKind::TypeArray(_) => ClassKindType::TypAry, } } pub fn is_instance(&self) -> bool { match &self.kind { ClassKind::Instance(_) => true, _ => false, } } pub fn is_array(&self) -> bool { match &self.kind { ClassKind::Instance(_) => false, _ => true, } } pub fn is_object_ary(&self) -> bool { match &self.kind { ClassKind::Instance(_) => false, ClassKind::TypeArray(_) => false, ClassKind::ObjectArray(_) => true, } } pub fn get_mirror(&self) -> Oop { match &self.kind { ClassKind::Instance(cls_obj) => cls_obj.mirror.clone().unwrap(), //[J ClassKind::TypeArray(typ_ary) => typ_ary.mirror.clone().unwrap(), //[Ljava/lang/Object; ClassKind::ObjectArray(obj_ary) => obj_ary.mirror.clone().unwrap(), _ => unreachable!(), } } pub fn set_mirror(&mut self, mirror: Oop) { match &mut self.kind { ClassKind::Instance(cls_obj) => cls_obj.mirror = Some(mirror), ClassKind::ObjectArray(obj_ary) => obj_ary.mirror = Some(mirror), ClassKind::TypeArray(typ_ary) => typ_ary.mirror = Some(mirror), } } pub fn get_source_file(&self) -> Option { match &self.kind { ClassKind::Instance(cls_obj) => cls_obj.source_file.clone(), _ => unreachable!(), } } pub fn get_annotation(&self) -> Option> { match &self.kind { ClassKind::Instance(cls) => { util::attributes::assemble_annotation(&cls.class_file.attrs) } _ => unreachable!(), } } pub fn get_type_annotation(&self) -> Option> { match &self.kind { ClassKind::Instance(cls) => { util::attributes::assemble_type_annotation(&cls.class_file.attrs) } _ => unreachable!(), } } pub fn get_attr_signatrue(&self) -> Option<&BytesRef> { match &self.kind { ClassKind::Instance(cls) => { let idx = util::attributes::get_signature(&cls.class_file.attrs); if idx != 0 { let cp = &cls.class_file.cp; let s = get_cp_utf8(cp, idx as usize); Some(s) } else { None } } _ => unreachable!(), } } } impl ArrayClassObject { pub fn get_dimension(&self) -> usize { match self.down_type.as_ref() { Some(down_type) => { let down_type = down_type.get_class(); let n = match &down_type.kind { ClassKind::Instance(_) => unreachable!(), ClassKind::ObjectArray(ary_cls_obj) => ary_cls_obj.get_dimension(), ClassKind::TypeArray(ary_cls_obj) => ary_cls_obj.get_dimension(), }; 1 + n } None => 1, } } } //open api impl Class { //todo: confirm static method pub fn get_static_method(&self, name: &BytesRef, desc: &BytesRef) -> Result { self.get_class_method_inner(name, desc, true) } pub fn get_class_method(&self, name: &BytesRef, desc: &BytesRef) -> Result { self.get_class_method_inner(name, desc, true) } pub fn get_this_class_method( &self, name: &BytesRef, desc: &BytesRef, ) -> Result { self.get_class_method_inner(name, desc, false) } pub fn get_virtual_method(&self, name: &BytesRef, desc: &BytesRef) -> Result { self.get_virtual_method_inner(name, desc) } pub fn get_interface_method( &self, name: &BytesRef, desc: &BytesRef, ) -> Result { self.get_interface_method_inner(name, desc) } pub fn get_field_id(&self, name: &BytesRef, desc: &BytesRef, is_static: bool) -> FieldIdRef { let k = (self.name.clone(), name.clone(), desc.clone()); if is_static { match &self.kind { ClassKind::Instance(cls_obj) => { if let Some(fid) = cls_obj.static_fields.get(&k) { return fid.clone(); } } _ => unreachable!(), } } else { match &self.kind { ClassKind::Instance(cls_obj) => { if let Some(fid) = cls_obj.inst_fields.get(&k) { return fid.clone(); } } _ => unreachable!(), } } let super_class = self.super_class.clone(); super_class .unwrap() .get_class() .get_field_id(name, desc, is_static) } pub fn put_field_value(rf: Arc, fir: FieldIdRef, v: Oop) { Self::put_field_value2(rf, fir.offset, v); } pub fn put_field_value2(rf: Arc, offset: usize, v: Oop) { let ptr = rf.get_mut_raw_ptr(); unsafe { match &mut (*ptr).v { oop::RefKind::Inst(inst) => inst.field_values[offset] = v, oop::RefKind::Mirror(mirror) => mirror.field_values[offset] = v, oop::RefKind::Array(ary) => ary.elements[offset] = v, t => unreachable!("t = {:?}", t), } } } pub fn get_field_value(rf: Arc, fid: FieldIdRef) -> Oop { Self::get_field_value2(rf, fid.offset) } pub fn get_field_value2(rf: Arc, offset: usize) -> Oop { unsafe { let ptr = rf.get_raw_ptr(); match &(*ptr).v { oop::RefKind::Inst(inst) => inst.field_values[offset].clone(), oop::RefKind::Mirror(mirror) => match mirror.field_values.get(offset) { Some(v) => v.clone(), _ => unreachable!("mirror = {:?}", mirror), }, oop::RefKind::Array(ary) => ary.elements[offset].clone(), t => unreachable!("t = {:?}", t), } } } pub fn put_static_field_value(&mut self, fid: FieldIdRef, v: Oop) { match &mut self.kind { ClassKind::Instance(cls_obj) => { let k = ( self.name.clone(), fid.field.name.clone(), fid.field.desc.clone(), ); if cls_obj.static_fields.contains_key(&k) { cls_obj.static_field_values[fid.offset] = v; } else { let super_class = self.super_class.clone(); super_class .unwrap() .get_mut_class() .put_static_field_value(fid, v); } } _ => unreachable!(), } } pub fn get_static_field_value(&self, fid: FieldIdRef) -> Oop { match &self.kind { ClassKind::Instance(cls_obj) => { let k = ( self.name.clone(), fid.field.name.clone(), fid.field.desc.clone(), ); if cls_obj.static_fields.contains_key(&k) { cls_obj.static_field_values[fid.offset].clone() } else { let super_class = self.super_class.clone(); super_class.unwrap().get_class().get_static_field_value(fid) } } _ => unreachable!(), } } pub fn check_interface(&self, intf: ClassRef) -> bool { match &self.kind { ClassKind::Instance(inst) => { for e in inst.interfaces.values() { if Arc::ptr_eq(e, &intf) { return true; } let e = e.get_class(); if e.check_interface(intf.clone()) { return true; } } } _ => unreachable!(), } match &self.super_class { Some(super_cls) => { let super_cls = super_cls.get_class(); super_cls.check_interface(intf) } None => false, } } pub fn hack_as_native(&mut self, name: &[u8], desc: &[u8]) { match &mut self.kind { ClassKind::Instance(cls) => { let m = { let name = Arc::new(Vec::from(name)); let desc = Arc::new(Vec::from(desc)); let k = (name, desc); let it = cls.all_methods.get_mut(&k).unwrap(); let mut method = it.method.clone(); method.acc_flags |= ACC_NATIVE; let m = method::MethodId::new(it.offset, method); cls.all_methods.insert(k, m.clone()); m }; info!( "hack_as_native: {}:{}:{}, native={}", unsafe { std::str::from_utf8_unchecked(self.name.as_slice()) }, unsafe { std::str::from_utf8_unchecked(name) }, unsafe { std::str::from_utf8_unchecked(desc) }, m.method.is_native() ); } _ => unreachable!(), } } } //open api new impl Class { pub fn new_class(class_file: ClassFileRef, class_loader: Option) -> Self { let cp = class_file.cp.clone(); let name = constant_pool::get_class_name(&cp, class_file.this_class as usize).clone(); let acc_flags = class_file.acc_flags; let class_obj = ClassObject { class_file, n_inst_fields: 0, all_methods: FxHashMap::default(), v_table: FxHashMap::default(), static_fields: FxHashMap::default(), inst_fields: FxHashMap::default(), static_field_values: vec![], interfaces: FxHashMap::default(), mirror: None, signature: None, source_file: None, enclosing_method: None, inner_classes: None, cp_cache: ConstantPoolCache::new(cp), }; let mutex = unsafe { let mut mutex = ReentrantMutex::uninitialized(); mutex.init(); mutex }; Self { clinit_mutex: Arc::new(Mutex::new(())), name, state: std::sync::atomic::AtomicU8::new(State::Allocated.into()), acc_flags, super_class: None, class_loader, kind: ClassKind::Instance(class_obj), mutex, } } pub fn new_object_ary(class_loader: ClassLoader, component: ClassRef, elm_name: &[u8]) -> Self { let name = Vec::from(elm_name); let name = Arc::new(name); let ary_cls_obj = ArrayClassObject { value_type: ValueType::ARRAY, down_type: None, component: Some(component), mirror: None, }; let mutex = unsafe { let mut mutex = ReentrantMutex::uninitialized(); mutex.init(); mutex }; Self { clinit_mutex: Arc::new(Mutex::new(())), name, state: std::sync::atomic::AtomicU8::new(State::Allocated.into()), acc_flags: 0, //todo: should be 0? super_class: None, class_loader: Some(class_loader), kind: ClassKind::ObjectArray(ary_cls_obj), mutex, } } pub fn new_prime_ary(class_loader: ClassLoader, value_type: ValueType) -> Self { let ary_cls_obj = ArrayClassObject { value_type, down_type: None, component: None, mirror: None, }; let mut name = Vec::with_capacity(2); name.push(b'['); name.extend_from_slice(value_type.into()); let mutex = unsafe { let mut mutex = ReentrantMutex::uninitialized(); mutex.init(); mutex }; Self { clinit_mutex: Arc::new(Mutex::new(())), name: Arc::new(name), state: std::sync::atomic::AtomicU8::new(State::Allocated.into()), acc_flags: 0, //todo: should be 0? super_class: None, class_loader: Some(class_loader), kind: ClassKind::TypeArray(ary_cls_obj), mutex, } } pub fn new_wrapped_ary(class_loader: ClassLoader, down_type: ClassRef) -> Self { let cls = down_type.get_class(); debug_assert!(cls.is_array()); //build name let mut name2 = Vec::with_capacity(1 + cls.name.len()); name2.push(b'['); name2.extend_from_slice(cls.name.as_slice()); let kind = match cls.get_class_kind_type() { ClassKindType::Instance => unreachable!(), ClassKindType::TypAry => ClassKind::TypeArray(ArrayClassObject { value_type: ValueType::ARRAY, down_type: Some(down_type), component: None, mirror: None, }), ClassKindType::ObjectAry => { let component = { let cls = down_type.get_class(); match &cls.kind { ClassKind::ObjectArray(ary_cls) => ary_cls.component.clone(), _ => unreachable!(), } }; ClassKind::ObjectArray(ArrayClassObject { value_type: ValueType::ARRAY, down_type: Some(down_type), component, mirror: None, }) } }; let mutex = unsafe { let mut mutex = ReentrantMutex::uninitialized(); mutex.init(); mutex }; Self { clinit_mutex: Arc::new(Mutex::new(())), name: Arc::new(name2), state: std::sync::atomic::AtomicU8::new(State::Allocated.into()), acc_flags: 0, //todo: should be 0? super_class: None, class_loader: Some(class_loader), kind, mutex, } } } //inner api for link impl ClassObject { fn link_super_class( &mut self, name: BytesRef, class_loader: Option, ) -> Option { let class_file = &self.class_file; let cp = &class_file.cp; if class_file.super_class == 0 { if name.as_slice() != consts::J_OBJECT { unreachable!("should be java/lang/Object"); } None } else { let name = constant_pool::get_class_name(cp, class_file.super_class as usize); let super_class = runtime::require_class(class_loader, name).unwrap(); { let c = super_class.get_class(); debug_assert!(c.is_instance()); debug_assert!(!c.is_final(), "should not final"); } Some(super_class) } } fn link_fields(&mut self, self_ref: ClassRef, cls_name: BytesRef, num_field_of_super: usize) { let cls_file = self.class_file.clone(); let cp = &cls_file.cp; let mut n_static = 0; let mut offset_field = num_field_of_super; cls_file.fields.iter().for_each(|it| { let field = field::Field::new(cp, it, cls_name.clone(), self_ref.clone()); let k = (cls_name.clone(), field.name.clone(), field.desc.clone()); if field.is_static() { let fid = field::FieldId { offset: n_static, field, }; self.static_fields.insert(k, Arc::new(fid)); n_static += 1; } else { let fid = field::FieldId { offset: offset_field, field, }; self.inst_fields.insert(k, Arc::new(fid)); offset_field += 1; } }); self.n_inst_fields = offset_field; self.static_field_values = vec![Oop::Null; n_static]; } fn link_interfaces(&mut self) { let class_file = self.class_file.clone(); let cp = &class_file.cp; class_file .interfaces .iter() .for_each(|it| match runtime::require_class2(*it, cp) { Some(class) => { let name = class.get_class().name.clone(); self.interfaces.insert(name, class); } None => { let name = constant_pool::get_class_name(cp, *it as usize); error!("link interface failed {:?}", name); } }); } fn link_methods(&mut self, this_ref: ClassRef, cls_name: BytesRef) { let class_file = self.class_file.clone(); let cp = &class_file.cp; class_file.methods.iter().enumerate().for_each(|(i, it)| { let method = method::Method::new( cp, it, this_ref.clone(), class_file.clone(), i, cls_name.clone(), ); let method_id = method::MethodId::new(i, method); let name = method_id.method.name.clone(); let desc = method_id.method.desc.clone(); let k = (name, desc); self.all_methods.insert(k.clone(), method_id.clone()); if !method_id.method.is_static() { self.v_table.insert(k, method_id); } }); } fn link_attributes(&mut self) { let class_file = self.class_file.clone(); let cp = &class_file.cp; class_file.attrs.iter().for_each(|a| match a { AttributeType::Signature { signature_index } => { let s = get_cp_utf8(cp, *signature_index as usize); self.signature = Some(s.clone()); } AttributeType::SourceFile { source_file_index } => { let s = get_cp_utf8(cp, *source_file_index as usize); self.source_file = Some(s.clone()); } AttributeType::EnclosingMethod { em } => { self.enclosing_method = Some(em.clone()); } AttributeType::InnerClasses { classes } => { self.inner_classes = Some(classes.clone()); } _ => (), }); } fn init_static_fields(&mut self) { let values = &mut self.static_field_values; self.static_fields.iter().for_each(|(_, it)| { if it.field.is_final() { match it.field.get_attr_constant_value() { Some(v) => values[it.offset] = v, None => values[it.offset] = it.field.get_constant_value(), } } else { values[it.offset] = it.field.get_constant_value(); } }); } } impl Class { pub fn get_class_method_inner( &self, name: &BytesRef, desc: &BytesRef, with_super: bool, ) -> Result { let k = (name.clone(), desc.clone()); match &self.kind { ClassKind::Instance(cls_obj) => { if let Some(m) = cls_obj.all_methods.get(&k) { return Ok(m.clone()); } } ClassKind::ObjectArray(ary) => { //use java/lang/Object, methods } _ => unreachable!(), } if with_super { match self.super_class.as_ref() { Some(super_class) => { return super_class .get_class() .get_class_method_inner(name, desc, with_super); } None => return Err(()), } } Err(()) } fn get_virtual_method_inner( &self, name: &BytesRef, desc: &BytesRef, ) -> Result { let k = (name.clone(), desc.clone()); match &self.kind { ClassKind::Instance(cls_obj) => { if let Some(m) = cls_obj.v_table.get(&k) { return Ok(m.clone()); } } _ => unreachable!(), } match self.super_class.as_ref() { Some(super_class) => super_class.get_class().get_virtual_method_inner(name, desc), None => Err(()), } } pub fn get_interface_method_inner( &self, name: &BytesRef, desc: &BytesRef, ) -> Result { let k = (name.clone(), desc.clone()); match &self.kind { ClassKind::Instance(cls_obj) => match cls_obj.v_table.get(&k) { Some(m) => return Ok(m.clone()), None => { for (_, itf) in cls_obj.interfaces.iter() { let cls = itf.get_class(); let m = cls.get_interface_method(name, desc); if m.is_ok() { return m; } } } }, _ => unreachable!(), } match self.super_class.as_ref() { Some(super_class) => super_class .get_class() .get_interface_method_inner(name, desc), None => Err(()), } } } ================================================ FILE: crates/vm/src/oop/consts.rs ================================================ use crate::oop::Oop; use std::sync::Arc; lazy_static! { static ref INT0: Oop = { Oop::new_int(0) }; static ref LONG0: Oop = { Oop::new_long(0) }; static ref FLOAT0: Oop = { Oop::new_float(0.0) }; static ref DOUBLE0: Oop = { Oop::new_double(0.0) }; } pub fn get_int0() -> Oop { INT0.clone() } pub fn get_long0() -> Oop { LONG0.clone() } pub fn get_float0() -> Oop { FLOAT0.clone() } pub fn get_double0() -> Oop { DOUBLE0.clone() } pub fn init() { lazy_static::initialize(&INT0); lazy_static::initialize(&LONG0); lazy_static::initialize(&FLOAT0); lazy_static::initialize(&DOUBLE0); } ================================================ FILE: crates/vm/src/oop/field.rs ================================================ use crate::oop::{self, consts as oop_consts, Oop, ValueType}; use crate::runtime::require_class2; use crate::types::ClassRef; use crate::types::*; use crate::util; use crate::util::PATH_SEP; use classfile::{ constant_pool, consts, flags::*, AttributeType, BytesRef, ConstantPool, ConstantPoolType, FieldInfo, U2, }; use std::fmt; use std::ops::Deref; use std::sync::Arc; pub fn get_field_ref(cp: &ConstantPool, idx: usize, is_static: bool) -> FieldIdRef { let (class_index, name_and_type_index) = constant_pool::get_field_ref(cp, idx); //load Field's Class, then init it let class = require_class2(class_index, cp).unwrap_or_else(|| { panic!( "Unknown field class {:?}", cp.get(class_index as usize) .expect("Missing item") .as_cp_item(cp) ) }); oop::class::init_class(&class); oop::class::init_class_fully(&class); let (name, desc) = constant_pool::get_name_and_type(cp, name_and_type_index as usize); let class = class.get_class(); class.get_field_id(name, desc, is_static) } pub fn build_inited_field_values(class: ClassRef) -> Vec { let n = { let class = class.get_class(); match &class.kind { oop::class::ClassKind::Instance(class_obj) => class_obj.n_inst_fields, _ => unreachable!(), } }; let mut field_values = vec![Oop::Null; n]; let mut cur_cls = class; loop { let cls = cur_cls.clone(); let cls = cls.get_class(); match &cls.kind { oop::class::ClassKind::Instance(cls_obj) => { cls_obj.inst_fields.iter().for_each(|(_, fir)| { match fir.field.value_type { ValueType::BYTE | ValueType::BOOLEAN | ValueType::CHAR | ValueType::SHORT | ValueType::INT => { field_values[fir.offset] = oop_consts::get_int0(); } ValueType::LONG => { field_values[fir.offset] = oop_consts::get_long0(); } ValueType::FLOAT => { field_values[fir.offset] = oop_consts::get_float0(); } ValueType::DOUBLE => { field_values[fir.offset] = oop_consts::get_double0(); } ValueType::OBJECT | ValueType::ARRAY => { //ignore, has been inited by NULL } ValueType::VOID => unreachable!(), } }); } _ => unreachable!(), } if cls.super_class.is_none() { break; } else { cur_cls = cls.super_class.clone().unwrap(); } } field_values } #[derive(Debug, Clone)] pub struct FieldId { pub offset: usize, pub field: Field, } #[derive(Clone)] pub struct Field { pub class: ClassRef, pub cls_name: BytesRef, pub name: BytesRef, pub desc: BytesRef, pub acc_flags: U2, pub value_type: ValueType, pub attr_constant_value: Option, } impl Field { pub fn new(cp: &ConstantPool, fi: &FieldInfo, cls_name: BytesRef, class: ClassRef) -> Self { let name = constant_pool::get_utf8(cp, fi.name_index as usize).clone(); let desc = constant_pool::get_utf8(cp, fi.desc_index as usize).clone(); let value_type = desc.first().unwrap().into(); let acc_flags = fi.acc_flags; let mut attr_constant_value = None; for it in fi.attrs.iter() { if let AttributeType::ConstantValue { constant_value_index, } = it { let v = constant_value(cp, *constant_value_index as usize); attr_constant_value = Some(v); break; } } Self { class, cls_name, name, desc, acc_flags, value_type, attr_constant_value, } } pub fn is_public(&self) -> bool { (self.acc_flags & ACC_PUBLIC) == ACC_PUBLIC } pub fn is_private(&self) -> bool { (self.acc_flags & ACC_PRIVATE) == ACC_PRIVATE } pub fn is_protected(&self) -> bool { (self.acc_flags & ACC_PROTECTED) == ACC_PROTECTED } pub fn is_final(&self) -> bool { (self.acc_flags & ACC_FINAL) == ACC_FINAL } pub fn is_static(&self) -> bool { (self.acc_flags & ACC_STATIC) == ACC_STATIC } pub fn is_volatile(&self) -> bool { (self.acc_flags & ACC_VOLATILE) == ACC_VOLATILE } pub fn get_constant_value(&self) -> Oop { match self.value_type { ValueType::BYTE | ValueType::BOOLEAN | ValueType::CHAR | ValueType::SHORT | ValueType::INT => oop_consts::get_int0(), ValueType::LONG => oop_consts::get_long0(), ValueType::FLOAT => oop_consts::get_float0(), ValueType::DOUBLE => oop_consts::get_double0(), ValueType::OBJECT | ValueType::ARRAY => Oop::Null, _ => unreachable!(), } } pub fn get_attr_constant_value(&self) -> Option { self.attr_constant_value.clone() } } impl fmt::Debug for Field { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let cls_name = unsafe { std::str::from_utf8_unchecked(self.cls_name.as_slice()) }; let name = unsafe { std::str::from_utf8_unchecked(self.name.as_slice()) }; let desc = unsafe { std::str::from_utf8_unchecked(self.desc.as_slice()) }; write!(f, "{}:{}:{}", cls_name, name, desc) } } fn constant_value(cp: &ConstantPool, v_idx: usize) -> Oop { match cp.get(v_idx) { Some(ConstantPoolType::Long { v }) => { let v = i64::from_be_bytes(*v); Oop::new_long(v) } Some(ConstantPoolType::Float { v }) => { let v = u32::from_be_bytes(*v); let v = f32::from_bits(v); Oop::new_float(v) } Some(ConstantPoolType::Double { v }) => { let v = u64::from_be_bytes(*v); let v = f64::from_bits(v); Oop::new_double(v) } Some(ConstantPoolType::Integer { v }) => { let v = i32::from_be_bytes(*v); Oop::new_int(v) } Some(ConstantPoolType::String { string_index }) => { let v = constant_pool::get_utf8(cp, *string_index as usize).clone(); Oop::new_const_utf8(v) } _ => unreachable!(), } } ================================================ FILE: crates/vm/src/oop/inst.rs ================================================ use crate::oop::{field, Oop}; use crate::types::ClassRef; #[derive(Debug, Clone)] pub struct InstOopDesc { pub class: ClassRef, pub field_values: Vec, } impl InstOopDesc { pub fn new(class: ClassRef) -> Self { let field_values = field::build_inited_field_values(class.clone()); Self { class, field_values, } } } ================================================ FILE: crates/vm/src/oop/mirror.rs ================================================ use crate::oop::{Oop, ValueType}; use crate::types::ClassRef; #[derive(Debug, Clone)] pub struct MirrorOopDesc { pub target: Option, pub field_values: Vec, pub value_type: ValueType, } impl MirrorOopDesc { pub fn is_prim_mirror(&self) -> bool { self.target.is_none() } } ================================================ FILE: crates/vm/src/oop/mod.rs ================================================ #![allow(unused)] use std::fmt; use std::sync::{Arc, Condvar, Mutex, RwLock}; use classfile::{BytesRef, ClassFile}; use crate::new_br; use crate::oop::class::ClassObject; use crate::runtime::{require_class3, ClassLoader}; use crate::types::*; use crate::util::oop::{get_java_lang_integer_value_offset, get_java_lang_string_value_offset}; pub use self::ary::{ArrayOopDesc, TypeArrayDesc, TypeArrayEnum}; pub use self::class::{Class, ClassKind}; pub use self::inst::InstOopDesc; pub use self::mirror::MirrorOopDesc; pub use self::reference::{RefKind, RefKindDesc}; pub use self::values::ValueType; pub mod ary; pub mod class; pub mod consts; pub mod field; pub mod inst; pub mod mirror; pub mod reference; pub mod values; #[derive(Clone)] pub enum Oop { Int(i32), Long(i64), Float(f32), Double(f64), /* used by: Throwable.java private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; */ ConstUtf8(BytesRef), //used by oop::field::Filed::get_constant_value Null, Ref(Arc), } #[derive(Debug)] pub struct OopPtr(u64); impl fmt::Debug for Oop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Oop::Int(v) => write!(f, "Oop(Int({}))", *v), Oop::Long(v) => write!(f, "Oop(Long({}))", *v), Oop::Float(v) => write!(f, "Oop(Float({}))", *v), Oop::Double(v) => write!(f, "Oop(Double({}))", *v), Oop::ConstUtf8(v) => write!( f, "Oop(ConstUtf8({}))", String::from_utf8_lossy(v.as_slice()) ), Oop::Null => write!(f, "Oop(Null)"), Oop::Ref(rf) => { let ptr = rf.get_raw_ptr(); unsafe { match &(*ptr).v { RefKind::Array(ary) => write!(f, "Oop(OopRef(Array))"), RefKind::Inst(inst) => write!(f, "Oop(OopRef(Instance))"), RefKind::TypeArray(ary) => write!(f, "Oop(OopRef(TypeArray))"), RefKind::Mirror(mirror) => write!(f, "Oop(OopRef(Mirror))"), } } } } } } //primitive value factor impl Oop { pub fn new_int(v: i32) -> Self { Oop::Int(v) } pub fn new_long(v: i64) -> Self { Oop::Long(v) } pub fn new_float(v: f32) -> Self { Oop::Float(v) } pub fn new_double(v: f64) -> Self { Oop::Double(v) } } //primitive ary value factor impl Oop { pub fn new_type_ary(v: u8, len: usize) -> Oop { match TypeArrayEnum::from(v) { TypeArrayEnum::Boolean => Self::new_bool_ary(len), TypeArrayEnum::Char => Self::new_char_ary(len), TypeArrayEnum::Float => Self::new_float_ary(len), TypeArrayEnum::Double => Self::new_double_ary(len), TypeArrayEnum::Byte => Self::new_byte_ary(len), TypeArrayEnum::Short => Self::new_short_ary(len), TypeArrayEnum::Int => Self::new_int_ary(len), TypeArrayEnum::Long => Self::new_long_ary(len), } } pub fn char_ary_from1(v: &[u16]) -> Oop { let elms = Vec::from(v); Self::new_char_ary2(elms) } pub fn new_byte_ary(len: usize) -> Oop { let elms = vec![0; len]; Self::new_byte_ary2(elms) } fn new_bool_ary(len: usize) -> Oop { let elms = vec![0; len]; Self::new_bool_ary2(elms) } fn new_char_ary(len: usize) -> Oop { let elms = vec![0; len]; Self::new_char_ary2(elms) } fn new_short_ary(len: usize) -> Oop { let elms = vec![0; len]; Self::new_short_ary2(elms) } fn new_int_ary(len: usize) -> Oop { let elms = vec![0; len]; Self::new_int_ary2(elms) } fn new_float_ary(len: usize) -> Oop { let elms = vec![0.0; len]; Self::new_float_ary2(elms) } fn new_double_ary(len: usize) -> Oop { let elms = vec![0.0; len]; Self::new_double_ary2(elms) } fn new_long_ary(len: usize) -> Oop { let elms = vec![0; len]; Self::new_long_ary2(elms) } pub fn new_byte_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Byte(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_bool_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Bool(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_char_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Char(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_short_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Short(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_int_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Int(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_float_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Float(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_double_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Double(ary); Self::new_ref(RefKind::TypeArray(v)) } pub fn new_long_ary2(elms: Vec) -> Oop { let ary = Box::new(elms); let v = TypeArrayDesc::Long(ary); Self::new_ref(RefKind::TypeArray(v)) } } //reference value factory impl Oop { pub fn new_const_utf8(v: BytesRef) -> Self { Oop::ConstUtf8(v) } pub fn new_null() -> Self { Oop::Null } pub fn new_inst(cls_obj: ClassRef) -> Oop { let v = InstOopDesc::new(cls_obj); Self::new_ref(RefKind::Inst(v)) } } //mirror impl Oop { pub fn new_mirror(target: ClassRef) -> Oop { let java_lang_class = require_class3(None, b"java/lang/Class").unwrap(); let field_values = field::build_inited_field_values(java_lang_class); let v = MirrorOopDesc { target: Some(target), field_values, value_type: ValueType::OBJECT, }; Self::new_ref(RefKind::Mirror(v)) } pub fn new_prim_mirror(value_type: ValueType, target: Option) -> Oop { let java_lang_class = require_class3(None, b"java/lang/Class").unwrap(); let field_values = field::build_inited_field_values(java_lang_class); let v = MirrorOopDesc { target, field_values, value_type, }; Self::new_ref(RefKind::Mirror(v)) } pub fn new_ary_mirror(target: ClassRef, value_type: ValueType) -> Oop { let java_lang_class = require_class3(None, b"java/lang/Class").unwrap(); let field_values = field::build_inited_field_values(java_lang_class); let v = MirrorOopDesc { target: Some(target), field_values: vec![], value_type, }; Self::new_ref(RefKind::Mirror(v)) } } //array reference factory impl Oop { pub fn new_ref_ary(ary_cls_obj: ClassRef, len: usize) -> Oop { let elements = vec![Oop::Null; len]; Self::new_ref_ary2(ary_cls_obj, elements) } pub fn new_ref_ary2(ary_cls_obj: ClassRef, elms: Vec) -> Oop { let v = ArrayOopDesc::new(ary_cls_obj, elms); Self::new_ref(RefKind::Array(v)) } } //private helper impl Oop { fn new_ref(v: RefKind) -> Oop { let v = RefKindDesc::new(v); let v = Box::new(v); let ptr = Box::into_raw(v) as u64; let rf = Arc::new(OopPtr(ptr)); Oop::Ref(rf) } } impl Oop { pub fn hash_code(&self) -> i32 { match self { Oop::Ref(rf) => { if OopPtr::is_java_lang_string(rf.clone()) { OopPtr::java_lang_string_hash(rf.clone()) } else { rf.0 as i32 } } Oop::Null => 0, _ => unreachable!(), } } } impl Oop { pub fn is_null(&self) -> bool { match self { Oop::Null => true, _ => false, } } #[inline] pub fn extract_int(&self) -> i32 { match self { Oop::Int(v) => *v, _ => unreachable!(), } } #[inline] pub fn extract_float(&self) -> f32 { match &self { Oop::Float(v) => *v, _ => unreachable!(), } } #[inline] pub fn extract_long(&self) -> i64 { match self { Oop::Long(v) => *v, _ => unreachable!(), } } #[inline] pub fn extract_double(&self) -> f64 { match self { Oop::Double(v) => *v, _ => unreachable!(), } } #[inline] pub fn extract_ref(&self) -> Arc { match self { Oop::Ref(v) => v.clone(), t => unreachable!("t = {:?}", t), } } } impl OopPtr { pub fn get_raw_ptr(&self) -> *const RefKindDesc { self.0 as *const RefKindDesc } pub fn get_mut_raw_ptr(&self) -> *mut RefKindDesc { self.0 as *mut RefKindDesc } } impl OopPtr { pub fn extract_inst(&self) -> &InstOopDesc { let ptr = self.get_raw_ptr(); unsafe { (*ptr).v.extract_inst() } } pub fn extract_array(&self) -> &ArrayOopDesc { let ptr = self.get_raw_ptr(); unsafe { (*ptr).v.extract_array() } } pub fn extract_mut_array(&self) -> &mut ArrayOopDesc { let ptr = self.get_mut_raw_ptr(); unsafe { (*ptr).v.extract_mut_array() } } pub fn extract_type_array(&self) -> &TypeArrayDesc { let ptr = self.get_raw_ptr(); unsafe { (*ptr).v.extract_type_array() } } pub fn extract_mut_type_array(&self) -> &mut TypeArrayDesc { let ptr = self.get_mut_raw_ptr(); unsafe { (*ptr).v.extract_mut_type_array() } } pub fn extract_mirror(&self) -> &MirrorOopDesc { let ptr = self.get_raw_ptr(); unsafe { (*ptr).v.extract_mirror() } } } impl OopPtr { pub fn monitor_enter(&self) { let ptr = self.get_raw_ptr(); unsafe { (*ptr).monitor_enter() }; } pub fn monitor_exit(&self) { let ptr = self.get_raw_ptr(); unsafe { (*ptr).monitor_exit() }; } pub fn notify_all(&self) { let ptr = self.get_raw_ptr(); unsafe { (*ptr).notify_all() } } pub fn wait(&self) { let ptr = self.get_raw_ptr(); unsafe { (*ptr).wait() } } pub fn wait_timeout(&self, duration: std::time::Duration) { let ptr = self.get_raw_ptr(); unsafe { (*ptr).wait_timeout(duration) } } } impl OopPtr { pub fn is_eq(l: &Oop, r: &Oop) -> bool { let l_is_null = l.is_null(); let r_is_null = r.is_null(); match (l_is_null, r_is_null) { (true, true) => return true, (true, false) => return false, (false, true) => return false, (false, false) => (), } let l = l.extract_ref(); let r = r.extract_ref(); if l.0 == r.0 { true } else if Self::is_java_lang_string(l.clone()) && Self::is_java_lang_string(r.clone()) { Self::is_java_lang_string_eq(l, r) } else { false } } pub fn is_java_lang_string(rf: Arc) -> bool { let ptr = rf.get_raw_ptr(); unsafe { match &(*ptr).v { RefKind::Inst(inst) => { let cls = inst.class.get_class(); cls.name.as_slice() == b"java/lang/String" } _ => false, } } } fn is_java_lang_string_eq(l: Arc, r: Arc) -> bool { let offset = get_java_lang_string_value_offset(); //java.lang.String.value let v1 = Class::get_field_value2(l, offset); let v2 = Class::get_field_value2(r, offset); let rf1 = v1.extract_ref(); let rf2 = v2.extract_ref(); let chars1 = rf1.extract_type_array().extract_chars(); let chars2 = rf2.extract_type_array().extract_chars(); chars1 == chars2 } } impl OopPtr { pub fn java_lang_string(rf: Arc) -> String { let v = Self::java_lang_string_value(rf); String::from_utf16_lossy(v.as_slice()) } //java.lang.String.value pub fn java_lang_string_value(rf: Arc) -> Vec { let offset = get_java_lang_string_value_offset(); let v = Class::get_field_value2(rf, offset); let rf = v.extract_ref(); let chars = rf.extract_type_array().extract_chars(); chars.to_vec() } pub fn java_lang_string_hash(rf: Arc) -> i32 { let offset = get_java_lang_string_value_offset(); let v = Class::get_field_value2(rf, offset); let rf = v.extract_ref(); let chars = rf.extract_type_array().extract_chars(); let mut h = 0i32; for v in chars.iter() { h = h.wrapping_mul(31).wrapping_add(*v as i32); } h } //java.lang.Integer.value pub fn java_lang_integer_value(rf: Arc) -> i32 { let offset = get_java_lang_integer_value_offset(); Class::get_field_value2(rf, offset).extract_int() } //java.lang.Thread.eetop pub fn java_lang_thread_eetop(rf: Arc) -> i64 { let fid = { let inst = rf.extract_inst(); let cls = inst.class.clone(); let cls = cls.get_class(); cls.get_field_id(&new_br("eetop"), &new_br("J"), false) }; Class::get_field_value(rf, fid).extract_long() } } impl Drop for OopPtr { fn drop(&mut self) { let _v = unsafe { Box::from_raw(self.0 as *mut RefKindDesc) }; } } pub fn init() { consts::init(); } ================================================ FILE: crates/vm/src/oop/reference.rs ================================================ use crate::oop::{ArrayOopDesc, InstOopDesc, MirrorOopDesc, TypeArrayDesc}; use crate::runtime::thread::{Condvar, ReentrantMutex}; use std::fmt; use std::fmt::Formatter; use std::time::Duration; #[derive(Debug)] pub enum RefKind { Inst(InstOopDesc), Array(ArrayOopDesc), TypeArray(TypeArrayDesc), Mirror(MirrorOopDesc), } pub struct RefKindDesc { pub v: RefKind, pub hash_code: Option, mutex: ReentrantMutex, cond_var: Condvar, } impl RefKindDesc { pub fn new(v: RefKind) -> Self { let mutex = unsafe { let mut mutex = ReentrantMutex::uninitialized(); mutex.init(); mutex }; let cond_var = unsafe { let mut cond = Condvar::new(); cond.init(); cond }; Self { v, hash_code: None, mutex, cond_var, } } } impl RefKindDesc { pub fn monitor_enter(&self) { unsafe { self.mutex.lock(); } } pub fn monitor_exit(&self) { unsafe { self.mutex.unlock(); } } pub fn wait(&self) { unsafe { self.cond_var.wait(&self.mutex); } } pub fn wait_timeout(&self, duration: Duration) { unsafe { self.cond_var.wait_timeout(&self.mutex, duration); } } pub fn notify_all(&self) { unsafe { self.cond_var.notify_all(); } } } impl RefKind { pub fn extract_inst(&self) -> &InstOopDesc { match &self { RefKind::Inst(v) => v, _ => unreachable!(), } } pub fn extract_array(&self) -> &ArrayOopDesc { match &self { RefKind::Array(v) => v, _ => unreachable!(), } } pub fn extract_mut_array(&mut self) -> &mut ArrayOopDesc { let v = self; match v { RefKind::Array(v) => v, _ => unreachable!(), } } pub fn extract_type_array(&self) -> &TypeArrayDesc { match &self { RefKind::TypeArray(v) => v, _ => unreachable!(), } } pub fn extract_mut_type_array(&mut self) -> &mut TypeArrayDesc { let v = self; match v { RefKind::TypeArray(v) => v, _ => unreachable!(), } } pub fn extract_mirror(&self) -> &MirrorOopDesc { match &self { RefKind::Mirror(v) => v, _ => unreachable!(), } } } impl Drop for RefKindDesc { fn drop(&mut self) { unsafe { self.mutex.destroy(); } } } impl fmt::Debug for RefKindDesc { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("RefKindDesc") .field("v", &self.v) .field("hash_code", &self.hash_code) .field("mutex", &"mutex") .finish() } } ================================================ FILE: crates/vm/src/oop/values.rs ================================================ use crate::runtime::ClassLoader; #[derive(Debug, Clone, Copy, PartialOrd, PartialEq)] pub enum ValueType { BYTE, BOOLEAN, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, VOID, OBJECT, ARRAY, } impl From<&u8> for ValueType { fn from(v: &u8) -> Self { match v { b'B' => ValueType::BYTE, b'Z' => ValueType::BOOLEAN, b'C' => ValueType::CHAR, b'S' => ValueType::SHORT, b'I' => ValueType::INT, b'J' => ValueType::LONG, b'F' => ValueType::FLOAT, b'D' => ValueType::DOUBLE, b'V' => ValueType::VOID, b'L' => ValueType::OBJECT, b'[' => ValueType::ARRAY, t => { let s = [*t]; let s = String::from_utf8_lossy(&s); unreachable!("Unknown ValueType = {}", s) } } } } impl Into<&[u8]> for ValueType { fn into(self) -> &'static [u8] { match self { ValueType::BYTE => b"B", ValueType::BOOLEAN => b"Z", ValueType::CHAR => b"C", ValueType::SHORT => b"S", ValueType::INT => b"I", ValueType::LONG => b"J", ValueType::FLOAT => b"F", ValueType::DOUBLE => b"D", ValueType::VOID => b"V", ValueType::OBJECT => b"L", ValueType::ARRAY => b"[", } } } impl ValueType { pub fn parse_wrap(class_loader: Option, desc: &str) -> Self { match desc.as_bytes().first().unwrap() { b'B' | b'Z' | b'C' | b'S' | b'I' => ValueType::INT, b'J' => ValueType::LONG, b'F' => ValueType::FLOAT, b'D' => ValueType::DOUBLE, b'V' => ValueType::VOID, b'L' => ValueType::OBJECT, b'[' => ValueType::ARRAY, _ => unreachable!(), } } pub fn get_primitive_name(&self) -> &'static [u8] { match *self { ValueType::BYTE => b"byte", ValueType::BOOLEAN => b"boolean", ValueType::CHAR => b"char", ValueType::SHORT => b"short", ValueType::INT => b"int", ValueType::LONG => b"long", ValueType::FLOAT => b"float", ValueType::DOUBLE => b"double", ValueType::VOID | ValueType::OBJECT | ValueType::ARRAY => unreachable!(), } } } ================================================ FILE: crates/vm/src/runtime/class_loader.rs ================================================ use crate::native; use crate::oop::class::ClassPtr; use crate::oop::{self, Class, ValueType}; use crate::runtime::{self, ClassPathResult}; use crate::types::*; use crate::util; use class_parser::parse_class; use classfile::{constant_pool, BytesRef, ConstantPool, U2}; use std::sync::{Arc, Mutex}; #[derive(Debug, Copy, Clone)] pub enum ClassLoader { Base, Bootstrap, } pub fn require_class(class_loader: Option, name: &BytesRef) -> Option { require_class3(class_loader, name.as_slice()) } pub fn require_class2(index: U2, cp: &ConstantPool) -> Option { let class = constant_pool::get_class_name(cp, index as usize); // trace!("require_class2 class = {}", String::from_utf8_lossy(class.as_slice())); require_class3(None, class.as_slice()) } pub fn require_class3(class_loader: Option, name: &[u8]) -> Option { let class_loader = class_loader.unwrap_or(ClassLoader::Bootstrap); class_loader.load_class(name) } impl ClassLoader { fn load_class(&self, name: &[u8]) -> Option { debug_assert!(!name.contains(&b'.')); // error!("load_class name = {}", String::from_utf8_lossy(name)); match self { ClassLoader::Base => (), ClassLoader::Bootstrap => { let it = runtime::sys_dic_find(name); if it.is_some() { // info!("load_class in dic: {}", String::from_utf8_lossy(name)); return it; } } } if is_array(name) { self.load_array_class(name) } else { let class = self.load_class_from_path(name); if let Some(class) = &class { match self { ClassLoader::Base => (), ClassLoader::Bootstrap => { runtime::sys_dic_put(name, class.clone()); let this_ref = class.clone(); { let mut cls = class.get_mut_class(); cls.set_class_state(oop::class::State::Loaded); cls.link_class(this_ref); } native::java_lang_Class::create_mirror(class.clone()); } } } class } } fn load_array_class(&self, name: &[u8]) -> Option { match calc_dimension(name) { Some(1) => { // dimension == 1 match name.get(1) { Some(b'L') => { //[Ljava/lang/Object; let elm = &name[2..name.len() - 1]; match self.load_class(elm) { Some(elm) => { let mut class = Class::new_object_ary(*self, elm, name); let class = ClassPtr::new(class); { let this_ref = class.clone(); let mut class = class.get_mut_class(); class.link_class(this_ref); } match self { ClassLoader::Base => (), ClassLoader::Bootstrap => { runtime::sys_dic_put(name, class.clone()); } } native::java_lang_Class::create_mirror(class.clone()); Some(class) } None => None, } } Some(t) => { //B, Z... let elm = t.into(); let class = Class::new_prime_ary(*self, elm); let class = ClassPtr::new(class); { let this_ref = class.clone(); let mut class = class.get_mut_class(); class.link_class(this_ref); } match self { ClassLoader::Base => (), ClassLoader::Bootstrap => { runtime::sys_dic_put(name, class.clone()); } } //mirror has been created when vm inited Some(class) } None => unreachable!(), } } _ => { // dimension > 1 let down_type_name = &name[1..]; match self.load_array_class(down_type_name) { Some(down_type) => { let class = Class::new_wrapped_ary(*self, down_type); let class = ClassPtr::new(class); match self { ClassLoader::Base => (), ClassLoader::Bootstrap => { runtime::sys_dic_put(name, class.clone()); } } native::java_lang_Class::create_mirror(class.clone()); Some(class) } None => None, } } } } fn load_class_from_path(&self, name: &[u8]) -> Option { let name = unsafe { std::str::from_utf8_unchecked(name) }; match runtime::find_class_in_classpath(name) { Ok(ClassPathResult(_, buf)) => match parse_class(&buf) { Ok(cf) => { let cfr = Arc::new(Box::new(cf.1)); let class = Class::new_class(cfr, Some(*self)); Some(ClassPtr::new(class)) } Err(e) => unreachable!("name={}, {}", name, e), }, Err(_) => None, } } } fn calc_dimension(name: &[u8]) -> Option { if is_array(name) { name.iter().position(|&c| c != b'[') } else { None } } fn is_array(name: &[u8]) -> bool { name.starts_with(&[b'[']) } #[cfg(test)] mod tests { #[test] fn t_basic() { use super::calc_dimension; assert_eq!(calc_dimension("".as_bytes()), None); assert_eq!(calc_dimension("Ljava/lang/Object;".as_bytes()), None); assert_eq!(calc_dimension("Z".as_bytes()), None); assert_eq!(calc_dimension("[B".as_bytes()), Some(1)); assert_eq!(calc_dimension("[[B".as_bytes()), Some(2)); assert_eq!(calc_dimension("[[[B".as_bytes()), Some(3)); assert_eq!(calc_dimension("[[[[B".as_bytes()), Some(4)); assert_eq!(calc_dimension("[[[[[B".as_bytes()), Some(5)); assert_eq!(calc_dimension("[Ljava/lang/Object;".as_bytes()), Some(1)); assert_eq!(calc_dimension("[[Ljava/lang/Object;".as_bytes()), Some(2)); let name = "[Ljava/lang/Object;"; assert_eq!("java/lang/Object", &name[2..name.len() - 1]); } } ================================================ FILE: crates/vm/src/runtime/class_path_manager.rs ================================================ use crate::util; use std::fs::File; use std::io::{self, BufReader, Cursor, Read, Seek}; use std::path::{self, Path}; use std::sync::{Arc, Mutex, RwLock}; use zip::ZipArchive; lazy_static! { static ref CPM: RwLock = { RwLock::new(ClassPathManager::new()) }; } pub fn init() { lazy_static::initialize(&CPM); } pub fn find_class(name: &str) -> Result { let cpm = CPM.read().unwrap(); cpm.search_class(name) } pub fn add_path(path: &str) { let mut cpm = CPM.write().unwrap(); cpm.add_class_path(path); } pub fn add_paths(path: &str) { let mut cpm = CPM.write().unwrap(); cpm.add_class_paths(path); } #[derive(Debug)] pub struct ClassPathResult(pub String, pub Vec); type ZipRef = Arc>>>; enum ClassSource { DIR, JAR(ZipRef), } struct ClassPathEntry(ClassSource, String); struct ClassPathManager { runtime_class_path: Vec, } impl ClassPathManager { fn new() -> Self { Self { runtime_class_path: vec![], } } pub fn add_class_path(&mut self, path: &str) -> Result<(), io::Error> { let p = Path::new(path); if p.is_dir() { self.runtime_class_path .push(ClassPathEntry(ClassSource::DIR, path.to_string())); } else { let f = File::open(p)?; let mut z = ZipArchive::new(f)?; let handle = Arc::new(Mutex::new(Box::new(z))); self.runtime_class_path .push(ClassPathEntry(ClassSource::JAR(handle), path.to_string())); } Ok(()) } pub fn add_class_paths(&mut self, path: &str) { path.split(util::PATH_SEP).for_each(|p| { if let Err(e) = self.add_class_path(p) { error!("add class path error, path={}, e={:?}", p, e) } }); } pub fn search_class(&self, name: &str) -> Result { let name = name.replace("/", util::FILE_SEP); let name = name.replace(".", util::FILE_SEP); trace!("search_class: {}", name); for it in self.runtime_class_path.iter() { match &it.0 { ClassSource::DIR => { let mut p = String::from(&it.1); p.push_str(util::FILE_SEP); p.push_str(&name); p.push_str(".class"); if let Ok(data) = std::fs::read(&p) { return Ok(ClassPathResult(p, data)); } } ClassSource::JAR(handle) => { let mut p = String::from(&name); p.push_str(".class"); let mut handle = handle.lock().unwrap(); let mut zf = handle.by_name(&p); if let Ok(mut zf) = zf { let mut v = Vec::with_capacity(zf.size() as usize); let r = zf.read_to_end(&mut v); debug_assert!(r.is_ok()); return Ok(ClassPathResult(it.1.clone(), v)); } } } } Err(io::Error::new( io::ErrorKind::NotFound, format!("Search class failed: {}", name), )) } pub fn size(&self) -> usize { self.runtime_class_path.len() } } #[cfg(test)] mod tests { #[test] fn t_basic_zip() { let f = "test/class_path_test.jar"; let f = super::File::open(f).unwrap(); let mut za = super::ZipArchive::new(f).unwrap(); for i in 0..za.len() { let mut zf = za.by_index(i).unwrap(); println!("{}", zf.name()); } } #[test] fn t_replace_all() { let class = "java.lang.String"; assert_eq!(class.replace(".", "/"), "java/lang/String"); } #[test] fn t_add_cls_path() { let mut cpm = super::ClassPathManager::new(); assert!(cpm.add_class_path("test/").is_ok()); assert!(cpm.add_class_path("test_no_exist/").is_err()); assert!(cpm .add_class_path("test/classloader/class_path_test.jar") .is_ok()); assert!(cpm .add_class_path("test/classloader/class_path_test_no_exist.jar") .is_err()); } #[test] fn t_add_cls_paths() { let mut cpm = super::ClassPathManager::new(); cpm.add_class_paths("test/:test/classloader/class_path_test.jar"); assert_eq!(2, cpm.size()); } #[test] fn t_search_cls() { let mut cpm = super::ClassPathManager::new(); let _ = cpm.add_class_path("test/classloader/class_path_test.jar"); assert!(cpm.search_class("Foo").is_ok()); } #[test] fn t_search_cls2() { let mut cpm = super::ClassPathManager::new(); cpm.add_class_paths("test/classloader/class_path_test.jar"); assert!(cpm.search_class("Sample").is_err()); assert!(cpm.search_class("Foo").is_ok()); } } ================================================ FILE: crates/vm/src/runtime/cmp.rs ================================================ use crate::oop; use crate::runtime::require_class3; use crate::types::ClassRef; use classfile::consts as cls_consts; use std::sync::Arc; pub fn instance_of(s: ClassRef, t: ClassRef) -> bool { // Return if S and T are the same class if Arc::ptr_eq(&s, &t) { return true; } let (s_kind, s_is_intf) = { let cls = s.get_class(); (cls.get_class_kind_type(), cls.is_interface()) }; let (t_kind, t_is_intf) = { let cls = t.get_class(); (cls.get_class_kind_type(), cls.is_interface()) }; // If S is an ordinary (non-array) class if s_kind == oop::class::ClassKindType::Instance && !s_is_intf { // If T is an interface type, then S must implement interface T. if t_is_intf { let s = s.get_class(); return s.check_interface(t); } // If T is a class type, then S must be the same class as T, // or S must be a subclass of T; if t_kind == oop::class::ClassKindType::Instance { return check_inherit(s, t); } return false; } // If S is an interface type if s_is_intf { // If T is an interface type, then T must be the same interface as S // or a superinterface of S. if t_is_intf { return check_inherit(s, t); } // If T is a class type, then T must be Object if t_kind == oop::class::ClassKindType::Instance { let object = require_class3(None, cls_consts::J_OBJECT).unwrap(); return Arc::ptr_eq(&t, &object); } return false; } // If S is a class representing the array type SC[], // that is, an array of components of type SC match s_kind { oop::class::ClassKindType::TypAry | oop::class::ClassKindType::ObjectAry => { // If T is an interface type, then T must be one of the interfaces // implemented by arrays (JLS §4.10.3). // https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.10.3 // array implements: // 1. java/lang/Cloneable // 2. java/io/Serializable if t_is_intf { let serializable = require_class3(None, cls_consts::J_SERIALIZABLE).unwrap(); let cloneable = require_class3(None, cls_consts::J_CLONEABLE).unwrap(); return Arc::ptr_eq(&t, &serializable) || Arc::ptr_eq(&t, &cloneable); } if t_kind == oop::class::ClassKindType::Instance { let object = require_class3(None, cls_consts::J_OBJECT).unwrap(); return Arc::ptr_eq(&t, &object); } if t_kind == oop::class::ClassKindType::TypAry && s_kind == oop::class::ClassKindType::TypAry { let cls = s.get_class(); let (s_dimension, s_value_type) = match &cls.kind { oop::class::ClassKind::TypeArray(cls) => (cls.get_dimension(), cls.value_type), _ => unreachable!(), }; let cls = t.get_class(); let (t_dimension, t_value_type) = match &cls.kind { oop::class::ClassKind::TypeArray(cls) => (cls.get_dimension(), cls.value_type), _ => unreachable!(), }; return s_dimension == t_dimension && s_value_type == t_value_type; } if t_kind == oop::class::ClassKindType::ObjectAry && s_kind == oop::class::ClassKindType::ObjectAry { let cls = s.get_class(); let (s_dimension, s_component) = match &cls.kind { oop::class::ClassKind::ObjectArray(cls) => { (cls.get_dimension(), cls.component.clone().unwrap()) } _ => unreachable!(), }; let cls = t.get_class(); let (t_dimension, t_component) = match &cls.kind { oop::class::ClassKind::ObjectArray(cls) => { (cls.get_dimension(), cls.component.clone().unwrap()) } _ => unreachable!(), }; return s_dimension == t_dimension && check_inherit(s_component, t_component); } } _ => (), } false } pub fn check_inherit(s: ClassRef, t: ClassRef) -> bool { let mut super_cls = s; loop { if Arc::ptr_eq(&super_cls, &t) { return true; } let cls = { super_cls.get_class().super_class.clone() }; match cls { Some(cls) => super_cls = cls, None => break, } } false } ================================================ FILE: crates/vm/src/runtime/constant_pool.rs ================================================ use std::cell::RefCell; use rustc_hash::FxHashMap; use classfile::ConstantPool; use crate::oop::field; use crate::types::{FieldIdRef, MethodIdRef}; use crate::{oop, runtime}; enum CacheType { Field(FieldIdRef), Method(MethodIdRef), } impl CacheType { fn extract_field(&self) -> FieldIdRef { match self { CacheType::Field(fid) => fid.clone(), _ => unreachable!(), } } fn extract_method(&self) -> MethodIdRef { match self { CacheType::Method(mid) => mid.clone(), _ => unreachable!(), } } } pub struct ConstantPoolCache { cp: ConstantPool, cache: RefCell>, } impl ConstantPoolCache { pub fn new(cp: ConstantPool) -> Self { Self { cp, cache: RefCell::new(FxHashMap::default()), } } pub fn get_field(&self, idx: usize, is_static: bool) -> FieldIdRef { let cache = self.cache.borrow(); let it = cache.get(&idx); match it { Some(it) => it.extract_field(), None => { drop(cache); let fid = field::get_field_ref(&self.cp, idx, is_static); self.cache_field(idx, fid.clone()); fid } } } fn cache_field(&self, k: usize, v: FieldIdRef) { let mut cache = self.cache.borrow_mut(); let v = CacheType::Field(v); cache.insert(k, v); } pub fn get_method(&self, idx: usize) -> MethodIdRef { let cache = self.cache.borrow(); let it = cache.get(&idx); match it { Some(it) => it.extract_method(), None => { drop(cache); let m = runtime::method::get_method_ref(&self.cp, idx).unwrap(); self.cache_method(idx, m.clone()); m } } } fn cache_method(&self, k: usize, v: MethodIdRef) { let mut cache = self.cache.borrow_mut(); let v = CacheType::Method(v); cache.insert(k, v); } } ================================================ FILE: crates/vm/src/runtime/consts.rs ================================================ pub const THREAD_MAX_STACK_FRAMES: usize = 512; // pub const WARN_THREAD_MAX_STACK_FRAMES: usize = 512; ================================================ FILE: crates/vm/src/runtime/dataarea.rs ================================================ use crate::oop::Oop; use crate::runtime::local::Local; use crate::runtime::stack::Stack; use std::cell::RefCell; use std::sync::{Arc, RwLock}; /* The origin of DataArea java method execution method: Every time a method is called, a new Frame is constructed, and the frame is pushed to the current thread.frames stack. After the method is executed, the Frame is popped. If an exception occurs, jvm_fillInStackTrace traverses the current thread frames: extract the class name, method name, and pc (pc for LineNumberTable Attributes from each frame) Locate the error line of code) and construct an exception stack. The DataArea in the Frame is wrapped with RefCell, so that java_call::invoke_java can execute Java Method, you can use the read-only frame to execute bytecode; when there is an exception, you can also let jvm_fillInStackTrace traverse the frames to get the necessary information. The nature of RefCell makes this possible. In a read-only Frame context, to modify the DataArea, borrow_mut is fine. */ pub struct DataArea { pub stack: RefCell, pub return_v: RefCell>, } unsafe impl Sync for DataArea {} impl DataArea { pub fn new(max_stack: usize) -> Self { let stack = RefCell::new(Stack::new(max_stack)); Self { stack, return_v: RefCell::new(None), } } } ================================================ FILE: crates/vm/src/runtime/exception.rs ================================================ use crate::oop::{self, Oop}; use crate::runtime::{self, require_class3}; use crate::types::JavaThreadRef; use crate::{new_br, util}; use std::sync::atomic::Ordering; pub fn new(name: &[u8], msg: Option) -> Oop { let cls = match require_class3(None, name) { Some(cls) => cls, None => panic!("ClassNotFound: {}", String::from_utf8_lossy(name)), }; oop::class::init_class(&cls); oop::class::init_class_fully(&cls); let ex = Oop::new_inst(cls.clone()); //invoke ctor match &msg { Some(msg) => { //with 'String' arg ctor let msg = util::oop::new_java_lang_string2(msg); let args = vec![ex.clone(), msg]; runtime::invoke::invoke_ctor(cls, new_br("(Ljava/lang/String;)V"), args); } None => { //No arg ctor let args = vec![ex.clone()]; runtime::invoke::invoke_ctor(cls, new_br("()V"), args); } } ex } pub fn meet_ex(cls_name: &'static [u8], msg: Option) { let jt = runtime::thread::current_java_thread(); { let jt = jt.read().unwrap(); let frame = jt.frames.last().unwrap(); let frame = frame.try_read().unwrap(); frame.ex_here.store(true, Ordering::Relaxed); } let ex = new(cls_name, msg); jt.write().unwrap().set_ex(ex); } ================================================ FILE: crates/vm/src/runtime/frame.rs ================================================ use crate::oop; use crate::runtime::DataArea; use crate::types::*; use classfile::{ConstantPool, U1}; use std::sync::Arc; pub struct Frame { pub frame_id: usize, //for debug pub class: ClassRef, //avoid lock class to access cp pub cp: ConstantPool, pub mir: MethodIdRef, pub code: Arc>, pub pc: std::sync::atomic::AtomicI32, pub ex_here: std::sync::atomic::AtomicBool, // The variable part of Frame is placed here pub area: DataArea, } // unsafe impl Sync for Frame {} //new impl Frame { pub fn new(mir: MethodIdRef, frame_id: usize) -> Self { let class = mir.method.class.clone(); let cp = { let cls_obj = class.extract_inst(); cls_obj.class_file.cp.clone() }; let pc = std::sync::atomic::AtomicI32::new(0); let ex_here = std::sync::atomic::AtomicBool::new(false); // trace!("method.code.is_some = {}", mir.method.code.is_some()); match &mir.method.code { Some(code) => { // trace!("max_locals = {}, max_stack = {}", code.max_locals, code.max_stack); let area = DataArea::new(code.max_stack as usize); let code = code.code.clone(); Self { frame_id, class, cp, mir, code, pc, ex_here, area, } } None => Self { frame_id, class, cp: Arc::new(Vec::new()), mir, code: Arc::new(vec![]), pc, ex_here, area: DataArea::new(0), }, } } } ================================================ FILE: crates/vm/src/runtime/init_vm.rs ================================================ use crate::oop; use crate::oop::{Class, Oop}; use crate::runtime::{self, require_class3}; use crate::types::JavaThreadRef; use crate::util; use crate::{native, new_br}; use classfile::consts::{ J_ARRAY_INDEX_OUT_OF_BOUNDS, J_CLASS, J_CLASS_NOT_FOUND, J_CLONEABLE, J_FIELD, J_INPUT_STREAM, J_INTERNAL_ERROR, J_IOEXCEPTION, J_METHOD_CTOR, J_NPE, J_OBJECT, J_PRINT_STREAM, J_SECURITY_MANAGER, J_SERIALIZABLE, J_STRING, J_SYSTEM, J_THREAD, J_THREAD_GROUP, J_THROWABLE, }; use std::borrow::BorrowMut; use std::sync::Arc; pub fn initialize_jvm() { initialize_vm_structs(); let thread_cls = oop::class::load_and_init(J_THREAD); let thread_group_cls = oop::class::load_and_init(J_THREAD_GROUP); let init_thread_oop = oop::Oop::new_inst(thread_cls.clone()); { let mut cls = thread_cls.get_mut_class(); //todo: getNativeHandler // let id = util::new_field_id(J_THREAD, b"eetop", b"J"); // cls.put_field_value2(init_thread_oop.clone(), id, oop::OopDesc::new_long(0)); //todo: define java::lang::ThreadPriority::NORMAL_PRIORITY let id = cls.get_field_id(&new_br("priority"), &new_br("I"), false); Class::put_field_value(init_thread_oop.extract_ref(), id, oop::Oop::new_int(5)); } // JavaMainThread is created with java_thread_obj none // Now we have created a thread for it. let jt = runtime::thread::current_java_thread(); jt.write() .unwrap() .set_java_thread_obj(init_thread_oop.clone()); // Create and construct the system thread group. let system_thread_group = oop::Oop::new_inst(thread_group_cls.clone()); let args = vec![system_thread_group.clone()]; runtime::invoke::invoke_ctor(thread_group_cls.clone(), new_br("()V"), args); let main_thread_group = oop::Oop::new_inst(thread_group_cls.clone()); { let mut cls = thread_cls.get_mut_class(); let id = cls.get_field_id(&new_br("group"), &new_br("Ljava/lang/ThreadGroup;"), false); Class::put_field_value(init_thread_oop.extract_ref(), id, main_thread_group.clone()); } let _ = oop::class::load_and_init(J_INPUT_STREAM); let _ = oop::class::load_and_init(J_PRINT_STREAM); let _ = oop::class::load_and_init(J_SECURITY_MANAGER); // Construct the main thread group let args = vec![ main_thread_group.clone(), Oop::Null, system_thread_group, util::oop::new_java_lang_string2("main"), ]; runtime::invoke::invoke_ctor( thread_group_cls, new_br("(Ljava/lang/Void;Ljava/lang/ThreadGroup;Ljava/lang/String;)V"), args, ); //todo: disable sun.security.util.Debug for the following operations //need to impl java_security_accesscontroller // let sun_debug_cls = do_init(b"sun/security/util/Debug", jt); let args = vec![ init_thread_oop, main_thread_group, util::oop::new_java_lang_string2("main"), ]; runtime::invoke::invoke_ctor( thread_cls, new_br("(Ljava/lang/ThreadGroup;Ljava/lang/String;)V"), args, ); hack_classes(); let init_system_classes_method = { let cls = require_class3(None, J_SYSTEM).unwrap(); let cls = cls.get_class(); cls.get_static_method(&new_br("initializeSystemClass"), &new_br("()V")) .unwrap() }; let mut jc = runtime::invoke::JavaCall::new_with_args(init_system_classes_method, vec![]); jc.invoke(None, false); //todo: re-enable sun.security.util.Debug //setup security let _ = oop::class::load_and_init(b"sun/security/provider/Sun"); let _ = oop::class::load_and_init(b"sun/security/rsa/SunRsaSign"); let _ = oop::class::load_and_init(b"com/sun/net/ssl/internal/ssl/Provider"); } fn initialize_vm_structs() { let class_obj = oop::class::load_and_init(J_CLASS); native::java_lang_Class::create_delayed_mirrors(); native::java_lang_Class::create_delayed_ary_mirrors(); let _ = oop::class::load_and_init(J_OBJECT); let string_cls = oop::class::load_and_init(J_STRING); { let cls = string_cls.get_class(); let fir = cls.get_field_id(&new_br("value"), &new_br("[C"), false); util::oop::set_java_lang_string_value_offset(fir.offset); } let integer_cls = oop::class::load_and_init(b"java/lang/Integer"); { let cls = integer_cls.get_class(); let fir = cls.get_field_id(&new_br("value"), &new_br("I"), false); util::oop::set_java_lang_integer_value_offset(fir.offset); } let _ = oop::class::load_and_init(J_CLONEABLE); let _ = oop::class::load_and_init(J_SERIALIZABLE); let _ = oop::class::load_and_init(J_NPE); let _ = oop::class::load_and_init(J_ARRAY_INDEX_OUT_OF_BOUNDS); let _ = oop::class::load_and_init(J_CLASS_NOT_FOUND); let _ = oop::class::load_and_init(J_INTERNAL_ERROR); let _ = oop::class::load_and_init(J_IOEXCEPTION); let _ = oop::class::load_and_init(J_FIELD); let _ = oop::class::load_and_init(J_METHOD_CTOR); let _ = oop::class::load_and_init(J_THROWABLE); //todo: //java::lang::reflect::Constructor::initialize //java::lang::reflect::Method::initialize { let mut cls = class_obj.get_mut_class(); let id = cls.get_field_id(&new_br("useCaches"), &new_br("Z"), true); cls.put_static_field_value(id, oop::Oop::new_int(1)); } } fn hack_classes() { let charset_cls = oop::class::load_and_init(b"java/nio/charset/Charset"); let ascii_charset_cls = oop::class::load_and_init(b"sun/nio/cs/US_ASCII"); let ascii_inst = oop::Oop::new_inst(ascii_charset_cls.clone()); let args = vec![ascii_inst.clone()]; runtime::invoke::invoke_ctor(ascii_charset_cls, new_br("()V"), args); { let mut cls = charset_cls.get_mut_class(); let id = cls.get_field_id( &new_br("defaultCharset"), &new_br("Ljava/nio/charset/Charset;"), true, ); cls.put_static_field_value(id, ascii_inst); } let encoder = oop::class::load_and_init(b"sun/nio/cs/StreamEncoder"); { let mut cls = encoder.get_mut_class(); cls.hack_as_native(b"forOutputStreamWriter", b"(Ljava/io/OutputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamEncoder;"); } let system = oop::class::load_and_init(b"java/lang/System"); { let mut cls = system.get_mut_class(); cls.hack_as_native(b"load", b"(Ljava/lang/String;)V"); //todo: support load lib cls.hack_as_native(b"loadLibrary", b"(Ljava/lang/String;)V"); //fixme: rm, just for debug // let id = util::new_method_id(b"getProperty", b"(Ljava/lang/String;)Ljava/lang/String;"); // cls.hack_as_native(id); } /* let mut mir = { let cls = encoder.lock().unwrap(); let id = util::new_method_id(b"forOutputStreamWriter", b"(Ljava/io/OutputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamEncoder;"); cls.get_static_method(id).unwrap() }; */ } ================================================ FILE: crates/vm/src/runtime/interp.rs ================================================ use crate::oop::{ self, consts as oop_consts, field, Class, ClassKind, Oop, OopPtr, TypeArrayDesc, ValueType, }; use crate::runtime::local::Local; use crate::runtime::stack::Stack; use crate::runtime::{ self, cmp, exception, require_class, require_class2, require_class3, thread, DataArea, Frame, JavaCall, }; use crate::types::*; use crate::util; use classfile::{ constant_pool::get_utf8 as get_cp_utf8, consts as cls_const, ClassFile, ConstantPool, ConstantPoolType, OpCode, U1, U2, }; use nix::sys::socket::SockType::Datagram; use std::borrow::BorrowMut; use std::cell::RefCell; use std::collections::HashMap; use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLockReadGuard}; macro_rules! array_store { ($ary:ident, $pos:ident, $v:ident) => { let len = $ary.len(); if ($pos < 0) || ($pos as usize >= len) { let msg = format!("length is {}, but index is {}", len, $pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { $ary[$pos as usize] = $v; } }; } macro_rules! iarray_load { ($area:ident, $ary:ident, $pos:ident) => { let len = $ary.len(); if ($pos < 0) || ($pos as usize >= len) { drop($area); let msg = format!("length is {}, but index is {}", len, $pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { $area.push_int($ary[$pos as usize] as i32); } }; } macro_rules! read_byte { ($pc:expr, $code:expr) => {{ let pc = $pc.fetch_add(1, Ordering::Relaxed); $code[pc as usize] }}; } macro_rules! read_i2 { ($pc:expr, $code:expr) => {{ let h = read_byte!($pc, $code) as i16; let l = read_byte!($pc, $code) as i16; (h << 8 | l) as i32 }}; } macro_rules! read_u1 { ($pc:expr, $code:expr) => {{ let pc = $pc.fetch_add(1, Ordering::Relaxed); $code[pc as usize] as usize }}; } macro_rules! read_u2 { ($pc:expr, $code:expr) => {{ read_u1!($pc, $code) << 8 | read_u1!($pc, $code) }}; } macro_rules! opcode_const { (null, $interp:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_null(); }; (m1, $interp:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const_m1(); }; (0, $interp:ident, $with_nop:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const0($with_nop); }; (1, $interp:ident, $with_nop:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const1($with_nop); }; (2, $interp:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const2(); }; (3, $interp:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const3(); }; (4, $interp:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const4(); }; (5, $interp:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_const5(); }; } macro_rules! opcode_load { (int, $interp:ident, $pos:expr) => { let v = $interp.local.get_int($pos); let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_int(v); }; (long, $interp:ident, $pos:expr) => { let v = $interp.local.get_long($pos); let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_long(v); }; (float, $interp:ident, $pos:expr) => { let v = $interp.local.get_float($pos); let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_float(v); }; (double, $interp:ident, $pos:expr) => { let v = $interp.local.get_double($pos); let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_double(v); }; (a, $interp:ident, $pos:expr) => { let v = $interp.local.get_ref($pos); let mut stack = $interp.frame.area.stack.borrow_mut(); stack.push_ref(v, false); }; } macro_rules! opcode_store { (int, $interp:ident, $pos:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v = stack.pop_int(); $interp.local.set_int($pos, v); }; (long, $interp:ident, $pos:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v = stack.pop_long(); $interp.local.set_long($pos, v); }; (float, $interp:ident, $pos:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v = stack.pop_float(); $interp.local.set_float($pos, v); }; (double, $interp:ident, $pos:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v = stack.pop_double(); $interp.local.set_double($pos, v); }; (a, $interp:ident, $pos:expr) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); $interp.local.set_ref($pos, v); }; } macro_rules! opcode_math_op { (int, $interp:ident, $op:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int(); let v = v1.$op(v2); stack.push_int(v); }; (long, $interp:ident, $op:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v2 = stack.pop_long(); let v1 = stack.pop_long(); let v = v1.$op(v2); stack.push_long(v); }; (float, $interp:ident, $op:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v2 = stack.pop_float(); let v1 = stack.pop_float(); let v = v1.$op(v2); stack.push_float(v); }; (double, $interp:ident, $op:ident) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v2 = stack.pop_double(); let v1 = stack.pop_double(); let v = v1.$op(v2); stack.push_double(v); }; } macro_rules! opcode_if { ($interp:ident, $op:tt) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int(); if v1 $op v2 { drop(stack); $interp.goto_by_offset_hardcoded(2); } else { let _ = $interp.frame.pc.fetch_add(2, Ordering::Relaxed); } }; ($interp:ident, $op:tt, 0) => { let mut stack = $interp.frame.area.stack.borrow_mut(); let v = stack.pop_int(); if v $op 0 { drop(stack); $interp.goto_by_offset_hardcoded(2); } else { let _ = $interp.frame.pc.fetch_add(2, Ordering::Relaxed); } }; } pub struct Interp<'a> { frame: RwLockReadGuard<'a, Box>, local: Local, cp: ConstantPool, code: Arc>, op_widen: bool, } impl<'a> Interp<'a> { pub fn new(frame: RwLockReadGuard<'a, Box>, local: Local) -> Self { let cp = frame.cp.clone(); let code = frame.code.clone(); let op_widen = false; Self { frame, local, cp, code, op_widen, } } } impl<'a> Interp<'a> { fn debug_op(&self, code: u8, op: OpCode) { let frame_id = self.frame.frame_id; trace!( "interp: {:?} ({}/{}) {:?}", op, code, frame_id, self.frame.mir.method ); } } impl<'a> Interp<'a> { pub fn run(&mut self) { let jt = runtime::thread::current_java_thread(); let codes = self.code.clone(); loop { let code = read_byte!(self.frame.pc, codes); let code = OpCode::from(code); match code { OpCode::athrow => { self.athrow(jt); break; } OpCode::ireturn => { self.ireturn(); break; } OpCode::lreturn => { self.lreturn(); break; } OpCode::freturn => { self.freturn(); break; } OpCode::dreturn => { self.dreturn(); break; } OpCode::areturn => { self.areturn(); break; } OpCode::return_void => { self.return_void(); break; } OpCode::nop => (), OpCode::aconst_null => { opcode_const!(null, self); } OpCode::iconst_m1 => { opcode_const!(m1, self); } OpCode::iconst_0 => { opcode_const!(0, self, false); } OpCode::iconst_1 => { opcode_const!(1, self, false); } OpCode::iconst_2 => { opcode_const!(2, self); } OpCode::iconst_3 => { opcode_const!(3, self); } OpCode::iconst_4 => { opcode_const!(4, self); } OpCode::iconst_5 => { opcode_const!(5, self); } OpCode::lconst_0 => { opcode_const!(0, self, true); } OpCode::lconst_1 => { opcode_const!(1, self, true); } OpCode::fconst_0 => { opcode_const!(0, self, false); } OpCode::fconst_1 => { opcode_const!(1, self, false); } OpCode::fconst_2 => { opcode_const!(2, self); } OpCode::dconst_0 => { opcode_const!(0, self, true); } OpCode::dconst_1 => { opcode_const!(1, self, true); } OpCode::bipush => self.bipush(), OpCode::sipush => self.sipush(), OpCode::ldc => self.ldc(), OpCode::ldc_w => self.ldc_w(), OpCode::ldc2_w => self.ldc2_w(), OpCode::iload => { let pos = self.opcode_pos(); opcode_load!(int, self, pos); } OpCode::lload => { let pos = self.opcode_pos(); opcode_load!(long, self, pos); } OpCode::fload => { let pos = self.opcode_pos(); opcode_load!(float, self, pos); } OpCode::dload => { let pos = self.opcode_pos(); opcode_load!(double, self, pos); } OpCode::aload => { let pos = self.opcode_pos(); opcode_load!(a, self, pos); } OpCode::iload_0 => { opcode_load!(int, self, 0); } OpCode::iload_1 => { opcode_load!(int, self, 1); } OpCode::iload_2 => { opcode_load!(int, self, 2); } OpCode::iload_3 => { opcode_load!(int, self, 3); } OpCode::lload_0 => { opcode_load!(long, self, 0); } OpCode::lload_1 => { opcode_load!(long, self, 1); } OpCode::lload_2 => { opcode_load!(long, self, 2); } OpCode::lload_3 => { opcode_load!(long, self, 3); } OpCode::fload_0 => { opcode_load!(float, self, 0); } OpCode::fload_1 => { opcode_load!(float, self, 1); } OpCode::fload_2 => { opcode_load!(float, self, 2); } OpCode::fload_3 => { opcode_load!(float, self, 3); } OpCode::dload_0 => { opcode_load!(double, self, 0); } OpCode::dload_1 => { opcode_load!(double, self, 1); } OpCode::dload_2 => { opcode_load!(double, self, 2); } OpCode::dload_3 => { opcode_load!(double, self, 3); } OpCode::aload_0 => { opcode_load!(a, self, 0); } OpCode::aload_1 => { opcode_load!(a, self, 1); } OpCode::aload_2 => { opcode_load!(a, self, 2); } OpCode::aload_3 => { opcode_load!(a, self, 3); } OpCode::iaload => self.iaload(), OpCode::laload => self.laload(), OpCode::faload => self.faload(), OpCode::daload => self.daload(), OpCode::aaload => self.aaload(), OpCode::baload => self.baload(), OpCode::caload => self.caload(), OpCode::saload => self.saload(), OpCode::istore => { let pos = self.opcode_pos(); opcode_store!(int, self, pos); } OpCode::lstore => { let pos = self.opcode_pos(); opcode_store!(long, self, pos); } OpCode::fstore => { let pos = self.opcode_pos(); opcode_store!(float, self, pos); } OpCode::dstore => { let pos = self.opcode_pos(); opcode_store!(double, self, pos); } OpCode::astore => { let pos = self.opcode_pos(); opcode_store!(a, self, pos); } OpCode::istore_0 => { opcode_store!(int, self, 0); } OpCode::istore_1 => { opcode_store!(int, self, 1); } OpCode::istore_2 => { opcode_store!(int, self, 2); } OpCode::istore_3 => { opcode_store!(int, self, 3); } OpCode::lstore_0 => { opcode_store!(long, self, 0); } OpCode::lstore_1 => { opcode_store!(long, self, 1); } OpCode::lstore_2 => { opcode_store!(long, self, 2); } OpCode::lstore_3 => { opcode_store!(long, self, 3); } OpCode::fstore_0 => { opcode_store!(float, self, 0); } OpCode::fstore_1 => { opcode_store!(float, self, 1); } OpCode::fstore_2 => { opcode_store!(float, self, 2); } OpCode::fstore_3 => { opcode_store!(float, self, 3); } OpCode::dstore_0 => { opcode_store!(double, self, 0); } OpCode::dstore_1 => { opcode_store!(double, self, 1); } OpCode::dstore_2 => { opcode_store!(double, self, 2); } OpCode::dstore_3 => { opcode_store!(double, self, 3); } OpCode::astore_0 => { opcode_store!(a, self, 0); } OpCode::astore_1 => { opcode_store!(a, self, 1); } OpCode::astore_2 => { opcode_store!(a, self, 2); } OpCode::astore_3 => { opcode_store!(a, self, 3); } OpCode::iastore => self.iastore(), OpCode::lastore => self.lastore(), OpCode::fastore => self.fastore(), OpCode::dastore => self.dastore(), OpCode::aastore => self.aastore(), OpCode::bastore => self.bastore(), OpCode::castore => self.castore(), OpCode::sastore => self.sastore(), OpCode::pop => self.pop(), OpCode::pop2 => self.pop2(), OpCode::dup => self.dup(), OpCode::dup_x1 => self.dup_x1(), OpCode::dup_x2 => self.dup_x2(), OpCode::dup2 => self.dup2(), OpCode::dup2_x1 => self.dup2_x1(), OpCode::dup2_x2 => self.dup2_x2(), OpCode::swap => self.swap(), OpCode::iadd => { opcode_math_op!(int, self, wrapping_add); } OpCode::ladd => { opcode_math_op!(long, self, wrapping_add); } OpCode::fadd => { use std::ops::Add; opcode_math_op!(float, self, add); } OpCode::dadd => { use std::ops::Add; opcode_math_op!(double, self, add); } OpCode::isub => { opcode_math_op!(int, self, wrapping_sub); } OpCode::lsub => { opcode_math_op!(long, self, wrapping_sub); } OpCode::fsub => { use std::ops::Sub; opcode_math_op!(float, self, sub); } OpCode::dsub => { use std::ops::Sub; opcode_math_op!(double, self, sub); } OpCode::imul => { opcode_math_op!(int, self, wrapping_mul); } OpCode::lmul => { opcode_math_op!(long, self, wrapping_mul); } OpCode::fmul => { use std::ops::Mul; opcode_math_op!(float, self, mul); } OpCode::dmul => { use std::ops::Mul; opcode_math_op!(double, self, mul); } OpCode::idiv => self.idiv(), OpCode::ldiv => self.ldiv(), OpCode::fdiv => self.fdiv(), OpCode::ddiv => self.ddiv(), OpCode::irem => self.irem(), OpCode::lrem => self.lrem(), OpCode::frem => self.frem(), OpCode::drem => self.drem(), OpCode::ineg => self.ineg(), OpCode::lneg => self.lneg(), OpCode::fneg => self.fneg(), OpCode::dneg => self.dneg(), OpCode::ishl => self.ishl(), OpCode::lshl => self.lshl(), OpCode::ishr => self.ishr(), OpCode::lshr => self.lshr(), OpCode::iushr => self.iushr(), OpCode::lushr => self.lushr(), OpCode::iand => { use std::ops::BitAnd; opcode_math_op!(int, self, bitand); } OpCode::land => { use std::ops::BitAnd; opcode_math_op!(long, self, bitand); } OpCode::ior => { use std::ops::BitOr; opcode_math_op!(int, self, bitor); } OpCode::lor => { use std::ops::BitOr; opcode_math_op!(long, self, bitor); } OpCode::ixor => { use std::ops::BitXor; opcode_math_op!(int, self, bitxor); } OpCode::lxor => { use std::ops::BitXor; opcode_math_op!(long, self, bitxor); } OpCode::iinc => self.iinc(), OpCode::i2l => self.i2l(), OpCode::i2f => self.i2f(), OpCode::i2d => self.i2d(), OpCode::l2i => self.l2i(), OpCode::l2f => self.l2f(), OpCode::l2d => self.l2d(), OpCode::f2i => self.f2i(), OpCode::f2l => self.f2l(), OpCode::f2d => self.f2d(), OpCode::d2i => self.d2i(), OpCode::d2l => self.d2l(), OpCode::d2f => self.d2f(), OpCode::i2b => self.i2b(), OpCode::i2c => self.i2c(), OpCode::i2s => self.i2s(), OpCode::lcmp => self.lcmp(), OpCode::fcmpl => self.fcmpl(), OpCode::fcmpg => self.fcmpg(), OpCode::dcmpl => self.dcmpl(), OpCode::dcmpg => self.dcmpg(), OpCode::ifeq => { opcode_if!(self, ==, 0); } OpCode::ifne => { opcode_if!(self, !=, 0); } OpCode::iflt => { opcode_if!(self, <, 0); } OpCode::ifge => { opcode_if!(self, >=, 0); } OpCode::ifgt => { opcode_if!(self, >, 0); } OpCode::ifle => { opcode_if!(self, <=, 0); } OpCode::if_icmpeq => { opcode_if!(self, ==); } OpCode::if_icmpne => { opcode_if!(self, !=); } OpCode::if_icmplt => { opcode_if!(self, <); } OpCode::if_icmpge => { opcode_if!(self, >=); } OpCode::if_icmpgt => { opcode_if!(self, >); } OpCode::if_icmple => { opcode_if!(self, <=); } OpCode::if_acmpeq => self.if_acmpeq(), OpCode::if_acmpne => self.if_acmpne(), OpCode::goto => self.goto(), OpCode::jsr => self.jsr(), OpCode::ret => self.ret(), OpCode::tableswitch => self.table_switch(), OpCode::lookupswitch => self.lookup_switch(), OpCode::getstatic => self.get_static(), OpCode::putstatic => self.put_static(), OpCode::getfield => self.get_field(), OpCode::putfield => self.put_field(), OpCode::invokevirtual => self.invoke_virtual(), OpCode::invokespecial => self.invoke_special(), OpCode::invokestatic => self.invoke_static(), OpCode::invokeinterface => self.invoke_interface(), OpCode::invokedynamic => self.invoke_dynamic(), OpCode::new => self.new_(), OpCode::newarray => self.new_array(), OpCode::anewarray => self.anew_array(), OpCode::arraylength => self.array_length(), OpCode::checkcast => self.check_cast(), OpCode::instanceof => self.instance_of(), OpCode::monitorenter => self.monitor_enter(), OpCode::monitorexit => self.monitor_exit(), OpCode::wide => self.wide(), OpCode::multianewarray => self.multi_anew_array(), OpCode::ifnull => self.if_null(), OpCode::ifnonnull => self.if_non_null(), OpCode::goto_w => self.goto_w(), OpCode::jsr_w => self.jsr_w(), _ => unreachable!(), } let is_meet_ex = thread::is_meet_ex(); if is_meet_ex { let mut th = jt.write().unwrap(); let ex = th.take_ex().unwrap(); drop(th); match self.try_handle_exception(ex) { Ok(_) => (), Err(ex) => { let mut th = jt.write().unwrap(); th.set_ex(ex); break; } } } } } } //helper methods impl<'a> Interp<'a> { fn load_constant(&self, pos: usize) { match &self.cp[pos] { ConstantPoolType::Integer { v } => { let mut stack = self.frame.area.stack.borrow_mut(); stack.push_int2(v) } ConstantPoolType::Float { v } => { let mut stack = self.frame.area.stack.borrow_mut(); stack.push_float2(v) } ConstantPoolType::Long { v } => { let mut stack = self.frame.area.stack.borrow_mut(); stack.push_long2(v) } ConstantPoolType::Double { v } => { let mut stack = self.frame.area.stack.borrow_mut(); stack.push_double2(v) } ConstantPoolType::String { string_index } => { let s = get_cp_utf8(&self.cp, *string_index as usize); let s = util::oop::new_java_lang_string3(s.as_slice()); let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(s, false); } ConstantPoolType::Class { name_index } => { let name = get_cp_utf8(&self.cp, *name_index as usize); let name = unsafe { std::str::from_utf8_unchecked(name.as_slice()) }; let cl = { self.frame.class.get_class().class_loader }; trace!("load_constant name={}, cl={:?}", name, cl); let class = runtime::require_class3(cl, name.as_bytes()).unwrap(); oop::class::init_class(&class); oop::class::init_class_fully(&class); let mirror = { class.get_class().get_mirror() }; let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(mirror, false); } _ => unreachable!(), } } #[inline] fn goto_abs(&self, pc: i32) { self.frame.pc.store(pc, Ordering::Relaxed); } #[inline] fn goto_by_offset(&self, branch: i32) { let _ = self.frame.pc.fetch_add(branch, Ordering::Relaxed); } #[inline] fn goto_by_offset_with_occupied(&self, branch: i32, occupied: i32) { self.goto_by_offset(branch); self.goto_by_offset(-(occupied - 1)); } #[inline] fn goto_by_offset_hardcoded(&self, occupied: i32) { let codes = &self.code; let pc = self.frame.pc.load(Ordering::Relaxed); let high = codes[pc as usize] as i16; let low = codes[(pc + 1) as usize] as i16; let branch = (high << 8) | low; self.goto_by_offset_with_occupied(branch as i32, occupied); } #[inline] fn goto_abs_with_occupied(&self, pc: i32, occupied: i32) { self.goto_abs(pc); self.goto_by_offset(-(occupied - 1)); } fn set_return(&self, v: Option) { let mut return_v = self.frame.area.return_v.borrow_mut(); *return_v = v; } fn get_field_helper(&self, receiver: Oop, idx: usize, is_static: bool) { let class = self.frame.class.extract_inst(); let fir = class.cp_cache.get_field(idx, is_static); debug_assert_eq!(fir.field.is_static(), is_static); trace!("get_field_helper={:?}, is_static={}", fir.field, is_static); let value_type = fir.field.value_type; let v = if is_static { let class = fir.field.class.get_class(); class.get_static_field_value(fir.clone()) } else { let rf = receiver.extract_ref(); Class::get_field_value2(rf, fir.offset) }; let with_nop = match value_type { ValueType::DOUBLE | ValueType::LONG => true, _ => false, }; let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(v, with_nop); } fn pop_value(&self, vt: ValueType) -> Oop { let mut stack = self.frame.area.stack.borrow_mut(); match vt { ValueType::INT | ValueType::SHORT | ValueType::CHAR | ValueType::BOOLEAN | ValueType::BYTE => { let v = stack.pop_int(); Oop::new_int(v) } ValueType::FLOAT => { let v = stack.pop_float(); Oop::new_float(v) } ValueType::DOUBLE => { let v = stack.pop_double(); Oop::new_double(v) } ValueType::LONG => { let v = stack.pop_long(); Oop::new_long(v) } ValueType::ARRAY | ValueType::OBJECT => stack.pop_ref(), _ => unreachable!(), } } fn put_field_helper(&self, idx: usize, is_static: bool) { let class = self.frame.class.extract_inst(); let fir = class.cp_cache.get_field(idx, is_static); debug_assert_eq!(fir.field.is_static(), is_static); trace!("put_field_helper={:?}, is_static={}", fir.field, is_static); let value_type = fir.field.value_type; // info!("value_type = {:?}", value_type); let v = self.pop_value(value_type); if is_static { let mut class = fir.field.class.get_mut_class(); class.put_static_field_value(fir.clone(), v); } else { let receiver = { let mut stack = self.frame.area.stack.borrow_mut(); stack.pop_ref() }; match receiver { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), _ => Class::put_field_value2(receiver.extract_ref(), fir.offset, v), } } } fn invoke_helper(&self, is_static: bool, idx: usize, force_no_resolve: bool) { let class = self.frame.class.extract_inst(); let mir = class.cp_cache.get_method(idx); let caller = match &mir.method.signature.retype { classfile::SignatureType::Void => None, _ => Some(&self.frame.area), }; debug_assert_eq!(mir.method.is_static(), is_static); if let Ok(mut jc) = runtime::invoke::JavaCall::new(&self.frame.area, mir) { jc.invoke(caller, force_no_resolve); } } pub fn check_cast_helper(&self, is_cast: bool) { let pc = &self.frame.pc; let codes = &self.code; let cp_idx = read_i2!(pc, codes); let target_cls = require_class2(cp_idx as U2, &self.cp).unwrap(); let obj_rf = self.pop_value(ValueType::OBJECT); let obj_rf_clone = obj_rf.clone(); let op_check_cast = |r: bool, obj_cls: ClassRef, target_cls: ClassRef| { if r { let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(obj_rf_clone, false); } else { let obj_name = { obj_cls.get_class().name.clone() }; let target_name = { target_cls.get_class().name.clone() }; let obj_name = String::from_utf8_lossy(obj_name.as_slice()).replace("/", "."); let target_name = String::from_utf8_lossy(target_name.as_slice()).replace("/", "."); let msg = format!("{} cannot be cast to {}", obj_name, target_name); exception::meet_ex(cls_const::J_CCE, Some(msg)); } }; let op_instance_of = |r: bool| { let mut stack = self.frame.area.stack.borrow_mut(); if r { stack.push_const1(false); } else { stack.push_const0(false); } }; match obj_rf { Oop::Null => { let mut stack = self.frame.area.stack.borrow_mut(); if is_cast { stack.push_ref(obj_rf, false); } else { stack.push_const0(false); } } Oop::Ref(rf) => { let rf = rf.get_raw_ptr(); unsafe { match &(*rf).v { oop::RefKind::Inst(inst) => { let obj_cls = inst.class.clone(); let r = cmp::instance_of(obj_cls.clone(), target_cls.clone()); if is_cast { op_check_cast(r, obj_cls, target_cls); } else { op_instance_of(r); } } oop::RefKind::Array(ary) => { let obj_cls = ary.class.clone(); let r = cmp::instance_of(obj_cls.clone(), target_cls.clone()); if is_cast { op_check_cast(r, obj_cls, target_cls); } else { op_instance_of(r); } } oop::RefKind::Mirror(mirror) => { //run here codes: //$JDK_TEST/Appendable/Basic.java //Will eventually call java.security.Security.getSpiClass ("MessageDigest"): //Exception in thread "main" java.lang.ClassCastException: java.security.MessageDigestSpi cannot be cast to java.lang.Class let obj_cls = mirror.target.clone().unwrap(); let target_name = target_cls.get_class().name.as_slice(); let r = target_name == b"java/lang/Class" || cmp::instance_of(obj_cls.clone(), target_cls.clone()); if is_cast { op_check_cast(r, obj_cls, target_cls); } else { op_instance_of(r); } } _ => unreachable!(), } } } _ => unreachable!(), } } #[inline] fn opcode_pos(&mut self) -> usize { let pc = &self.frame.pc; let codes = &self.code; let op_widen = self.op_widen; if op_widen { self.op_widen = false; read_u2!(pc, codes) } else { read_u1!(pc, codes) } } } //handle exception impl<'a> Interp<'a> { fn try_handle_exception(&self, ex: Oop) -> Result<(), Oop> { let ex_cls = { let rf = ex.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let handler = { let pc = self.frame.pc.load(Ordering::Relaxed); self.frame .mir .method .find_exception_handler(&self.cp, pc as u16, ex_cls) }; match handler { Some(pc) => { let mut stack = self.frame.area.stack.borrow_mut(); stack.clear(); stack.push_ref(ex, false); drop(stack); let line_num = self.frame.mir.method.get_line_num(pc); info!( "Found Exception Handler: line={}, frame_id={}, {:?}", line_num, self.frame.frame_id, self.frame.mir.method ); self.goto_abs(pc as i32); Ok(()) } None => { let pc = self.frame.pc.load(Ordering::Relaxed); let line_num = self.frame.mir.method.get_line_num(pc as u16); info!( "NotFound Exception Handler: line={}, frame_id={}, {:?}", line_num, self.frame.frame_id, self.frame.mir.method, ); Err(ex) } } } } //byte code impl impl<'a> Interp<'a> { #[inline] fn sipush(&self) { let pc = &self.frame.pc; let codes = &self.code; let v = read_i2!(pc, codes); let mut stack = self.frame.area.stack.borrow_mut(); stack.push_int(v); } #[inline] fn bipush(&self) { let pc = &self.frame.pc; let codes = &self.code; let v = (read_byte!(pc, codes) as i8) as i32; let mut stack = self.frame.area.stack.borrow_mut(); stack.push_int(v); } #[inline] fn ldc(&self) { let pc = &self.frame.pc; let codes = &self.code; let pos = read_u1!(pc, codes); self.load_constant(pos); } #[inline] fn ldc_w(&self) { let pc = &self.frame.pc; let codes = &self.code; let pos = read_u2!(pc, codes); self.load_constant(pos); } #[inline] fn ldc2_w(&self) { self.ldc_w(); } #[inline] fn iaload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_type_array(); let ary = ary.extract_ints(); iarray_load!(stack, ary, pos); } _ => unreachable!(), } } #[inline] fn saload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_type_array(); let ary = ary.extract_shorts(); iarray_load!(stack, ary, pos); } _ => unreachable!(), } } #[inline] fn caload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_type_array(); let ary = ary.extract_chars(); iarray_load!(stack, ary, pos); } _ => unreachable!(), } } #[inline] fn baload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let mut rf = (*rf).get_raw_ptr(); unsafe { let ary = (*rf).v.extract_type_array(); let len = ary.len(); if (pos < 0) || (pos as usize >= len) { let msg = format!("length is {}, but index is {}", len, pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { match ary { TypeArrayDesc::Byte(ary) => { let v = ary[pos as usize]; stack.push_int(v as i32); } TypeArrayDesc::Bool(ary) => { let v = ary[pos as usize]; stack.push_int(v as i32); } t => unreachable!("t = {:?}", t), } } } } _ => unreachable!(), } } #[inline] fn laload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_type_array(); let ary = ary.extract_longs(); let len = ary.len(); if (pos < 0) || (pos as usize >= len) { let msg = format!("length is {}, but index is {}", len, pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { let v = ary[pos as usize]; stack.push_long(v); } } _ => unreachable!(), } } #[inline] fn faload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_type_array(); let ary = ary.extract_floats(); let len = ary.len(); if (pos < 0) || (pos as usize >= len) { let msg = format!("length is {}, but index is {}", len, pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { let v = ary[pos as usize]; stack.push_float(v); } } _ => unreachable!(), } } #[inline] fn daload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_type_array(); let ary = ary.extract_doubles(); let len = ary.len(); if (pos < 0) || (pos as usize >= len) { let msg = format!("length is {}, but index is {}", len, pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { let v = ary[pos as usize]; stack.push_double(v); } } _ => unreachable!(), } } #[inline] fn aaload(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let pos = stack.pop_int(); let rf = stack.pop_ref(); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_array(); let ary = &ary.elements; let len = ary.len(); if (pos < 0) || (pos as usize >= len) { let msg = format!("length is {}, but index is {}", len, pos); exception::meet_ex(cls_const::J_ARRAY_INDEX_OUT_OF_BOUNDS, Some(msg)); } else { let v = ary[pos as usize].clone(); stack.push_ref(v, false); } } _ => unreachable!(), } } #[inline] fn bastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let mut rf = (*rf).get_mut_raw_ptr(); unsafe { let ary = (*rf).v.extract_mut_type_array(); match ary { oop::TypeArrayDesc::Byte(ary) => { let v = v as u8; array_store!(ary, pos, v); } oop::TypeArrayDesc::Bool(ary) => { let v = v as u8; array_store!(ary, pos, v); } _ => unreachable!(), } } } _ => unreachable!(), } } #[inline] fn castore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_chars(); let v = v as u16; array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn sastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_shorts(); let v = v as i16; array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn iastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_ints(); array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn lastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_long(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_longs(); array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn fastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_float(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_floats(); array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn dastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_double(); let pos = stack.pop_int(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_type_array(); let ary = ary.extract_mut_doubles(); array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn aastore(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); let pos = stack.pop_int(); let ary_rf = stack.pop_ref(); drop(stack); match ary_rf { Oop::Null => exception::meet_ex(cls_const::J_NPE, None), Oop::Ref(rf) => { let ary = rf.extract_mut_array(); let ary = &mut ary.elements; array_store!(ary, pos, v); } _ => unreachable!(), } } #[inline] fn pop(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.drop_top(); } #[inline] fn pop2(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.drop_top(); stack.drop_top(); } #[inline] fn dup(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.dup(); } #[inline] fn dup_x1(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.dup_x1(); } #[inline] fn dup_x2(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.dup_x2(); } #[inline] fn dup2(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.dup2(); } #[inline] fn dup2_x1(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.dup2_x1(); } #[inline] fn dup2_x2(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.dup2_x2(); } #[inline] fn swap(&self) { let mut stack = self.frame.area.stack.borrow_mut(); stack.swap(); } #[inline] fn idiv(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int(); if v2 == 0 { drop(stack); exception::meet_ex( cls_const::J_ARITHMETIC_EX, Some("divide by zero".to_string()), ); } else { stack.push_int(v1 / v2); } } #[inline] fn ldiv(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_long(); let v1 = stack.pop_long(); if v2 == 0 { drop(stack); exception::meet_ex( cls_const::J_ARITHMETIC_EX, Some("divide by zero".to_string()), ); } else { stack.push_long(v1 / v2); } } #[inline] fn fdiv(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_float(); let v1 = stack.pop_float(); if v2 == 0.0 { drop(stack); exception::meet_ex( cls_const::J_ARITHMETIC_EX, Some("divide by zero".to_string()), ); } else { stack.push_float(v1 / v2); } } #[inline] fn ddiv(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_double(); let v1 = stack.pop_double(); if v2 == 0.0 { drop(stack); exception::meet_ex( cls_const::J_ARITHMETIC_EX, Some("divide by zero".to_string()), ); } else { stack.push_double(v1 / v2); } } #[inline] fn irem(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int(); if v2 == 0 { drop(stack); exception::meet_ex( cls_const::J_ARITHMETIC_EX, Some("divide by zero".to_string()), ); } else { stack.push_int(v1 - (v1 / v2) * v2); } } #[inline] fn lrem(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_long(); let v1 = stack.pop_long(); if v2 == 0 { drop(stack); exception::meet_ex( cls_const::J_ARITHMETIC_EX, Some("divide by zero".to_string()), ); } else { stack.push_long(v1 - (v1 / v2) * v2); } } #[inline] fn frem(&self) { panic!("Use of deprecated instruction frem, please check your Java compiler"); } #[inline] fn drem(&self) { panic!("Use of deprecated instruction drem, please check your Java compiler"); } #[inline] fn ineg(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); stack.push_int(-v); } #[inline] fn lneg(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_long(); stack.push_long(-v); } #[inline] fn fneg(&self) { panic!("Use of deprecated instruction fneg, please check your Java compiler"); } #[inline] fn dneg(&self) { panic!("Use of deprecated instruction dneg, please check your Java compiler"); } #[inline] fn ishl(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int(); let s = v2 & 0x1F; stack.push_int(v1 << s); } #[inline] fn lshl(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_long(); let s = (v2 & 0x3F) as i64; stack.push_long(v1 << s); } #[inline] fn ishr(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int(); let s = v2 & 0x1F; stack.push_int(v1 >> s); } #[inline] fn lshr(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_long(); let s = (v2 & 0x3F) as i64; stack.push_long(v1 >> s); } #[inline] fn iushr(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_int() as u32; let s = (v2 & 0x1F) as u32; stack.push_int((v1 >> s) as i32); } #[inline] fn lushr(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_int(); let v1 = stack.pop_long() as u64; let s = (v2 & 0x3F) as u64; stack.push_long((v1 >> s) as i64); } #[inline] fn iinc(&mut self) { let pc = &self.frame.pc; let codes = &self.code; let op_widen = self.op_widen; let pos; let factor; if op_widen { self.op_widen = false; pos = read_u2!(pc, codes); factor = (read_u2!(pc, codes) as i16) as i32 } else { pos = read_u1!(pc, codes); factor = (read_byte!(pc, codes) as i8) as i32 }; let v = self.local.get_int(pos); let v = v.wrapping_add(factor); self.local.set_int(pos, v); } #[inline] fn i2l(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); stack.push_long(v as i64); } #[inline] fn i2f(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); stack.push_float(v as f32); } #[inline] fn i2d(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); stack.push_double(v as f64); } #[inline] fn l2i(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_long(); stack.push_int(v as i32); } #[inline] fn l2f(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_long(); stack.push_float(v as f32); } #[inline] fn l2d(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_long(); stack.push_double(v as f64); } #[inline] fn f2i(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_float(); if v.is_nan() { stack.push_int(0); } else if v.is_infinite() { if v.is_sign_positive() { stack.push_int(i32::MAX); } else { stack.push_int(i32::MIN); } } else { stack.push_int(v as i32); } } #[inline] fn f2l(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_float(); if v.is_nan() { stack.push_long(0); } else if v.is_infinite() { if v.is_sign_positive() { stack.push_long(i64::MAX); } else { stack.push_long(i64::MIN); } } else { stack.push_long(v as i64); } } #[inline] fn f2d(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_float(); stack.push_double(v as f64); } #[inline] fn d2i(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_double(); if v.is_nan() { stack.push_int(0); } else if v.is_infinite() { if v.is_sign_positive() { stack.push_int(i32::MAX); } else { stack.push_int(i32::MIN); } } else { stack.push_int(v as i32); } } #[inline] fn d2l(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_double(); if v.is_nan() { stack.push_long(0); } else if v.is_infinite() { if v.is_sign_positive() { stack.push_long(i64::MAX); } else { stack.push_long(i64::MIN); } } else { stack.push_long(v as i64); } } #[inline] fn d2f(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_double(); stack.push_float(v as f32); } #[inline] fn i2b(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let v = v as i8; stack.push_int(v as i32); } #[inline] fn i2c(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let v = v as u16; stack.push_int(v as i32); } #[inline] fn i2s(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let v = v as i16; stack.push_int(v as i32); } #[inline] fn lcmp(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v1 = stack.pop_long(); let v2 = stack.pop_long(); let v = match v1.cmp(&v2) { std::cmp::Ordering::Greater => -1, std::cmp::Ordering::Less => 1, std::cmp::Ordering::Equal => 0, }; stack.push_int(v); } #[inline] fn fcmpl(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v1 = stack.pop_float(); let v2 = stack.pop_float(); let v = if v1.is_nan() || v2.is_nan() { -1 } else if v1 > v2 { -1 } else if v1 < v2 { 1 } else { 0 }; stack.push_int(v); } #[inline] fn fcmpg(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v1 = stack.pop_float(); let v2 = stack.pop_float(); let v = if v1.is_nan() || v2.is_nan() { 1 } else if v1 > v2 { -1 } else if v1 < v2 { 1 } else { 0 }; stack.push_int(v); } #[inline] fn dcmpl(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v1 = stack.pop_double(); let v2 = stack.pop_double(); let v = if v1.is_nan() || v2.is_nan() { -1 } else if v1 > v2 { -1 } else if v1 < v2 { 1 } else { 0 }; stack.push_int(v); } #[inline] fn dcmpg(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v1 = stack.pop_double(); let v2 = stack.pop_double(); if v1.is_nan() || v2.is_nan() { stack.push_int(1); } else if v1 > v2 { stack.push_int(-1); } else if v1 < v2 { stack.push_int(1); } else { stack.push_int(0); } } #[inline] fn if_acmpeq(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_ref(); let v1 = stack.pop_ref(); if OopPtr::is_eq(&v1, &v2) { drop(stack); self.goto_by_offset_hardcoded(2); } else { let _ = self.frame.pc.fetch_add(2, Ordering::Relaxed); } } #[inline] fn if_acmpne(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v2 = stack.pop_ref(); let v1 = stack.pop_ref(); if !OopPtr::is_eq(&v1, &v2) { drop(stack); self.goto_by_offset_hardcoded(2); } else { let _ = self.frame.pc.fetch_add(2, Ordering::Relaxed); } } #[inline] fn goto(&self) { self.goto_by_offset_hardcoded(2); } #[inline] fn jsr(&self) { let _ = self.frame.pc.fetch_add(2, Ordering::Relaxed); panic!("Use of deprecated instruction jsr, please check your Java compiler"); } #[inline] fn ret(&mut self) { let pc = &self.frame.pc; let codes = &self.code; let op_widen = self.op_widen; let new_pc = if op_widen { self.op_widen = false; read_u2!(pc, codes) } else { read_u1!(pc, codes) }; pc.store(new_pc as i32, Ordering::Relaxed); } #[inline] fn table_switch(&self) { let pc = &self.frame.pc; let codes = &self.code; let mut bc = pc.load(Ordering::Relaxed) - 1; let origin_bc = bc; if bc % 4 != 0 { bc += (4 - bc % 4); } else { bc += 4; } let mut ptr = bc as usize; let default_byte = [codes[ptr], codes[ptr + 1], codes[ptr + 2], codes[ptr + 3]]; let default_byte = i32::from_be_bytes(default_byte); let low_byte = [ codes[ptr + 4], codes[ptr + 5], codes[ptr + 6], codes[ptr + 7], ]; let low_byte = i32::from_be_bytes(low_byte); let high_byte = [ codes[ptr + 8], codes[ptr + 9], codes[ptr + 10], codes[ptr + 11], ]; let high_byte = i32::from_be_bytes(high_byte); let num = high_byte - low_byte + 1; ptr += 12; // switch-case jump table let mut jump_table = Vec::with_capacity(num as usize); for pos in 0..num { let pos = [codes[ptr], codes[ptr + 1], codes[ptr + 2], codes[ptr + 3]]; let pos = i32::from_be_bytes(pos); let jump_pos = pos + origin_bc; ptr += 4; jump_table.push(jump_pos); } // default jump_table.push(default_byte + origin_bc); let top_value = { let mut stack = self.frame.area.stack.borrow_mut(); stack.pop_int() }; if (top_value > (jump_table.len() as i32 - 1 + low_byte)) || top_value < low_byte { self.goto_abs_with_occupied(*jump_table.last().unwrap() as i32, 1); } else { self.goto_abs_with_occupied( jump_table[(top_value - low_byte as i32) as usize] as i32, 1, ); } } #[inline] fn lookup_switch(&self) { let pc = &self.frame.pc; let codes = &self.code; let mut bc = pc.load(Ordering::Relaxed) - 1; let origin_bc = bc; if bc % 4 != 0 { bc += (4 - bc % 4); } else { bc += 4; } let mut ptr = bc as usize; let default_byte = [codes[ptr], codes[ptr + 1], codes[ptr + 2], codes[ptr + 3]]; let default_byte = u32::from_be_bytes(default_byte); let count = [ codes[ptr + 4], codes[ptr + 5], codes[ptr + 6], codes[ptr + 7], ]; let count = u32::from_be_bytes(count); ptr += 8; let mut jump_table: HashMap = HashMap::new(); for i in 0..count { let value = [codes[ptr], codes[ptr + 1], codes[ptr + 2], codes[ptr + 3]]; let value = u32::from_be_bytes(value); let position = [ codes[ptr + 4], codes[ptr + 5], codes[ptr + 6], codes[ptr + 7], ]; let position = u32::from_be_bytes(position) + origin_bc as u32; ptr += 8; jump_table.insert(value, position); } let top_value = { let mut stack = self.frame.area.stack.borrow_mut(); stack.pop_int() }; match jump_table.get(&(top_value as u32)) { Some(position) => self.goto_abs_with_occupied(*position as i32, 1), None => self.goto_abs_with_occupied(default_byte as i32 + origin_bc, 1), } } #[inline] fn ireturn(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_int(); let v = Oop::new_int(v); drop(stack); self.set_return(Some(v)); } #[inline] fn lreturn(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_long(); let v = Oop::new_long(v); drop(stack); self.set_return(Some(v)); } #[inline] fn freturn(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_float(); let v = Oop::new_float(v); drop(stack); self.set_return(Some(v)); } #[inline] fn dreturn(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_double(); let v = Oop::new_double(v); drop(stack); self.set_return(Some(v)); } #[inline] fn areturn(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); drop(stack); self.set_return(Some(v)); } #[inline] fn return_void(&self) { self.set_return(None); } #[inline] fn get_static(&self) { let pc = &self.frame.pc; let codes = &self.code; let cp_idx = read_u2!(pc, codes); self.get_field_helper(Oop::Null, cp_idx, true); } #[inline] fn put_static(&self) { let pc = &self.frame.pc; let codes = &self.code; let cp_idx = read_u2!(pc, codes); self.put_field_helper(cp_idx, true); } #[inline] fn get_field(&self) { let pc = &self.frame.pc; let codes = &self.code; let idx = read_u2!(pc, codes); let mut stack = self.frame.area.stack.borrow_mut(); let rf = stack.pop_ref(); drop(stack); match rf { Oop::Null => { exception::meet_ex(cls_const::J_NPE, None); } _ => { self.get_field_helper(rf, idx, false); } } } #[inline] fn put_field(&self) { let pc = &self.frame.pc; let codes = &self.code; let idx = read_u2!(pc, codes); self.put_field_helper(idx, false); } #[inline] fn invoke_virtual(&self) { let pc = &self.frame.pc; let codes = &self.code; let idx = read_u2!(pc, codes); self.invoke_helper(false, idx, false); } #[inline] fn invoke_special(&self) { let pc = &self.frame.pc; let codes = &self.code; let idx = read_u2!(pc, codes); self.invoke_helper(false, idx, true); } #[inline] fn invoke_static(&self) { let pc = &self.frame.pc; let codes = &self.code; let idx = read_u2!(pc, codes); self.invoke_helper(true, idx, true); } #[inline] fn invoke_interface(&self) { let pc = &self.frame.pc; let codes = &self.code; let cp_idx = read_u2!(pc, codes); let _count = read_u1!(pc, codes); let zero = read_u1!(pc, codes); if zero != 0 { warn!("interpreter: invalid invokeinterface: the value of the fourth operand byte must always be zero."); } self.invoke_helper(false, cp_idx, false); } #[inline] fn invoke_dynamic(&self) { //todo: impl unimplemented!() } #[inline] fn new_(&self) { let pc = &self.frame.pc; let codes = &self.code; let idx = read_u2!(pc, codes); let class = { match runtime::require_class2(idx as u16, &self.cp) { Some(class) => { oop::class::init_class(&class); oop::class::init_class_fully(&class); class } None => unreachable!("Cannot get class info from constant pool"), } }; let v = oop::Oop::new_inst(class); let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(v, false); } #[inline] fn new_array(&self) { let pc = &self.frame.pc; let codes = &self.code; let ary_type = read_byte!(pc, codes); let mut stack = self.frame.area.stack.borrow_mut(); let len = stack.pop_int(); if len < 0 { drop(stack); exception::meet_ex(cls_const::J_NASE, Some("length < 0".to_string())); } else { let len = len as usize; let ary = Oop::new_type_ary(ary_type, len); stack.push_ref(ary, false); } } #[inline] fn anew_array(&self) { let pc = &self.frame.pc; let codes = &self.code; let cp_idx = read_i2!(pc, codes); let mut stack = self.frame.area.stack.borrow_mut(); let length = stack.pop_int(); drop(stack); // info!("anew_array length={}", length); if length < 0 { exception::meet_ex(cls_const::J_NASE, Some("length < 0".to_string())); } else { let class = match runtime::require_class2(cp_idx as u16, &self.cp) { Some(class) => class, None => panic!("Cannot get class info from constant pool"), }; oop::class::init_class(&class); oop::class::init_class_fully(&class); let (name, cl) = { let class = class.get_class(); let t = class.get_class_kind_type(); let name = match t { oop::class::ClassKindType::Instance | oop::class::ClassKindType::ObjectAry => { let mut v = Vec::with_capacity(class.name.len() + 3); v.push(b'['); v.push(b'L'); v.extend_from_slice(class.name.as_slice()); v.push(b';'); v } oop::class::ClassKindType::TypAry => { let mut v = Vec::with_capacity(class.name.len() + 1); v.push(b'['); v.extend_from_slice(class.name.as_slice()); v } }; let name = Arc::new(name); (name, class.class_loader) }; trace!("anew_array name={}", unsafe { std::str::from_utf8_unchecked(name.as_slice()) }); match runtime::require_class(cl, &name) { Some(ary_cls_obj) => { oop::class::init_class(&ary_cls_obj); oop::class::init_class_fully(&ary_cls_obj); let ary = Oop::new_ref_ary(ary_cls_obj, length as usize); let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(ary, false); } None => unreachable!(), } } } #[inline] fn array_length(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); match v { Oop::Null => { drop(stack); exception::meet_ex(cls_const::J_NPE, None) } Oop::Ref(rf) => { let v = rf.get_raw_ptr(); unsafe { match &(*v).v { oop::RefKind::Array(ary) => { let len = ary.elements.len(); stack.push_int(len as i32); } oop::RefKind::TypeArray(ary) => { let len = ary.len(); stack.push_int(len as i32); } _ => unreachable!(), } } } _ => unreachable!(), } } #[inline] fn athrow(&self, jt: JavaThreadRef) { let mut stack = self.frame.area.stack.borrow_mut(); let ex = stack.pop_ref(); drop(stack); jt.write().unwrap().set_ex(ex); } #[inline] fn check_cast(&self) { self.check_cast_helper(true); } #[inline] fn instance_of(&self) { self.check_cast_helper(false); } #[inline] fn monitor_enter(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); drop(stack); match v { Oop::Null => { exception::meet_ex(cls_const::J_NPE, None); } Oop::Ref(v) => v.monitor_enter(), _ => unreachable!(), } } #[inline] fn monitor_exit(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let mut v = stack.pop_ref(); drop(stack); match v { Oop::Null => { exception::meet_ex(cls_const::J_NPE, None); } Oop::Ref(v) => v.monitor_exit(), _ => unreachable!(), } } #[inline] fn wide(&mut self) { self.op_widen = true; } #[inline] fn multi_anew_array(&self) { let pc = &self.frame.pc; let codes = &self.code; let cp_idx = read_u2!(pc, codes); let dimension = read_u1!(pc, codes); let mut lens = Vec::new(); let mut stack = self.frame.area.stack.borrow_mut(); for _ in 0..dimension { let sub = stack.pop_int(); //todo: check java/lang/NegativeArraySizeException lens.push(sub); } drop(stack); let cls = require_class2(cp_idx as u16, &self.cp).unwrap(); let ary = new_multi_object_array_helper(cls, &lens, 0); let mut stack = self.frame.area.stack.borrow_mut(); stack.push_ref(ary, false); } #[inline] fn if_null(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); match v { Oop::Null => { drop(stack); self.goto_by_offset_hardcoded(2) } _ => { let pc = &self.frame.pc; let _ = pc.fetch_add(2, Ordering::Relaxed); } } } #[inline] fn if_non_null(&self) { let mut stack = self.frame.area.stack.borrow_mut(); let v = stack.pop_ref(); match v { Oop::Null => { let pc = &self.frame.pc; let _ = pc.fetch_add(2, Ordering::Relaxed); } _ => { drop(stack); self.goto_by_offset_hardcoded(2) } } } #[inline] fn goto_w(&self) { let pc = &self.frame.pc; let _ = pc.fetch_add(4, Ordering::Relaxed); panic!("Use of deprecated instruction goto_w, please check your Java compiler") } #[inline] fn jsr_w(&self) { let pc = &self.frame.pc; let _ = pc.fetch_add(4, Ordering::Relaxed); panic!("Use of deprecated instruction jsr_w, please check your Java compiler") } #[inline] fn other_wise(&self) { let pc = &self.frame.pc; let codes = &self.frame.code; let pc = pc.load(Ordering::Relaxed); let pc = pc - 1; panic!( "Use of undefined bytecode: {} at {}", codes[pc as usize], pc ); } } fn new_multi_object_array_helper(cls: ClassRef, lens: &[i32], idx: usize) -> Oop { let length = lens[idx] as usize; let down_type = { let cls = cls.get_class(); match &cls.kind { oop::ClassKind::Instance(_) => unreachable!(), ClassKind::ObjectArray(obj_ary) => obj_ary.down_type.clone().unwrap(), ClassKind::TypeArray(typ_ary) => typ_ary.down_type.clone().unwrap(), } }; if idx < lens.len() - 1 { let mut elms = Vec::with_capacity(length); for i in 0..length { let e = new_multi_object_array_helper(down_type.clone(), lens, idx + 1); elms.push(e); } Oop::new_ref_ary2(cls, elms) } else { Oop::new_ref_ary(cls, length) } } ================================================ FILE: crates/vm/src/runtime/invoke.rs ================================================ use crate::native; use crate::native::JNINativeMethodStruct; use crate::oop::{self, Oop, ValueType}; use crate::runtime::local::Local; use crate::runtime::{self, exception, frame::Frame, thread, DataArea, Interp}; use crate::types::{ClassRef, FrameRef, JavaThreadRef, MethodIdRef}; use crate::util; use class_parser::MethodSignature; use classfile::{consts as cls_const, BytesRef, SignatureType}; use std::borrow::BorrowMut; use std::cell::RefCell; use std::sync::{Arc, Mutex}; pub struct JavaCall { pub mir: MethodIdRef, pub args: Vec, pub is_return_void: bool, } pub fn invoke_ctor(cls: ClassRef, desc: BytesRef, args: Vec) { let ctor = { let cls = cls.get_class(); cls.get_this_class_method(&util::S_INIT, &desc).unwrap() }; let mut jc = JavaCall::new_with_args(ctor, args); jc.invoke(None, false); } impl JavaCall { pub fn new_with_args(mir: MethodIdRef, args: Vec) -> Self { let is_return_void = mir.method.signature.retype == SignatureType::Void; Self { mir, args, is_return_void, } } pub fn new(caller: &DataArea, mir: MethodIdRef) -> Result { let mut args = build_args_from_caller_stack(&caller, &mir.method.signature); //insert 'this' value let has_this = !mir.method.is_static(); if has_this { let this = { let mut stack = caller.stack.borrow_mut(); stack.pop_ref() }; //check NPE if let Oop::Null = this { let cls_name = { let cls = mir.method.class.get_class(); cls.name.clone() }; error!("Java new failed, null this: {:?}", mir.method); //Fail fast, avoid a lot of logs, and it is not easy to locate the problem // panic!(); let jt = runtime::thread::current_java_thread(); let ex = exception::new(cls_const::J_NPE, None); let mut jt = jt.write().unwrap(); jt.set_ex(ex); return Err(()); } args.insert(0, this); } Ok(Self::new_with_args(mir, args)) } } impl JavaCall { //the 'caller' for store return value pub fn invoke(&mut self, caller: Option<&DataArea>, force_no_resolve: bool) { /* Do resolve again first, because you can override in a native way such as: UnixFileSystem override FileSystem public abstract boolean checkAccess(File f, int access); public native boolean checkAccess(File f, int access); */ self.resolve_virtual_method(force_no_resolve); self.debug(); if self.mir.method.is_native() { self.invoke_native(caller); } else { self.invoke_java(caller); } let jt = runtime::thread::current_java_thread(); let _ = jt.write().unwrap().frames.pop(); } } impl JavaCall { fn invoke_java(&mut self, caller: Option<&DataArea>) { self.prepare_sync(); let jt = runtime::thread::current_java_thread(); match self.prepare_frame() { Ok(frame) => { { jt.write().unwrap().frames.push(frame.clone()); } let local = self.build_local(); let frame_h = frame.try_read().unwrap(); let mut interp = Interp::new(frame_h, local); interp.run(); //if return void, not need set return value if !self.is_return_void && !thread::is_meet_ex() { let return_v = { let frame = frame.try_read().unwrap(); let area = frame.area.return_v.borrow(); area.clone() }; let caller = caller.unwrap(); let return_v = return_v.unwrap(); set_return(caller, &self.mir.method.signature.retype, return_v); } } Err(ex) => { jt.write().unwrap().set_ex(ex); } } self.fin_sync(); } fn invoke_native(&mut self, caller: Option<&DataArea>) { self.prepare_sync(); let jt = runtime::thread::current_java_thread(); let v = match self.prepare_frame() { Ok(frame) => { { jt.write().unwrap().frames.push(frame); } match &self.mir.native_impl { Some(method) => { let class = self.mir.method.class.clone(); let env = native::new_jni_env(class); method.invoke(env, &self.args) } None => { let package = self.mir.method.class.get_class().name.as_slice(); let desc = self.mir.method.desc.as_slice(); let name = self.mir.method.name.as_slice(); panic!( "Native method not found: {}:{}:{}", unsafe { std::str::from_utf8_unchecked(package) }, unsafe { std::str::from_utf8_unchecked(name) }, unsafe { std::str::from_utf8_unchecked(desc) }, ) } } } Err(ex) => Err(ex), }; match v { Ok(v) => { if !self.is_return_void && !thread::is_meet_ex() { let caller = caller.unwrap(); let return_v = v.unwrap(); set_return(caller, &self.mir.method.signature.retype, return_v); } } Err(ex) => jt.write().unwrap().set_ex(ex), } self.fin_sync(); } fn prepare_sync(&mut self) { if self.mir.method.is_synchronized() { if self.mir.method.is_static() { let class = self.mir.method.class.get_class(); class.monitor_enter(); } else { let v = self.args.first().unwrap(); let v = v.extract_ref(); v.monitor_enter(); } } } fn fin_sync(&mut self) { if self.mir.method.is_synchronized() { if self.mir.method.is_static() { let class = self.mir.method.class.get_class(); class.monitor_exit(); } else { let v = self.args.first().unwrap(); let v = v.extract_ref(); v.monitor_exit(); } } } fn prepare_frame(&mut self) -> Result { let jt = runtime::thread::current_java_thread(); let frame_len = { jt.read().unwrap().frames.len() }; if frame_len >= runtime::consts::THREAD_MAX_STACK_FRAMES { let ex = exception::new(cls_const::J_SOE, None); return Err(ex); } let frame_id = frame_len + 1; let frame = Frame::new(self.mir.clone(), frame_id); let frame_ref = new_sync_ref!(frame); Ok(frame_ref) } fn build_local(&self) -> Local { //JVM spec, 2.6.1 let max_locals = self.mir.method.get_max_locals(); let mut local = Local::new(max_locals); let mut slot_pos: usize = 0; for v in self.args.iter() { let step = match v { Oop::Int(v) => { local.set_int(slot_pos, *v); 1 } Oop::Float(v) => { local.set_float(slot_pos, *v); 1 } Oop::Double(v) => { local.set_double(slot_pos, *v); 2 } Oop::Long((v)) => { local.set_long(slot_pos, *v); 2 } _ => { local.set_ref(slot_pos, v.clone()); 1 } }; slot_pos += step; } local } fn resolve_virtual_method(&mut self, force_no_resolve: bool) { let resolve_again = if force_no_resolve { false } else { //todo: why is the value of 0 possible in acc_flags? /* This situation occurs when: java/util/regex/Matcher.java bool search(int from) boolean result = parentPattern.root.match(this, from, text); The acc_flags of the match method is 0, and what is found is java/util/regex/Patter$Node#match, Correct should use java/util/regex/Patter$Start#match */ self.mir.method.is_abstract() || (self.mir.method.is_public() && !self.mir.method.is_final()) || (self.mir.method.is_protected() && !self.mir.method.is_final()) || (self.mir.method.acc_flags == 0) }; trace!( "resolve_virtual_method resolve_again={}, acc_flags = {}", resolve_again, self.mir.method.acc_flags ); if resolve_again { let this = self.args.get(0).unwrap(); let rf = this.extract_ref(); let ptr = rf.get_raw_ptr(); unsafe { if let oop::RefKind::Inst(inst) = &(*ptr).v { let name = self.mir.method.name.clone(); let desc = self.mir.method.desc.clone(); let cls = inst.class.get_class(); match cls.get_virtual_method(&name, &desc) { Ok(mir) => self.mir = mir, _ => { let cls = self.mir.method.class.get_class(); warn!( "resolve again failed, {}:{}:{}, acc_flags = {}", String::from_utf8_lossy(cls.name.as_slice()), String::from_utf8_lossy(name.as_slice()), String::from_utf8_lossy(desc.as_slice()), self.mir.method.acc_flags ); } } } } } } fn debug(&self) { info!( "invoke method = {:?}, static={} native={} sync={}", self.mir.method, self.mir.method.is_static(), self.mir.method.is_native(), self.mir.method.is_synchronized() ); } } fn build_args_from_caller_stack(caller: &DataArea, sig: &MethodSignature) -> Vec { let mut caller = caller.stack.borrow_mut(); let mut args = Vec::with_capacity(sig.args.len() + 1); //build args from caller's stack, so should rev the signature args for it in sig.args.iter().rev() { let v = match it { SignatureType::Byte | SignatureType::Boolean | SignatureType::Int | SignatureType::Char | SignatureType::Short => { let v = caller.pop_int(); Oop::new_int(v) } SignatureType::Long => { let v = caller.pop_long(); Oop::new_long(v) } SignatureType::Float => { let v = caller.pop_float(); Oop::new_float(v) } SignatureType::Double => { let v = caller.pop_double(); Oop::new_double(v) } SignatureType::Object(_, _, _) | SignatureType::Array(_) => caller.pop_ref(), t => unreachable!("t = {:?}", t), }; args.push(v); } //the args built from caller's stack, should reverse args args.reverse(); args } pub fn set_return(caller: &DataArea, return_type: &SignatureType, v: Oop) { let with_nop = match return_type { SignatureType::Double | SignatureType::Long => true, _ => false, }; let mut stack = caller.stack.borrow_mut(); stack.push_ref(v, with_nop); } ================================================ FILE: crates/vm/src/runtime/local.rs ================================================ use crate::oop::{Oop, OopPtr}; use crate::runtime::Slot; use crate::util; pub struct Local { locals: Vec, } impl Local { pub fn new(size: usize) -> Self { let size = size + 1; let locals = vec![Slot::Nop; size]; Self { locals } } #[inline] pub fn set_int(&mut self, pos: usize, v: i32) { self.locals[pos] = Slot::I32(v); } #[inline] pub fn set_long(&mut self, pos: usize, v: i64) { self.locals[pos] = Slot::I64(v); } #[inline] pub fn set_float(&mut self, pos: usize, v: f32) { self.locals[pos] = Slot::F32(v); } #[inline] pub fn set_double(&mut self, pos: usize, v: f64) { self.locals[pos] = Slot::F64(v); } #[inline] pub fn set_ref(&mut self, pos: usize, v: Oop) { self.locals[pos] = Slot::Ref(v); } #[inline] pub fn get_int(&self, pos: usize) -> i32 { match self.locals.get(pos).unwrap() { Slot::I32(v) => *v, Slot::Ref(v) => OopPtr::java_lang_integer_value(v.extract_ref()), t => panic!("Illegal type {:?}", t), } } #[inline] pub fn get_long(&self, pos: usize) -> i64 { if let Slot::I64(v) = self.locals.get(pos).unwrap() { *v } else { panic!("Illegal type"); } } #[inline] pub fn get_float(&self, pos: usize) -> f32 { if let Slot::F32(v) = self.locals.get(pos).unwrap() { *v } else { panic!("Illegal type"); } } #[inline] pub fn get_double(&self, pos: usize) -> f64 { if let Slot::F64(v) = self.locals.get(pos).unwrap() { *v } else { panic!("Illegal type"); } } #[inline] pub fn get_ref(&self, pos: usize) -> Oop { match self.locals.get(pos) { Some(Slot::Ref(v)) => v.clone(), t => panic!("Illegal type = {:?}", t), } } } ================================================ FILE: crates/vm/src/runtime/method.rs ================================================ use crate::native::JNINativeMethod; use crate::oop::{self, ValueType}; use crate::runtime::local::Local; use crate::runtime::stack::Stack; use crate::runtime::{self, require_class2}; use crate::types::ClassRef; use crate::types::*; use crate::util::PATH_SEP; use crate::{native, util}; use class_parser::MethodSignature; use classfile::{ attributes::Code, attributes::LineNumber, constant_pool, consts, flags::*, AttributeType, BytesRef, ConstantPool, FieldInfo, MethodInfo, U2, }; use std::fmt; use std::fmt::Formatter; use std::ops::Deref; use std::sync::Arc; pub fn get_method_ref(cp: &ConstantPool, idx: usize) -> Result { let (tag, class_index, name_and_type_index) = constant_pool::get_method_ref(cp, idx); //load Method's Class, then init it let class = require_class2(class_index, cp).unwrap_or_else(|| { panic!( "Unknown method class {:?}", cp.get(class_index as usize) .expect("Missing item") .as_cp_item(cp) ) }); oop::class::init_class(&class); oop::class::init_class_fully(&class); let (name, desc) = constant_pool::get_name_and_type(cp, name_and_type_index as usize); let class = class.get_class(); trace!( "get_method_ref cls={}, name={}, desc={}", unsafe { std::str::from_utf8_unchecked(class.name.as_slice()) }, unsafe { std::str::from_utf8_unchecked(name.as_slice()) }, unsafe { std::str::from_utf8_unchecked(desc.as_slice()) }, ); if tag == consts::CONSTANT_METHOD_REF_TAG { // invokespecial, invokestatic and invokevirtual class.get_class_method(name, desc) } else { // invokeinterface class.get_interface_method(name, desc) } } #[derive(Clone)] pub struct MethodId { pub offset: usize, pub method: Method, pub native_impl: Option, } impl MethodId { pub fn new(offset: usize, method: Method) -> Arc { let native_impl = if method.is_native() { let package = method.class.get_class().name.as_slice(); let desc = method.desc.as_slice(); let name = method.name.as_slice(); native::find_symbol(package, name, desc) } else { None }; Arc::new(Self { offset, method, native_impl, }) } } #[derive(Clone)] pub struct Method { pub class: ClassRef, pub class_file: ClassFileRef, pub cls_name: BytesRef, pub name: BytesRef, pub desc: BytesRef, pub acc_flags: U2, pub signature: MethodSignature, pub code: Option, pub line_num_table: Vec, method_info_index: usize, } impl Method { pub fn new( cp: &ConstantPool, mi: &MethodInfo, class: ClassRef, class_file: ClassFileRef, method_info_index: usize, cls_name: BytesRef, ) -> Self { let name = constant_pool::get_utf8(cp, mi.name_index as usize).clone(); let desc = constant_pool::get_utf8(cp, mi.desc_index as usize).clone(); let acc_flags = mi.acc_flags; let signature = MethodSignature::new(desc.as_slice()); let code = mi.get_code(); let line_num_table = mi.get_line_number_table(); Self { class, class_file, cls_name, name, desc, acc_flags, signature, code, line_num_table, method_info_index, } } pub fn get_max_locals(&self) -> usize { match &self.code { Some(code) => code.max_locals as usize, None => 0, } } pub fn get_max_stack(&self) -> usize { match &self.code { Some(code) => code.max_stack as usize, None => 0, } } pub fn build_local(&self) -> Local { let max_locals = self.get_max_locals(); Local::new(max_locals) } pub fn build_stack(&self) -> Stack { let max_stack = self.get_max_stack(); Stack::new(max_stack) } pub fn find_exception_handler(&self, cp: &ConstantPool, pc: U2, ex: ClassRef) -> Option { if let Some(code) = &self.code { for e in code.exceptions.iter() { if e.contains(pc) { if e.is_finally() { return Some(e.handler_pc); } if let Some(class) = runtime::require_class2(e.catch_type, cp) { if runtime::cmp::instance_of(ex.clone(), class) { return Some(e.handler_pc); } } } } } None } pub fn get_line_num(&self, pc: U2) -> i32 { let mut best_bci = 0; let mut best_line = -1; for it in self.line_num_table.iter() { if it.start_pc == pc { return it.number as i32; } else if it.start_pc < pc && it.start_pc >= best_bci { best_bci = it.start_pc; best_line = it.number as i32; } } best_line } pub fn get_annotation(&self) -> Option> { let method_info = self.class_file.methods.get(self.method_info_index).unwrap(); util::attributes::assemble_annotation(&method_info.attrs) } pub fn get_param_annotation(&self) -> Option> { let method_info = self.class_file.methods.get(self.method_info_index).unwrap(); util::attributes::assemble_param_annotation(&method_info.attrs) } pub fn get_type_annotation(&self) -> Option> { let method_info = self.class_file.methods.get(self.method_info_index).unwrap(); util::attributes::assemble_type_annotation(&method_info.attrs) } pub fn get_annotation_default(&self) -> Option> { let method_info = self.class_file.methods.get(self.method_info_index).unwrap(); util::attributes::assemble_annotation_default(&method_info.attrs) } pub fn check_annotation(&self, name: &[u8]) -> bool { let method_info = self.class_file.methods.get(self.method_info_index).unwrap(); for it in method_info.attrs.iter() { if let AttributeType::RuntimeVisibleAnnotations { raw, annotations } = it { for it in annotations.iter() { if it.type_name.as_slice() == name { return true; } } } } false } pub fn is_public(&self) -> bool { self.acc_flags & ACC_PUBLIC != 0 } pub fn is_private(&self) -> bool { self.acc_flags & ACC_PRIVATE != 0 } pub fn is_protected(&self) -> bool { self.acc_flags & ACC_PROTECTED != 0 } pub fn is_final(&self) -> bool { self.acc_flags & ACC_FINAL != 0 } pub fn is_static(&self) -> bool { self.acc_flags & ACC_STATIC != 0 } pub fn is_synchronized(&self) -> bool { self.acc_flags & ACC_SYNCHRONIZED != 0 } pub fn is_native(&self) -> bool { self.acc_flags & ACC_NATIVE != 0 } pub fn is_abstract(&self) -> bool { self.acc_flags & ACC_ABSTRACT != 0 } pub fn is_interface(&self) -> bool { self.acc_flags & ACC_INTERFACE != 0 } } impl fmt::Debug for MethodId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let cls_name = unsafe { std::str::from_utf8_unchecked(self.method.cls_name.as_slice()) }; let name = unsafe { std::str::from_utf8_unchecked(self.method.name.as_slice()) }; let desc = unsafe { std::str::from_utf8_unchecked(self.method.desc.as_slice()) }; write!( f, "{}:{}:{}, offset = {}", cls_name, name, desc, self.offset ) } } impl fmt::Debug for Method { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let cls_name = unsafe { std::str::from_utf8_unchecked(self.cls_name.as_slice()) }; let name = unsafe { std::str::from_utf8_unchecked(self.name.as_slice()) }; let desc = unsafe { std::str::from_utf8_unchecked(self.desc.as_slice()) }; write!(f, "{}:{}:{}", cls_name, name, desc) } } ================================================ FILE: crates/vm/src/runtime/mod.rs ================================================ #![allow(unused)] pub use class_loader::{require_class, require_class2, require_class3, ClassLoader}; pub use class_path_manager::{ add_path as add_class_path, add_paths as add_class_paths, find_class as find_class_in_classpath, ClassPathResult, }; pub use constant_pool::ConstantPoolCache; pub use consts::THREAD_MAX_STACK_FRAMES; pub use dataarea::DataArea; pub use frame::Frame; pub use interp::Interp; pub use invoke::JavaCall; pub use slot::Slot; pub use sys_dic::{find as sys_dic_find, put as sys_dic_put}; pub use thread::JavaThread; mod class_loader; mod class_path_manager; pub mod cmp; mod constant_pool; mod consts; mod dataarea; pub mod exception; mod frame; mod init_vm; pub mod interp; pub mod invoke; mod local; pub mod method; mod slot; mod stack; mod sys_dic; pub mod thread; pub mod vm; pub fn init() { sys_dic::init(); class_path_manager::init(); } ================================================ FILE: crates/vm/src/runtime/slot.rs ================================================ use crate::oop::Oop; #[derive(Debug, Clone)] pub enum Slot { ConstM1, Const0, Const1, Const2, Const3, Const4, Const5, I32(i32), F32(f32), F64(f64), I64(i64), Ref(Oop), Nop, //for Stack long, double } ================================================ FILE: crates/vm/src/runtime/stack.rs ================================================ use crate::oop::{consts, Oop}; use crate::runtime::Slot; #[derive(Debug)] pub struct Stack { inner: Vec, } impl Stack { pub fn new(size: usize) -> Self { Self { inner: Vec::with_capacity(size), } } #[inline] pub fn push_int(&mut self, v: i32) { self.inner.push(Slot::I32(v)); } #[inline] pub fn push_int2(&mut self, v: &[u8; 4]) { let v = i32::from_be_bytes(*v); self.inner.push(Slot::I32(v)); } #[inline] pub fn push_float(&mut self, v: f32) { self.inner.push(Slot::F32(v)); } #[inline] pub fn push_float2(&mut self, v: &[u8; 4]) { let v = u32::from_be_bytes(*v); let v = f32::from_bits(v); self.inner.push(Slot::F32(v)); } #[inline] pub fn push_double(&mut self, v: f64) { self.push_nop(); self.inner.push(Slot::F64(v)); } #[inline] pub fn push_double2(&mut self, v: &[u8; 8]) { let v = u64::from_be_bytes(*v); let v = f64::from_bits(v); self.push_double(v); } #[inline] pub fn push_long(&mut self, v: i64) { self.push_nop(); self.inner.push(Slot::I64(v)); } #[inline] pub fn push_long2(&mut self, v: &[u8; 8]) { let v = i64::from_be_bytes(*v); self.push_long(v); } #[inline] pub fn push_null(&mut self) { self.inner.push(Slot::Ref(Oop::Null)); } #[inline] pub fn push_const_m1(&mut self) { self.inner.push(Slot::ConstM1); } /* double & long, with_nop = true */ #[inline] pub fn push_const0(&mut self, with_nop: bool) { if with_nop { self.push_nop(); } self.inner.push(Slot::Const0); } /* double & long, with_nop = true */ #[inline] pub fn push_const1(&mut self, with_nop: bool) { if with_nop { self.push_nop(); } self.inner.push(Slot::Const1); } #[inline] pub fn push_const2(&mut self) { self.inner.push(Slot::Const2); } #[inline] pub fn push_const3(&mut self) { self.inner.push(Slot::Const3); } #[inline] pub fn push_const4(&mut self) { self.inner.push(Slot::Const4); } #[inline] pub fn push_const5(&mut self) { self.inner.push(Slot::Const5); } #[inline] pub fn push_ref(&mut self, v: Oop, with_nop: bool) { if with_nop { self.push_nop(); } self.inner.push(Slot::Ref(v)); } #[inline] pub fn pop_int(&mut self) -> i32 { match self.inner.pop().unwrap() { Slot::ConstM1 => -1, Slot::Const0 => 0, Slot::Const1 => 1, Slot::Const2 => 2, Slot::Const3 => 3, Slot::Const4 => 4, Slot::Const5 => 5, Slot::I32(v) => v, Slot::Ref(v) => v.extract_int(), _ => panic!("Illegal type"), } } #[inline] pub fn pop_float(&mut self) -> f32 { match self.inner.pop().unwrap() { Slot::Const0 => 0.0, Slot::Const1 => 1.0, Slot::Const2 => 2.0, Slot::F32(v) => v, Slot::Ref(v) => v.extract_float(), _ => panic!("Illegal type"), } } #[inline] pub fn pop_double(&mut self) -> f64 { match self.inner.pop() { Some(v) => { self.pop_nop(); match v { Slot::Const0 => 0.0, Slot::Const1 => 1.0, Slot::F64(v) => v, Slot::Ref(v) => v.extract_double(), _ => panic!("Illegal type"), } } None => panic!("Empty Stack!"), } } #[inline] pub fn pop_long(&mut self) -> i64 { match self.inner.pop() { Some(v) => { self.pop_nop(); match v { Slot::Const0 => 0, Slot::Const1 => 1, Slot::I64(v) => v, Slot::Ref(v) => v.extract_long(), _ => panic!("Illegal type"), } } _ => panic!("Empty Stack!"), } } #[inline] pub fn pop_ref(&mut self) -> Oop { match self.inner.pop() { Some(Slot::Ref(v)) => v, t => panic!("Illegal type = {:?}", t), } } #[inline] pub fn drop_top(&mut self) { let _ = self.inner.pop(); } pub fn clear(&mut self) { self.inner.clear(); } #[inline] pub fn dup(&mut self) { let v = self.inner.pop().unwrap(); self.inner.push(v.clone()); self.inner.push(v); } #[inline] pub fn dup_x1(&mut self) { let v1 = self.inner.pop().unwrap(); let v2 = self.inner.pop().unwrap(); self.inner.push(v1.clone()); self.inner.push(v2); self.inner.push(v1); } #[inline] pub fn dup_x2(&mut self) { let v1 = self.inner.pop().unwrap(); let v2 = self.inner.pop().unwrap(); let v3 = self.inner.pop().unwrap(); self.inner.push(v1.clone()); self.inner.push(v3); self.inner.push(v2); self.inner.push(v1); } #[inline] pub fn dup2(&mut self) { let v1 = self.inner.pop().unwrap(); let v2 = self.inner.pop().unwrap(); self.inner.push(v2.clone()); self.inner.push(v1.clone()); self.inner.push(v2); self.inner.push(v1); } #[inline] pub fn dup2_x1(&mut self) { let v1 = self.inner.pop().unwrap(); let v2 = self.inner.pop().unwrap(); let v3 = self.inner.pop().unwrap(); self.inner.push(v2.clone()); self.inner.push(v1.clone()); self.inner.push(v3); self.inner.push(v2); self.inner.push(v1); } #[inline] pub fn dup2_x2(&mut self) { let v1 = self.inner.pop().unwrap(); let v2 = self.inner.pop().unwrap(); let v3 = self.inner.pop().unwrap(); let v4 = self.inner.pop().unwrap(); self.inner.push(v2.clone()); self.inner.push(v1.clone()); self.inner.push(v4); self.inner.push(v3); self.inner.push(v2); self.inner.push(v1); } #[inline] pub fn swap(&mut self) { let v1 = self.inner.pop().unwrap(); let v2 = self.inner.pop().unwrap(); self.inner.push(v1); self.inner.push(v2); } } impl Stack { fn push_nop(&mut self) { self.inner.push(Slot::Nop); } fn pop_nop(&mut self) { match self.inner.pop() { Some(Slot::Nop) => (), _ => panic!("Should be Nop!"), } } } ================================================ FILE: crates/vm/src/runtime/sys_dic.rs ================================================ use crate::types::ClassRef; use crate::util; use rustc_hash::FxHashMap; use std::sync::{Arc, Mutex}; type SystemDictionary = Mutex>; lazy_static! { static ref SYS_DIC: SystemDictionary = { Mutex::new(FxHashMap::default()) }; } pub fn put(key: &[u8], klass: ClassRef) { debug_assert!(!key.contains(&b'.')); let key = Vec::from(key); let key = unsafe { String::from_utf8_unchecked(key) }; let mut dict = SYS_DIC.lock().unwrap(); dict.insert(key, klass); } //key style: "sun/security/provider/Sun" pub fn find(key: &[u8]) -> Option { debug_assert!(!key.contains(&b'.')); let key = unsafe { std::str::from_utf8_unchecked(key) }; let dict = SYS_DIC.lock().unwrap(); dict.get(key).cloned() } pub fn init() { lazy_static::initialize(&SYS_DIC); } ================================================ FILE: crates/vm/src/runtime/thread/condvar.rs ================================================ use crate::runtime::thread::{mutex_raw, ReentrantMutex}; use std::cell::UnsafeCell; use std::time::Duration; pub struct Condvar { inner: UnsafeCell, } unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} const TIMESPEC_MAX: libc::timespec = libc::timespec { tv_sec: ::max_value(), tv_nsec: 1_000_000_000 - 1, }; fn saturating_cast_to_time_t(value: u64) -> libc::time_t { if value > ::max_value() as u64 { ::max_value() } else { value as libc::time_t } } impl Condvar { pub const fn new() -> Condvar { // Might be moved and address is changing it is better to avoid // initialization of potentially opaque OS data before it landed Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER), } } /// # Safety /// todo: This function should really be documented #[cfg(any( target_os = "macos", target_os = "ios", target_os = "l4re", target_os = "android", target_os = "hermit" ))] pub unsafe fn init(&mut self) {} #[cfg(not(any( target_os = "macos", target_os = "ios", target_os = "l4re", target_os = "android", target_os = "hermit" )))] pub unsafe fn init(&mut self) { use std::mem; let mut attr: libc::pthread_condattr_t = mem::uninitialized(); let r = libc::pthread_condattr_init(&mut attr); assert_eq!(r, 0); let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC); assert_eq!(r, 0); let r = libc::pthread_cond_init(self.inner.get(), &attr); assert_eq!(r, 0); let r = libc::pthread_condattr_destroy(&mut attr); assert_eq!(r, 0); } /// # Safety /// todo: This function should really be documented #[inline] pub unsafe fn notify_one(&self) { let r = libc::pthread_cond_signal(self.inner.get()); debug_assert_eq!(r, 0); } /// # Safety /// todo: This function should really be documented #[inline] pub unsafe fn notify_all(&self) { let r = libc::pthread_cond_broadcast(self.inner.get()); debug_assert_eq!(r, 0); } /// # Safety /// todo: This function should really be documented #[inline] pub unsafe fn wait(&self, mutex: &ReentrantMutex) { let r = libc::pthread_cond_wait(self.inner.get(), mutex_raw(mutex)); debug_assert_eq!(r, 0); } // This implementation is used on systems that support pthread_condattr_setclock // where we configure condition variable to use monotonic clock (instead of // default system clock). This approach avoids all problems that result // from changes made to the system time. #[cfg(not(any( target_os = "macos", target_os = "ios", target_os = "android", target_os = "hermit" )))] pub unsafe fn wait_timeout(&self, mutex: &ReentrantMutex, dur: Duration) -> bool { use std::mem; let mut now: libc::timespec = mem::zeroed(); let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now); assert_eq!(r, 0); // Nanosecond calculations can't overflow because both values are below 1e9. let nsec = dur.subsec_nanos() + now.tv_nsec as u32; let sec = saturating_cast_to_time_t(dur.as_secs()) .checked_add((nsec / 1_000_000_000) as libc::time_t) .and_then(|s| s.checked_add(now.tv_sec)); let nsec = nsec % 1_000_000_000; let timeout = sec .map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _, }) .unwrap_or(TIMESPEC_MAX); let r = libc::pthread_cond_timedwait(self.inner.get(), mutex_raw(mutex), &timeout); assert!(r == libc::ETIMEDOUT || r == 0); r == 0 } /// # Safety /// todo: This function should really be documented // This implementation is modeled after libcxx's condition_variable // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 #[cfg(any( target_os = "macos", target_os = "ios", target_os = "android", target_os = "hermit" ))] pub unsafe fn wait_timeout(&self, mutex: &ReentrantMutex, mut dur: Duration) -> bool { use std::ptr; use std::time::Instant; // 1000 years let max_dur = Duration::from_secs(1000 * 365 * 86400); if dur > max_dur { // OSX implementation of `pthread_cond_timedwait` is buggy // with super long durations. When duration is greater than // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` // in macOS Sierra return error 316. // // This program demonstrates the issue: // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c // // To work around this issue, and possible bugs of other OSes, timeout // is clamped to 1000 years, which is allowable per the API of `wait_timeout` // because of spurious wakeups. dur = max_dur; } // First, figure out what time it currently is, in both system and // stable time. pthread_cond_timedwait uses system time, but we want to // report timeout based on stable time. let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0, }; let stable_now = Instant::now(); let r = libc::gettimeofday(&mut sys_now, ptr::null_mut()); debug_assert_eq!(r, 0); let nsec = dur.subsec_nanos() as libc::c_long + (sys_now.tv_usec * 1000) as libc::c_long; let extra = (nsec / 1_000_000_000) as libc::time_t; let nsec = nsec % 1_000_000_000; let seconds = saturating_cast_to_time_t(dur.as_secs()); let timeout = sys_now .tv_sec .checked_add(extra) .and_then(|s| s.checked_add(seconds)) .map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec, }) .unwrap_or(TIMESPEC_MAX); // And wait! let r = libc::pthread_cond_timedwait(self.inner.get(), mutex_raw(mutex), &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); // ETIMEDOUT is not a totally reliable method of determining timeout due // to clock shifts, so do the check ourselves stable_now.elapsed() < dur } /// # Safety /// todo: This function should really be documented #[inline] #[cfg(not(target_os = "dragonfly"))] pub unsafe fn destroy(&self) { let r = libc::pthread_cond_destroy(self.inner.get()); debug_assert_eq!(r, 0); } #[inline] #[cfg(target_os = "dragonfly")] pub unsafe fn destroy(&self) { let r = libc::pthread_cond_destroy(self.inner.get()); // On DragonFly pthread_cond_destroy() returns EINVAL if called on // a condvar that was just initialized with // libc::PTHREAD_COND_INITIALIZER. Once it is used or // pthread_cond_init() is called, this behaviour no longer occurs. debug_assert!(r == 0 || r == libc::EINVAL); } } ================================================ FILE: crates/vm/src/runtime/thread/java_thread.rs ================================================ use crate::oop::{self, consts, Oop}; use crate::types::{FrameRef, JavaThreadRef}; use std::cell::RefCell; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; thread_local! { pub static THREAD: RefCell = RefCell::new(JavaThread::main()); pub static IS_MEET_EX: AtomicBool = AtomicBool::new(false); } pub fn current_java_thread() -> JavaThreadRef { THREAD.with(|t| t.borrow().clone()) } #[inline] pub fn is_meet_ex() -> bool { IS_MEET_EX.with(|v| v.load(Ordering::Relaxed)) } #[inline] fn set_meet_ex(val: bool) { IS_MEET_EX.with(|v| v.store(val, Ordering::Relaxed)); } pub struct JavaThread { pub frames: Vec, in_safe_point: bool, pub java_thread_obj: Option, pub ex: Option, pub is_alive: bool, pub eetop: i64, pub tag: String, //for debug } impl JavaThread { pub fn new(tag: Option, eetop: i64) -> JavaThreadRef { let tag = tag.unwrap_or_else(|| format!("thread-{}", eetop)); let t = Self { frames: Vec::new(), in_safe_point: false, java_thread_obj: None, ex: None, is_alive: false, eetop, tag, }; Arc::new(RwLock::new(Box::new(t))) } pub fn main() -> JavaThreadRef { JavaThread::new(Some("main".to_string()), 0) } pub fn set_java_thread_obj(&mut self, obj: Oop) { self.java_thread_obj = Some(obj); } } //exception impl JavaThread { pub fn set_ex(&mut self, ex: Oop) { set_meet_ex(true); self.ex = Some(ex); } pub fn take_ex(&mut self) -> Option { set_meet_ex(false); self.ex.take() } } ================================================ FILE: crates/vm/src/runtime/thread/main.rs ================================================ use crate::oop::{self, Class, Oop, OopPtr}; use crate::runtime::thread::thread_pool; use crate::runtime::{self, init_vm, vm, DataArea, JavaCall, JavaThread}; use crate::types::{ClassRef, FrameRef, JavaThreadRef, MethodIdRef}; use crate::{new_br, util}; use std::borrow::Borrow; pub struct MainThread { pub class: String, pub args: Vec, dispatch_uncaught_exception_called: bool, } impl MainThread { pub fn new(class: String, args: Vec) -> Self { Self { class, args, dispatch_uncaught_exception_called: false, } } pub fn run(&mut self) { let vm = vm::VM::new(3); //attach 'main' thread vm.threads.attach_current_thread(); info!("init vm start"); init_vm::initialize_jvm(); info!("init vm end"); let main_class = oop::class::load_and_init(self.class.as_bytes()); let mir = { let cls = main_class.get_class(); /* path info should be included in "--cp", and avoid same class load 2 times, otherwise, "" invoked 2 times. For example: "MyFile.java": private static File gf = newFile(); if allowed, as follows: "cargo run -- --cp $JDK:$MY_TEST test/with_package/my.ns.HelloWorld" will cause "" invoked 2 times, "newFile()" invoked 2 times, maybe create 2 files. should be like this: "cargo run -- --cp $JDK:$MY_TEST:test/with_package my.ns.HelloWorld" */ if self.class.as_bytes() != cls.name.as_slice() { panic!("Error: Could not find or load main class {}", self.class); } cls.get_static_method(&new_br("main"), &new_br("([Ljava/lang/String;)V")) }; let jt = runtime::thread::current_java_thread(); match mir { Ok(mir) => { let args = self.build_main_arg(); let mut jc = JavaCall::new_with_args(mir, args); jt.write().unwrap().is_alive = true; jc.invoke(None, true); jt.write().unwrap().is_alive = false; } _ => unreachable!("NotFound \"main\""), } if jt.read().unwrap().ex.is_some() { self.uncaught_ex(main_class); } //detach main thread vm.threads.detach_current_thread(); vm.threads.join_all(); } } impl MainThread { fn build_main_arg(&self) -> Vec { let args = self .args .iter() .map(|it| util::oop::new_java_lang_string2(it)) .collect(); //build ArrayOopDesc let ary_str_class = runtime::require_class3(None, b"[Ljava/lang/String;").unwrap(); vec![Oop::new_ref_ary2(ary_str_class, args)] } fn uncaught_ex(&mut self, main_cls: ClassRef) { if self.dispatch_uncaught_exception_called { self.uncaught_ex_internal(); } else { self.dispatch_uncaught_exception_called = true; self.call_dispatch_uncaught_exception(main_cls); } } fn call_dispatch_uncaught_exception(&mut self, main_cls: ClassRef) { let jt = runtime::thread::current_java_thread(); let v = { let jt = jt.read().unwrap(); jt.java_thread_obj.clone() }; match v { Some(v) => { let cls = { let rf = v.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let mir = { let cls = cls.get_class(); cls.get_this_class_method( &new_br("dispatchUncaughtException"), &new_br("(Ljava/lang/Throwable;)V"), ) }; match mir { Ok(mir) => { let ex = { let mut jt = jt.write().unwrap(); jt.take_ex().unwrap() }; let args = vec![v, ex]; let mut jc = JavaCall::new_with_args(mir, args); jc.invoke(None, false); } _ => self.uncaught_ex_internal(), } } None => self.uncaught_ex_internal(), } } fn uncaught_ex_internal(&mut self) { let jt = runtime::thread::current_java_thread(); let ex = { let mut jt = jt.write().unwrap(); jt.take_ex().unwrap() }; let cls = { let rf = ex.extract_ref(); let inst = rf.extract_inst(); inst.class.clone() }; let detail_message = { let fid = { let cls = cls.get_class(); cls.get_field_id( &new_br("detailMessage"), &new_br("Ljava/lang/String;"), false, ) }; let v = Class::get_field_value(ex.extract_ref(), fid); OopPtr::java_lang_string(v.extract_ref()) }; let name = { let cls = cls.get_class(); cls.name.clone() }; let cls_name = String::from_utf8_lossy(name.as_slice()); error!("Name={}, detailMessage={}", cls_name, detail_message); } } ================================================ FILE: crates/vm/src/runtime/thread/mod.rs ================================================ mod condvar; mod java_thread; mod main; mod mutex; mod thread_pool; mod threads; pub use condvar::Condvar; pub use java_thread::current_java_thread; pub use java_thread::JavaThread; pub use java_thread::THREAD; pub use main::MainThread; pub use mutex::raw as mutex_raw; pub use mutex::ReentrantMutex; pub use thread_pool::ThreadPool; pub use threads::Threads; pub use java_thread::is_meet_ex; ================================================ FILE: crates/vm/src/runtime/thread/mutex.rs ================================================ use std::cell::UnsafeCell; use std::mem; use std::mem::MaybeUninit; /// # Safety pub unsafe fn raw(m: &ReentrantMutex) -> *mut libc::pthread_mutex_t { m.inner.get() } pub struct ReentrantMutex { inner: UnsafeCell, } unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { /// # Safety /// todo: This function should really be documented pub unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: mem::uninitialized(), } } /// # Safety /// todo: This function should really be documented pub unsafe fn init(&mut self) { let mut attr = MaybeUninit::::uninit(); let mut ptr_attr = attr.as_mut_ptr(); let result = libc::pthread_mutexattr_init(ptr_attr as *mut _); debug_assert_eq!(result, 0); let result = libc::pthread_mutexattr_settype(ptr_attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE); debug_assert_eq!(result, 0); let result = libc::pthread_mutex_init(self.inner.get(), ptr_attr as *const _); debug_assert_eq!(result, 0); let result = libc::pthread_mutexattr_destroy(ptr_attr as *mut _); debug_assert_eq!(result, 0); } /// # Safety /// todo: This function should really be documented pub unsafe fn lock(&self) { let result = libc::pthread_mutex_lock(self.inner.get()); debug_assert_eq!(result, 0); } /// # Safety /// todo: This function should really be documented #[inline] pub unsafe fn try_lock(&self) -> bool { libc::pthread_mutex_trylock(self.inner.get()) == 0 } /// # Safety /// todo: This function should really be documented pub unsafe fn unlock(&self) { let result = libc::pthread_mutex_unlock(self.inner.get()); debug_assert_eq!(result, 0); } /// # Safety /// todo: This function should really be documented pub unsafe fn destroy(&self) { let result = libc::pthread_mutex_destroy(self.inner.get()); debug_assert_eq!(result, 0); } } ================================================ FILE: crates/vm/src/runtime/thread/thread_pool.rs ================================================ use crate::types::JavaThreadRef; use std::sync::mpsc; use std::sync::{Arc, Condvar, Mutex}; use std::thread; pub struct ThreadPool { workers: Vec, sender: mpsc::Sender, } enum Message { NewJob(Job), Terminate, } trait FnBox { fn call_box(self: Box); } impl FnBox for F { fn call_box(self: Box) { (*self)() } } type Job = Box; impl ThreadPool { /// Create a new ThreadPool. /// /// The size is the number of threads in the pool. /// /// # Panics /// /// The `new` function will panic if the size is zero. pub fn new(size: usize) -> ThreadPool { assert!(size > 0); let (sender, receiver) = mpsc::channel(); let receiver = Arc::new(Mutex::new(receiver)); let mut workers = Vec::with_capacity(size); for id in 0..size { workers.push(Worker::new(id, Arc::clone(&receiver))); } ThreadPool { workers, sender } } pub fn execute(&self, f: F) where F: FnOnce() + Send + 'static, { let job = Box::new(f); self.sender.send(Message::NewJob(job)).unwrap(); } } impl Drop for ThreadPool { fn drop(&mut self) { info!("Sending terminate message to all workers."); for _ in &mut self.workers { self.sender.send(Message::Terminate).unwrap(); } info!("Shutting down all workers."); for worker in &mut self.workers { info!("Shutting down worker {}", worker.id); if let Some(thread) = worker.thread.take() { thread.join().unwrap(); } } } } struct Worker { id: usize, thread: Option>, } impl Worker { fn new(id: usize, receiver: Arc>>) -> Worker { let thread = thread::spawn(move || loop { let message = receiver.lock().unwrap().recv().unwrap(); match message { Message::NewJob(job) => { warn!("Worker-{} got a job, executing", id); job.call_box(); warn!("Worker-{}, fin", id); } Message::Terminate => { warn!("Worker {} was told to terminate.", id); break; } } }); Worker { id, thread: Some(thread), } } } ================================================ FILE: crates/vm/src/runtime/thread/threads.rs ================================================ use crate::runtime; use crate::runtime::thread::ThreadPool; use crate::types::JavaThreadRef; use std::borrow::Borrow; use std::sync::atomic::{AtomicBool, AtomicI64, Ordering}; use std::sync::{Arc, Condvar, Mutex}; pub struct Threads { pool: Mutex, threads: Mutex>, cond_join: Condvar, next_id: AtomicI64, } impl Threads { pub fn new(thread_pool_count: usize) -> Threads { Threads { pool: Mutex::new(ThreadPool::new(thread_pool_count)), threads: Mutex::new(Vec::new()), cond_join: Condvar::new(), next_id: AtomicI64::new(1), } } } impl Threads { pub fn next_id(&self) -> i64 { self.next_id.fetch_add(1, Ordering::SeqCst) } pub fn attach_current_thread(&self) { runtime::thread::THREAD.with(|thread| { let mut threads = self.threads.lock().unwrap(); threads.push(thread.borrow().clone()); }); } pub fn attach_java_thread(&self, thread: JavaThreadRef) { let mut threads = self.threads.lock().unwrap(); threads.push(thread); } pub fn detach_current_thread(&self) { runtime::thread::THREAD.with(|thread| { let mut threads = self.threads.lock().unwrap(); threads.retain(|elem| !Arc::ptr_eq(elem, &*thread.borrow())); self.cond_join.notify_all(); }); } pub fn find_java_thread(&self, eetop: i64) -> Option { let mut threads = self.threads.lock().unwrap(); threads .iter() .find(|t| t.read().unwrap().eetop == eetop) .cloned() } pub fn join_all(&self) { let mut threads = self.threads.lock().unwrap(); while threads.len() > 0 { threads = self.cond_join.wait(threads).unwrap(); } } pub fn spawn_java_thread(&self, f: F) { let pool = self.pool.lock().unwrap(); pool.execute(f); } } ================================================ FILE: crates/vm/src/runtime/vm.rs ================================================ use crate::runtime::thread::Threads; use std::ptr; static mut VM_GLOBAL: *const u8 = ptr::null(); pub fn get_vm() -> &'static VM { unsafe { &*(VM_GLOBAL as *const VM) } } pub fn set_vm(vm: &VM) { let ptr = vm as *const _ as *const u8; unsafe { VM_GLOBAL = ptr; } } pub struct VM { pub threads: Threads, } impl VM { pub fn new(thread_pool_count: usize) -> Box { let vm = Box::new(VM { threads: Threads::new(thread_pool_count), }); set_vm(&vm); vm } } ================================================ FILE: crates/vm/src/types.rs ================================================ use crate::oop::class::ClassPtr; use crate::oop::field::FieldId; use crate::runtime::method::MethodId; use crate::runtime::Frame; use crate::runtime::JavaThread; use classfile::ClassFile; use std::sync::Arc; pub type FieldIdRef = Arc; pub type MethodIdRef = Arc; pub type ClassRef = Arc; def_ref!(ClassFileRef, ClassFile); def_sync_ref!(FrameRef, Frame); def_sync_ref!(JavaThreadRef, JavaThread); // Runtime string allocation def_ptr!(ByteAry, Vec); def_ptr!(BoolAry, Vec); def_ptr!(CharAry, Vec); def_ptr!(ShortAry, Vec); def_ptr!(IntAry, Vec); def_ptr!(LongAry, Vec); def_ptr!(FloatAry, Vec); def_ptr!(DoubleAry, Vec); ================================================ FILE: crates/vm/src/util/attributes.rs ================================================ use classfile::{AttributeType, BytesRef}; pub fn assemble_annotation(attrs: &[AttributeType]) -> Option> { let mut vis = None; let mut in_vis = None; for it in attrs.iter() { match it { AttributeType::RuntimeVisibleAnnotations { raw, .. } => { vis = Some(raw.clone()); } AttributeType::RuntimeInvisibleAnnotations { raw, .. } => { in_vis = Some(raw.clone()); } _ => (), } } do_assemble(vis, in_vis) } pub fn assemble_param_annotation(attrs: &[AttributeType]) -> Option> { let mut vis = None; let mut in_vis = None; for it in attrs.iter() { match it { AttributeType::RuntimeVisibleParameterAnnotations { raw, .. } => { vis = Some(raw.clone()); } AttributeType::RuntimeInvisibleParameterAnnotations { raw, .. } => { in_vis = Some(raw.clone()); } _ => (), } } do_assemble(vis, in_vis) } pub fn assemble_type_annotation(attrs: &[AttributeType]) -> Option> { let mut vis = None; let mut in_vis = None; for it in attrs.iter() { match it { AttributeType::RuntimeVisibleTypeAnnotations { raw, .. } => { vis = Some(raw.clone()); } AttributeType::RuntimeInvisibleTypeAnnotations { raw, .. } => { in_vis = Some(raw.clone()); } _ => (), } } do_assemble(vis, in_vis) } pub fn assemble_annotation_default(attrs: &[AttributeType]) -> Option> { let mut vis = None; for it in attrs.iter() { if let AttributeType::AnnotationDefault { raw, .. } = it { vis = Some(raw.clone()); } } do_assemble(vis, None) } pub fn get_signature(attrs: &[AttributeType]) -> u16 { for it in attrs.iter() { if let AttributeType::Signature { signature_index } = it { return *signature_index; } } 0 } fn do_assemble(vis: Option, in_vis: Option) -> Option> { let mut raw = None; if let Some(v) = vis { raw = Some(Vec::from(v.as_slice())); } if let Some(v) = in_vis { if let Some(raw) = raw.as_mut() { raw.extend_from_slice(v.as_slice()); } } raw } ================================================ FILE: crates/vm/src/util/consts.rs ================================================ use crate::new_br; use classfile::BytesRef; lazy_static! { pub static ref S_INIT: BytesRef = new_br(""); pub static ref S_CLINIT: BytesRef = new_br(""); pub static ref S_CLINIT_SIG: BytesRef = new_br("()V"); pub static ref S_RUN_SIG: BytesRef = new_br("()Ljava/lang/Object;"); pub static ref S_NEW_STRING_SIG: BytesRef = new_br("([C)V"); pub static ref S_CLAZZ: BytesRef = new_br("clazz"); pub static ref S_FD: BytesRef = new_br("fd"); pub static ref S_I: BytesRef = new_br("I"); pub static ref S_SLOT: BytesRef = new_br("slot"); pub static ref S_MODIFIERS: BytesRef = new_br("modifiers"); pub static ref S_NAME: BytesRef = new_br("name"); pub static ref S_SIGNATURE: BytesRef = new_br("signature"); pub static ref S_CONSTANT_POOL_OOP: BytesRef = new_br("constantPoolOop"); pub static ref S_RUN: BytesRef = new_br("run"); pub static ref S_ERR: BytesRef = new_br("err"); pub static ref S_OUT: BytesRef = new_br("out"); pub static ref S_IN: BytesRef = new_br("in"); pub static ref S_JAVA_LANG_CLASS: BytesRef = new_br("Ljava/lang/Class;"); pub static ref S_JAVA_LANG_OBJECT: BytesRef = new_br("Ljava/lang/Object;"); pub static ref S_JAVA_LANG_STRING: BytesRef = new_br("Ljava/lang/String;"); pub static ref S_JAVA_IO_FD: BytesRef = new_br("Ljava/io/FileDescriptor;"); pub static ref S_JAVA_IO_PRINT_STREAM: BytesRef = new_br("Ljava/io/PrintStream;"); pub static ref S_JAVA_IO_INPUT_STREAM: BytesRef = new_br("Ljava/io/InputStream;"); } ================================================ FILE: crates/vm/src/util/debug.rs ================================================ #![allow(unused)] use crate::runtime::thread::JavaThread; use std::fmt::Write; use std::sync::atomic::Ordering; pub fn print_stack_trace(jt: &JavaThread) { let mut w = String::new(); let _ = writeln!(&mut w); for (count, it) in jt.frames.iter().enumerate().rev() { let frame = it.read().unwrap(); let cls = frame.mir.method.class.get_class(); let method_id = frame.mir.method.name.clone(); let line_num = { let pc = frame.pc.load(Ordering::Relaxed); frame.mir.method.get_line_num(pc as u16) }; let _ = writeln!( &mut w, "{}{}:{}(:{})", " ".repeat(jt.frames.len() - count), String::from_utf8_lossy(cls.name.as_slice()), String::from_utf8_lossy(method_id.as_slice()), line_num ); } error!("{}", w); } ================================================ FILE: crates/vm/src/util/macros.rs ================================================ #[macro_export] macro_rules! def_sync_ref { ($name:ident, $t:ty) => { pub type $name = std::sync::Arc>>; }; } /* macro_rules! def_already_boxed_ref { ($name:ident, $t:ty) => { pub type $name = std::sync::Arc<$t>; }; } */ #[macro_export] macro_rules! def_ref { ($name:ident, $t:ty) => { pub type $name = std::sync::Arc>; }; } #[macro_export] macro_rules! def_ptr { ($name:ident, $t:ty) => { pub type $name = Box<$t>; }; } #[macro_export] macro_rules! new_sync_ref { ($name:ident) => { std::sync::Arc::new(std::sync::RwLock::new(Box::new($name))); }; } #[macro_export] macro_rules! new_ref { ($name:ident) => { std::sync::Arc::new(Box::new($name)); }; } ================================================ FILE: crates/vm/src/util/mod.rs ================================================ #[macro_use] pub mod macros; pub mod attributes; mod consts; pub mod debug; pub mod oop; mod sys; pub use self::consts::*; pub use self::sys::*; use classfile::BytesRef; use std::sync::Arc; pub fn new_field_id(cls: &[u8], name: &[u8], desc: &[u8]) -> BytesRef { Arc::new(vec![cls, name, desc].join(PATH_SEP.as_bytes())) } ================================================ FILE: crates/vm/src/util/oop.rs ================================================ use crate::oop::Oop; use crate::runtime::{self, require_class3}; use crate::util; static mut JAVA_LANG_STRING_VALUE_OFFSET: usize = 0; static mut JAVA_LANG_INTEGER_VALUE_OFFSET: usize = 0; pub fn set_java_lang_string_value_offset(offset: usize) { unsafe { JAVA_LANG_STRING_VALUE_OFFSET = offset; } } pub fn set_java_lang_integer_value_offset(offset: usize) { unsafe { JAVA_LANG_INTEGER_VALUE_OFFSET = offset; } } pub fn get_java_lang_string_value_offset() -> usize { unsafe { JAVA_LANG_STRING_VALUE_OFFSET } } pub fn get_java_lang_integer_value_offset() -> usize { unsafe { JAVA_LANG_INTEGER_VALUE_OFFSET } } pub fn new_java_lang_string2(v: &str) -> Oop { //build "char value[]" let chars: Vec = v.as_bytes().iter().map(|v| *v as u16).collect(); let ary = Oop::char_ary_from1(chars.as_slice()); //new String(char value[]) let string_cls = require_class3(None, b"java/lang/String").unwrap(); let string_oop = Oop::new_inst(string_cls.clone()); let args = vec![string_oop.clone(), ary]; runtime::invoke::invoke_ctor(string_cls, util::S_NEW_STRING_SIG.clone(), args); string_oop } pub fn new_java_lang_string3(bs: &[u8]) -> Oop { let buffer = classfile::constant_pool::construct_string_raw(bs); //build "char value[]" let ary = Oop::char_ary_from1(buffer.as_slice()); //new String(char value[]) let string_cls = require_class3(None, b"java/lang/String").unwrap(); let string_oop = Oop::new_inst(string_cls.clone()); let args = vec![string_oop.clone(), ary]; runtime::invoke::invoke_ctor(string_cls, util::S_NEW_STRING_SIG.clone(), args); string_oop } ================================================ FILE: crates/vm/src/util/sys.rs ================================================ #![allow(unused)] pub const FILE_SEP: &str = platform::FILE_SEP; pub const PATH_SEP: &str = platform::PATH_SEP; pub const LINE_SEP: &str = "\n"; #[cfg(unix)] mod platform { pub const FILE_SEP: &str = "/"; pub const PATH_SEP: &str = ":"; } #[cfg(windows)] mod platform { pub const FILE_SEP: &str = "\\"; pub const PATH_SEP: &str = ";"; } ================================================ FILE: jvm/Cargo.toml ================================================ [package] name = "jvm" version = "0.2.0" authors = ["douchuan <1843657913@qq.com>"] edition = "2018" [dependencies] clap = "3.0.0-beta.1" class-parser = { path = "../crates/class-parser", version = "0.1.0" } env_logger = "0.8.2" vm = { path = "../crates/vm", version = "0.1.0" } [dev-dependencies] classfile = { path = "../crates/classfile", version = "0.1.0" } libc = "0.2.71" ================================================ FILE: jvm/README.md ================================================ ## jvm The executable program of jvm. ## Usage Modify the run.sh script according to your environment. If you installed JDK, but can not find jre path, try: ```shell java -XshowSettings:properties ``` ================================================ FILE: jvm/r.sh ================================================ ######################################### #modify to according to your env # #If you installed JDK, but can not find jre path, try: #java -XshowSettings:properties # #On Linux, maybe #JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre" ######################################### JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre ######################################## MY_SAMPLE=sample ############################################## function join_by { local IFS="$1"; shift; echo "$*"; } ############################################## #FIXME: win should be ';' SEP=':' jars=$JAVA_HOME/lib/*.jar JDK=$(join_by $SEP ${jars[@]}) export JAVA_HOME cargo run -- --cp $JDK:$MY_SAMPLE HelloWorld ================================================ FILE: jvm/sample/HelloWorld.java ================================================ class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World."); } } ================================================ FILE: jvm/src/main.rs ================================================ extern crate clap; extern crate env_logger; mod options; use vm; use vm::runtime::{self, thread::MainThread}; use vm::util; fn main() { env_logger::init(); vm::init_vm(); let opt = options::parse(); if let Some(cp) = &opt.cp { runtime::add_class_paths(cp); } if let Some(classpath) = &opt.classpath { runtime::add_class_path(classpath); } let class = opt.class; let args = opt.args; // println!("main class: {}, args: {:?}", class, args); let mut thread = MainThread::new(class.replace(".", util::FILE_SEP), args); thread.run(); } ================================================ FILE: jvm/src/options.rs ================================================ use clap::Clap; #[derive(Clap, Debug)] #[clap(version)] pub struct Opt { /// class search path of directories and zip/jar files #[clap(long)] pub cp: Option, /// class search path of directories and zip/jar files #[clap(long)] pub classpath: Option, #[clap(required = true)] pub class: String, pub args: Vec, } pub fn parse() -> Opt { Opt::parse() } ================================================ FILE: jvm/t.sh ================================================ ######################################### ###modify to according to your env JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre JDK_SRC=/Users/douchuan/work/codes/vm/openjdk8 ######################################## JDK=$JAVA_HOME/lib/resources.jar:$JAVA_HOME/lib/rt.jar:$JAVA_HOME/lib/jsse.jar:$JAVA_HOME/lib/jce.jar:$JAVA_HOME/lib/charsets.jar:$JAVA_HOME/lib/jfr.jar JDK_T_LANG=$JDK_SRC/jdk/test/java/lang JDK_T_SM=$JDK_SRC/jdk/test/sun/misc MY_TEST=.:./test export JAVA_HOME #export RUST_LOG=warn #export RUST_LOG=info #export RUST_LOG=trace export RUST_BACKTRACE=full ### My Test #cargo run -- --cp $JDK:$MY_TEST Add #cargo run -- --cp $JDK:$MY_TEST MyHelloWorld 123 456 789 #cargo run -- --cp $JDK:$MY_TEST HelloWorldUnicode #cargo run -- --cp $JDK:$MY_TEST Ex #cargo run -- --cp $JDK:$MY_TEST MyFile #cargo run -- --cp $JDK:$MY_TEST MyInteger #cargo run -- --cp $JDK:$MY_TEST MyArrayCopy #cargo run -- --cp $JDK:$MY_TEST ThreadTest #cargo run -- --cp $JDK:$MY_TEST ThreadTest2 ### no 'join' in main thread #cargo run -- --cp $JDK:$MY_TEST ThreadTest3 ### fix Overflow #cargo run -- --cp $JDK:$MY_TEST SubOverflow ### fix Enum CloneNotSupportedException #cargo run -- --cp $JDK:$MY_TEST EnumDemo ### fix System.out.printf not work, resolve_again for acc_flags == 0 #cargo run -- --cp $JDK:$MY_TEST Printf ### fix ThreadLocal not work, resolve_again for protected #cargo run -- --cp $JDK:$MY_TEST ThreadLocalTest ### load with custom package ## should panic #cargo run -- --cp $JDK:$MY_TEST test/with_package/my.ns.HelloWorld ## ok #cargo run -- --cp $JDK:$MY_TEST:test/with_package my.ns.HelloWorld ###regex ## System.out.printf not work ## ## 原因1: invoke_virtual定位method错误 ## 因为java.util.regex.Pattern$Node.match方法的acc_flags==0, ##导致没有resolve_again,acc_flags为0的含义是什么? ## java.util.regex.Pattern$Node.match (错误的找到这个method) ## java.util.regex.Pattern$Start.match (应该用这个) ## ## 原因2: ## java.util.Formatter$FormatSpecifier.conversion(String) ## 调用Character.toLowerCase(c)转换错误 ## 码表在java/lang/CharacterDataLatin1中, ldc CONSTANT_String_info 加载 ## ## Modified UTF-8 strings 编码定义: ## JVM Spec, 4.4.7 The CONSTANT_Utf8_info Structure 定义 #cargo run -- --cp $JDK:$MY_TEST:./test/regex Printf ### ##Float.toString(1.0f) crash ##ThreadLocal.initialValue not called, so NPE happend #cargo run -- --cp $JDK:$MY_TEST:./test/float ToString #cargo run -- --cp $JDK:$MY_TEST:./test/char MyCheckScript #cargo run -- --cp $JDK:$MY_TEST:./test/annotation AnnotationTest ############################### ### jdk test ############################### #cargo run -- --cp $JDK:$JDK_T_LANG Compare #cargo run -- --cp $JDK:$JDK_T_LANG HashCode #cargo run -- --cp $JDK:$JDK_T_LANG ToString #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Appendable Basic #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/AssertionError Cause #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Boolean Factory #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Boolean GetBoolean #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Boolean MakeBooleanComparable #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Boolean ParseBoolean #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Byte Decode #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class ArrayMethods #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class GenericStringTest #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class IsEnum ##TODO: GC needed #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class TypeCheckMicroBenchmark #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class Cast #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class IsAnnotationType #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class IsSynthetic #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/asSubclass BasicUnit #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/forName InitArg #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/forName InvalidNameWithSlash #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/forName NonJavaNames #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/forName Z #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/getClasses Sanity #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Math AbsPositiveZero ##jdk/test/sun/misc #TODO: CopyMemory getUnsafe return null #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_SM MyCopyMemory #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_SM Safe ##TODO: depend on testng ##IntegralPrimitiveToString.java, PrimitiveSumMinMaxTest.java ##CharSequence/DefaultTest.java ##TODO: optimize ################################ ### oracle java #sum_t_list_add = 16 #sum_t_map_get = 1 #sum_t_map_put = 1 #sum_t_parse_int = 3 #sum_t_println = 26 #sum_t_int2integer = 13 ################################# ### mine release mode #sum_t_list_add = 1507 #sum_t_map_get = 27 #sum_t_map_put = 3 #sum_t_parse_int = 159 #sum_t_println = 768 #sum_t_int2integer = 1255 export TEST_SRC=$JDK_SRC/jdk/test/java/lang/Character cargo run --release -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Character MyCheckProp #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Character MyCheckProp #cargo run --release -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Character MyCheckProp1 #cargo run --release -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Character CheckProp #cargo run --release -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Character CheckScript #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/reflect/Constructor TestParameterAnnotations ##TODO: NonJavaNames just ignored currently ##NonJavaNames ##TODO: impl getDeclaredClasses0 #cargo run -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Class/getClasses Sanity ############################################ ###perf (linux) ############################################ #export TEST_SRC=$JDK_SRC/jdk/test/java/lang/Character #perf record --call-graph dwarf -- cargo run --release -- --cp $JDK:$JDK_T_LANG:$JDK_T_LANG/Character MyCheckProp #perf report ================================================ FILE: libjvm/Cargo.toml ================================================ [package] name = "libjvm" version = "0.1.0" authors = ["Лач "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] jni-sys = "0.3.0" libc = "0.2.68" lazy_static = "1.4.0" vm = { path = "../crates/vm", version = "0.1.0" } [lib] name = "jvm" crate-type = ["cdylib"] [profile.dev] panic = "abort" [profile.release] panic = "abort" ================================================ FILE: libjvm/README.md ================================================ ## jvm Drop-in implementation of libjvm.so/jvm.dll ## Usage Compile and copy to `openjdk root/lib/amd64/server/libjvm.so` ================================================ FILE: libjvm/src/invocation.rs ================================================ #![allow(non_snake_case)] #![allow(unused_imports)] //https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html use jni_sys::{jboolean, jint, jsize, JNIInvokeInterface_, JNINativeInterface_, JavaVM}; use lazy_static::lazy_static; use libc::c_void; use std::cell::RefCell; use std::sync::Mutex; use vm::runtime::thread::MainThread; use crate::native; #[no_mangle] extern "C" fn JNI_GetDefaultJavaVMInitArgs(args: *mut c_void) -> jint { 0 } #[derive(Clone)] struct VMHolder { jvm: Box, } unsafe impl Send for VMHolder {} unsafe impl Sync for VMHolder {} impl VMHolder { fn inner(&self) -> JavaVM { self.jvm.as_ref() as *const _ } } // TODO: Hotspot doesn't supports creation of multiple JVMs per process, should we? lazy_static! { static ref JVM: Mutex> = Mutex::new(None); } unsafe extern "system" fn DestroyJavaVM(_vm: *mut JavaVM) -> jint { todo!(); } unsafe extern "system" fn AttachCurrentThread( _vm: *mut JavaVM, _penv: *mut *mut c_void, _args: *mut c_void, ) -> jint { todo!(); } unsafe extern "system" fn DetachCurrentThread(_vm: *mut JavaVM) -> jint { todo!(); } unsafe extern "system" fn GetEnv( _vm: *mut JavaVM, penv: *mut *mut core::ffi::c_void, _version: jint, ) -> jint { use std::ptr::null_mut; *penv = Box::into_raw(Box::new(JNINativeInterface_ { reserved0: null_mut(), reserved1: null_mut(), reserved2: null_mut(), reserved3: null_mut(), GetVersion: Some(native::GetVersion), DefineClass: Some(native::DefineClass), FindClass: Some(native::FindClass), FromReflectedMethod: Some(native::FromReflectedMethod), FromReflectedField: Some(native::FromReflectedField), ToReflectedMethod: Some(native::ToReflectedMethod), GetSuperclass: Some(native::GetSuperclass), IsAssignableFrom: Some(native::IsAssignableFrom), ToReflectedField: Some(native::ToReflectedField), Throw: Some(native::Throw), ThrowNew: Some(native::ThrowNew), ExceptionOccurred: Some(native::ExceptionOccurred), ExceptionDescribe: Some(native::ExceptionDescribe), ExceptionClear: Some(native::ExceptionClear), FatalError: Some(native::FatalError), PushLocalFrame: Some(native::PushLocalFrame), PopLocalFrame: Some(native::PopLocalFrame), NewGlobalRef: Some(native::NewGlobalRef), DeleteGlobalRef: Some(native::DeleteGlobalRef), DeleteLocalRef: Some(native::DeleteLocalRef), IsSameObject: Some(native::IsSameObject), NewLocalRef: Some(native::NewLocalRef), EnsureLocalCapacity: Some(native::EnsureLocalCapacity), AllocObject: Some(native::AllocObject), NewObject: Some(native::NewObject), NewObjectV: Some(native::NewObjectV), NewObjectA: Some(native::NewObjectA), GetObjectClass: Some(native::GetObjectClass), IsInstanceOf: Some(native::IsInstanceOf), GetMethodID: Some(native::GetMethodID), CallObjectMethod: Some(native::CallObjectMethod), CallObjectMethodV: Some(native::CallObjectMethodV), CallObjectMethodA: Some(native::CallObjectMethodA), CallBooleanMethod: Some(native::CallBooleanMethod), CallBooleanMethodV: Some(native::CallBooleanMethodV), CallBooleanMethodA: Some(native::CallBooleanMethodA), CallByteMethod: Some(native::CallByteMethod), CallByteMethodV: Some(native::CallByteMethodV), CallByteMethodA: Some(native::CallByteMethodA), CallCharMethod: Some(native::CallCharMethod), CallCharMethodV: Some(native::CallCharMethodV), CallCharMethodA: Some(native::CallCharMethodA), CallShortMethod: Some(native::CallShortMethod), CallShortMethodV: Some(native::CallShortMethodV), CallShortMethodA: Some(native::CallShortMethodA), CallIntMethod: Some(native::CallIntMethod), CallIntMethodV: Some(native::CallIntMethodV), CallIntMethodA: Some(native::CallIntMethodA), CallLongMethod: Some(native::CallLongMethod), CallLongMethodV: Some(native::CallLongMethodV), CallLongMethodA: Some(native::CallLongMethodA), CallFloatMethod: Some(native::CallFloatMethod), CallFloatMethodV: Some(native::CallFloatMethodV), CallFloatMethodA: Some(native::CallFloatMethodA), CallDoubleMethod: Some(native::CallDoubleMethod), CallDoubleMethodV: Some(native::CallDoubleMethodV), CallDoubleMethodA: Some(native::CallDoubleMethodA), CallVoidMethod: Some(native::CallVoidMethod), CallVoidMethodV: Some(native::CallVoidMethodV), CallVoidMethodA: Some(native::CallVoidMethodA), CallNonvirtualObjectMethod: Some(native::CallNonvirtualObjectMethod), CallNonvirtualObjectMethodV: Some(native::CallNonvirtualObjectMethodV), CallNonvirtualObjectMethodA: Some(native::CallNonvirtualObjectMethodA), CallNonvirtualBooleanMethod: Some(native::CallNonvirtualBooleanMethod), CallNonvirtualBooleanMethodV: Some(native::CallNonvirtualBooleanMethodV), CallNonvirtualBooleanMethodA: Some(native::CallNonvirtualBooleanMethodA), CallNonvirtualByteMethod: Some(native::CallNonvirtualByteMethod), CallNonvirtualByteMethodV: Some(native::CallNonvirtualByteMethodV), CallNonvirtualByteMethodA: Some(native::CallNonvirtualByteMethodA), CallNonvirtualCharMethod: Some(native::CallNonvirtualCharMethod), CallNonvirtualCharMethodV: Some(native::CallNonvirtualCharMethodV), CallNonvirtualCharMethodA: Some(native::CallNonvirtualCharMethodA), CallNonvirtualShortMethod: Some(native::CallNonvirtualShortMethod), CallNonvirtualShortMethodV: Some(native::CallNonvirtualShortMethodV), CallNonvirtualShortMethodA: Some(native::CallNonvirtualShortMethodA), CallNonvirtualIntMethod: Some(native::CallNonvirtualIntMethod), CallNonvirtualIntMethodV: Some(native::CallNonvirtualIntMethodV), CallNonvirtualIntMethodA: Some(native::CallNonvirtualIntMethodA), CallNonvirtualLongMethod: Some(native::CallNonvirtualLongMethod), CallNonvirtualLongMethodV: Some(native::CallNonvirtualLongMethodV), CallNonvirtualLongMethodA: Some(native::CallNonvirtualLongMethodA), CallNonvirtualFloatMethod: Some(native::CallNonvirtualFloatMethod), CallNonvirtualFloatMethodV: Some(native::CallNonvirtualFloatMethodV), CallNonvirtualFloatMethodA: Some(native::CallNonvirtualFloatMethodA), CallNonvirtualDoubleMethod: Some(native::CallNonvirtualDoubleMethod), CallNonvirtualDoubleMethodV: Some(native::CallNonvirtualDoubleMethodV), CallNonvirtualDoubleMethodA: Some(native::CallNonvirtualDoubleMethodA), CallNonvirtualVoidMethod: Some(native::CallNonvirtualVoidMethod), CallNonvirtualVoidMethodV: Some(native::CallNonvirtualVoidMethodV), CallNonvirtualVoidMethodA: Some(native::CallNonvirtualVoidMethodA), GetFieldID: Some(native::GetFieldID), GetObjectField: Some(native::GetObjectField), GetBooleanField: Some(native::GetBooleanField), GetByteField: Some(native::GetByteField), GetCharField: Some(native::GetCharField), GetShortField: Some(native::GetShortField), GetIntField: Some(native::GetIntField), GetLongField: Some(native::GetLongField), GetFloatField: Some(native::GetFloatField), GetDoubleField: Some(native::GetDoubleField), SetObjectField: Some(native::SetObjectField), SetBooleanField: Some(native::SetBooleanField), SetByteField: Some(native::SetByteField), SetCharField: Some(native::SetCharField), SetShortField: Some(native::SetShortField), SetIntField: Some(native::SetIntField), SetLongField: Some(native::SetLongField), SetFloatField: Some(native::SetFloatField), SetDoubleField: Some(native::SetDoubleField), GetStaticMethodID: Some(native::GetStaticMethodID), CallStaticObjectMethod: Some(native::CallStaticObjectMethod), CallStaticObjectMethodV: Some(native::CallStaticObjectMethodV), CallStaticObjectMethodA: Some(native::CallStaticObjectMethodA), CallStaticBooleanMethod: Some(native::CallStaticBooleanMethod), CallStaticBooleanMethodV: Some(native::CallStaticBooleanMethodV), CallStaticBooleanMethodA: Some(native::CallStaticBooleanMethodA), CallStaticByteMethod: Some(native::CallStaticByteMethod), CallStaticByteMethodV: Some(native::CallStaticByteMethodV), CallStaticByteMethodA: Some(native::CallStaticByteMethodA), CallStaticCharMethod: Some(native::CallStaticCharMethod), CallStaticCharMethodV: Some(native::CallStaticCharMethodV), CallStaticCharMethodA: Some(native::CallStaticCharMethodA), CallStaticShortMethod: Some(native::CallStaticShortMethod), CallStaticShortMethodV: Some(native::CallStaticShortMethodV), CallStaticShortMethodA: Some(native::CallStaticShortMethodA), CallStaticIntMethod: Some(native::CallStaticIntMethod), CallStaticIntMethodV: Some(native::CallStaticIntMethodV), CallStaticIntMethodA: Some(native::CallStaticIntMethodA), CallStaticLongMethod: Some(native::CallStaticLongMethod), CallStaticLongMethodV: Some(native::CallStaticLongMethodV), CallStaticLongMethodA: Some(native::CallStaticLongMethodA), CallStaticFloatMethod: Some(native::CallStaticFloatMethod), CallStaticFloatMethodV: Some(native::CallStaticFloatMethodV), CallStaticFloatMethodA: Some(native::CallStaticFloatMethodA), CallStaticDoubleMethod: Some(native::CallStaticDoubleMethod), CallStaticDoubleMethodV: Some(native::CallStaticDoubleMethodV), CallStaticDoubleMethodA: Some(native::CallStaticDoubleMethodA), CallStaticVoidMethod: Some(native::CallStaticVoidMethod), CallStaticVoidMethodV: Some(native::CallStaticVoidMethodV), CallStaticVoidMethodA: Some(native::CallStaticVoidMethodA), GetStaticFieldID: Some(native::GetStaticFieldID), GetStaticObjectField: Some(native::GetStaticObjectField), GetStaticBooleanField: Some(native::GetStaticBooleanField), GetStaticByteField: Some(native::GetStaticByteField), GetStaticCharField: Some(native::GetStaticCharField), GetStaticShortField: Some(native::GetStaticShortField), GetStaticIntField: Some(native::GetStaticIntField), GetStaticLongField: Some(native::GetStaticLongField), GetStaticFloatField: Some(native::GetStaticFloatField), GetStaticDoubleField: Some(native::GetStaticDoubleField), SetStaticObjectField: Some(native::SetStaticObjectField), SetStaticBooleanField: Some(native::SetStaticBooleanField), SetStaticByteField: Some(native::SetStaticByteField), SetStaticCharField: Some(native::SetStaticCharField), SetStaticShortField: Some(native::SetStaticShortField), SetStaticIntField: Some(native::SetStaticIntField), SetStaticLongField: Some(native::SetStaticLongField), SetStaticFloatField: Some(native::SetStaticFloatField), SetStaticDoubleField: Some(native::SetStaticDoubleField), NewString: Some(native::NewString), GetStringLength: Some(native::GetStringLength), GetStringChars: Some(native::GetStringChars), ReleaseStringChars: Some(native::ReleaseStringChars), NewStringUTF: Some(native::NewStringUTF), GetStringUTFLength: Some(native::GetStringUTFLength), GetStringUTFChars: Some(native::GetStringUTFChars), ReleaseStringUTFChars: Some(native::ReleaseStringUTFChars), GetArrayLength: Some(native::GetArrayLength), NewObjectArray: Some(native::NewObjectArray), GetObjectArrayElement: Some(native::GetObjectArrayElement), SetObjectArrayElement: Some(native::SetObjectArrayElement), NewBooleanArray: Some(native::NewBooleanArray), NewByteArray: Some(native::NewByteArray), NewCharArray: Some(native::NewCharArray), NewShortArray: Some(native::NewShortArray), NewIntArray: Some(native::NewIntArray), NewLongArray: Some(native::NewLongArray), NewFloatArray: Some(native::NewFloatArray), NewDoubleArray: Some(native::NewDoubleArray), GetBooleanArrayElements: Some(native::GetBooleanArrayElements), GetByteArrayElements: Some(native::GetByteArrayElements), GetCharArrayElements: Some(native::GetCharArrayElements), GetShortArrayElements: Some(native::GetShortArrayElements), GetIntArrayElements: Some(native::GetIntArrayElements), GetLongArrayElements: Some(native::GetLongArrayElements), GetFloatArrayElements: Some(native::GetFloatArrayElements), GetDoubleArrayElements: Some(native::GetDoubleArrayElements), ReleaseBooleanArrayElements: Some(native::ReleaseBooleanArrayElements), ReleaseByteArrayElements: Some(native::ReleaseByteArrayElements), ReleaseCharArrayElements: Some(native::ReleaseCharArrayElements), ReleaseShortArrayElements: Some(native::ReleaseShortArrayElements), ReleaseIntArrayElements: Some(native::ReleaseIntArrayElements), ReleaseLongArrayElements: Some(native::ReleaseLongArrayElements), ReleaseFloatArrayElements: Some(native::ReleaseFloatArrayElements), ReleaseDoubleArrayElements: Some(native::ReleaseDoubleArrayElements), GetBooleanArrayRegion: Some(native::GetBooleanArrayRegion), GetByteArrayRegion: Some(native::GetByteArrayRegion), GetCharArrayRegion: Some(native::GetCharArrayRegion), GetShortArrayRegion: Some(native::GetShortArrayRegion), GetIntArrayRegion: Some(native::GetIntArrayRegion), GetLongArrayRegion: Some(native::GetLongArrayRegion), GetFloatArrayRegion: Some(native::GetFloatArrayRegion), GetDoubleArrayRegion: Some(native::GetDoubleArrayRegion), SetBooleanArrayRegion: Some(native::SetBooleanArrayRegion), SetByteArrayRegion: Some(native::SetByteArrayRegion), SetCharArrayRegion: Some(native::SetCharArrayRegion), SetShortArrayRegion: Some(native::SetShortArrayRegion), SetIntArrayRegion: Some(native::SetIntArrayRegion), SetLongArrayRegion: Some(native::SetLongArrayRegion), SetFloatArrayRegion: Some(native::SetFloatArrayRegion), SetDoubleArrayRegion: Some(native::SetDoubleArrayRegion), RegisterNatives: Some(native::RegisterNatives), UnregisterNatives: Some(native::UnregisterNatives), MonitorEnter: Some(native::MonitorEnter), MonitorExit: Some(native::MonitorExit), GetJavaVM: Some(native::GetJavaVM), GetStringRegion: Some(native::GetStringRegion), GetStringUTFRegion: Some(native::GetStringUTFRegion), GetPrimitiveArrayCritical: Some(native::GetPrimitiveArrayCritical), ReleasePrimitiveArrayCritical: Some(native::ReleasePrimitiveArrayCritical), GetStringCritical: Some(native::GetStringCritical), ReleaseStringCritical: Some(native::ReleaseStringCritical), NewWeakGlobalRef: Some(native::NewWeakGlobalRef), DeleteWeakGlobalRef: Some(native::DeleteWeakGlobalRef), ExceptionCheck: Some(native::ExceptionCheck), NewDirectByteBuffer: Some(native::NewDirectByteBuffer), GetDirectBufferAddress: Some(native::GetDirectBufferAddress), GetDirectBufferCapacity: Some(native::GetDirectBufferCapacity), GetObjectRefType: Some(native::GetObjectRefType), })) as *mut core::ffi::c_void; 0 } unsafe extern "system" fn AttachCurrentThreadAsDaemon( _vm: *mut JavaVM, _penv: *mut *mut c_void, _args: *mut c_void, ) -> jint { todo!(); } #[repr(C)] struct JavaVMOption { option_string: *const libc::c_void, extra_info: *const libc::c_void, } impl JavaVMOption { fn string(&self) -> &std::ffi::CStr { unsafe { std::ffi::CStr::from_ptr(self.option_string as *const _) } } } impl std::fmt::Debug for JavaVMOption { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.debug_struct("JavaVMOption") .field("option_string", &self.string()) .finish() } } #[repr(C)] struct JavaVMInitArgs { version: jint, n_options: jint, options: *const JavaVMOption, ignore_unrecognized: jboolean, } impl JavaVMInitArgs { fn options(&self) -> &[JavaVMOption] { unsafe { std::slice::from_raw_parts(self.options, self.n_options as usize) } } } impl std::fmt::Debug for JavaVMInitArgs { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.debug_struct("JavaVMInitArgs") .field("version", &self.version) .field("ignore_unrecognized", &self.ignore_unrecognized) .field("options", &self.options()) .finish() } } #[no_mangle] extern "C" fn JNI_CreateJavaVM( pvm: *mut *mut JavaVM, penv: *mut *mut c_void, args: *const JavaVMInitArgs, ) -> jint { let mut lock = JVM.lock().expect("jvm lock"); // Can't have multiple VMs per process if lock.is_some() { -1 } else { use std::ptr::null_mut; vm::native::init(); vm::oop::init(); vm::runtime::init(); // To be run from jre dir vm::runtime::add_class_path("./lib/rt.jar"); vm::runtime::add_class_path("./lib/jsse.jar"); let args = unsafe { &*args }; // TODO: Pass to jvm let mut properties: std::collections::HashMap = std::collections::HashMap::new(); for option in args.options() { let option: String = option.string().to_string_lossy().into(); if option.starts_with("-D") { let idx = option.find("=").expect("bad property argument format"); properties.insert(option[2..idx].to_owned(), option[idx..].to_owned()); } else if args.ignore_unrecognized == 0 { panic!("unknown option: {}", option); } } let holder = VMHolder { jvm: Box::new(JNIInvokeInterface_ { // We can use reserved fields for implementation details reserved0: null_mut(), reserved1: null_mut(), reserved2: null_mut(), DestroyJavaVM: Some(DestroyJavaVM), AttachCurrentThread: Some(AttachCurrentThread), DetachCurrentThread: Some(DetachCurrentThread), GetEnv: Some(GetEnv), AttachCurrentThreadAsDaemon: Some(AttachCurrentThreadAsDaemon), }), }; unsafe { *pvm = Box::into_raw(Box::new(holder.inner())); holder.jvm.GetEnv.unwrap()(&mut holder.inner(), penv, 0); } lock.replace(holder); 0 } } #[no_mangle] extern "C" fn JNI_GetCreatedJavaVMs( vm_buf: *mut *mut JavaVM, buf_len: jsize, n_vms: *mut jsize, ) -> jint { if buf_len >= 1 { let lock = JVM.lock().expect("jvm lock"); if let Some(ref holder) = lock.as_ref() { unsafe { *vm_buf = Box::into_raw(Box::new(holder.jvm.as_ref())); *n_vms = 1; } } else { unsafe { *n_vms = 0; } } 1 } else { unsafe { *n_vms = 0; }; 0 } } ================================================ FILE: libjvm/src/lib.rs ================================================ #![feature(c_variadic)] pub mod invocation; pub mod native; pub mod private; pub mod util; ================================================ FILE: libjvm/src/native.rs ================================================ #![allow(non_snake_case)] #![allow(unused_variables)] #![allow(unused_imports)] use jni_sys::{ jarray, jboolean, jbooleanArray, jbyte, jbyteArray, jchar, jcharArray, jclass, jdouble, jdoubleArray, jfieldID, jfloat, jfloatArray, jint, jintArray, jlong, jlongArray, jmethodID, jobject, jobjectArray, jobjectRefType, jshort, jshortArray, jsize, jstring, jthrowable, jvalue, jweak, JNIEnv, JNIInvokeInterface_, JNINativeInterface_, JNINativeMethod, JavaVM, }; use libc::{c_char, c_void}; pub type va_list = *mut c_void; pub unsafe extern "system" fn GetVersion(env: *mut JNIEnv) -> jint { todo!(); } pub unsafe extern "system" fn DefineClass( env: *mut JNIEnv, name: *const c_char, loader: jobject, buf: *const jbyte, len: jsize, ) -> jclass { todo!(); } pub unsafe extern "system" fn FindClass(env: *mut JNIEnv, name: *const c_char) -> jclass { todo!(); } pub unsafe extern "system" fn FromReflectedMethod(env: *mut JNIEnv, method: jobject) -> jmethodID { todo!(); } pub unsafe extern "system" fn FromReflectedField(env: *mut JNIEnv, field: jobject) -> jfieldID { todo!(); } pub unsafe extern "system" fn ToReflectedMethod( env: *mut JNIEnv, cls: jclass, methodID: jmethodID, isStatic: jboolean, ) -> jobject { todo!(); } pub unsafe extern "system" fn GetSuperclass(env: *mut JNIEnv, sub: jclass) -> jclass { todo!(); } pub unsafe extern "system" fn IsAssignableFrom( env: *mut JNIEnv, sub: jclass, sup: jclass, ) -> jboolean { todo!(); } pub unsafe extern "system" fn ToReflectedField( env: *mut JNIEnv, cls: jclass, fieldID: jfieldID, isStatic: jboolean, ) -> jobject { todo!(); } pub unsafe extern "system" fn Throw(env: *mut JNIEnv, obj: jthrowable) -> jint { todo!(); } pub unsafe extern "system" fn ThrowNew( env: *mut JNIEnv, clazz: jclass, msg: *const c_char, ) -> jint { todo!(); } pub unsafe extern "system" fn ExceptionOccurred(env: *mut JNIEnv) -> jthrowable { todo!(); } pub unsafe extern "system" fn ExceptionDescribe(env: *mut JNIEnv) { todo!(); } pub unsafe extern "system" fn ExceptionClear(env: *mut JNIEnv) { todo!(); } pub unsafe extern "system" fn FatalError(env: *mut JNIEnv, msg: *const c_char) -> ! { todo!(); } pub unsafe extern "system" fn PushLocalFrame(env: *mut JNIEnv, capacity: jint) -> jint { todo!(); } pub unsafe extern "system" fn PopLocalFrame(env: *mut JNIEnv, result: jobject) -> jobject { todo!(); } pub unsafe extern "system" fn NewGlobalRef(env: *mut JNIEnv, lobj: jobject) -> jobject { todo!(); } pub unsafe extern "system" fn DeleteGlobalRef(env: *mut JNIEnv, gref: jobject) { todo!(); } pub unsafe extern "system" fn DeleteLocalRef(env: *mut JNIEnv, obj: jobject) { todo!(); } pub unsafe extern "system" fn IsSameObject( env: *mut JNIEnv, obj1: jobject, obj2: jobject, ) -> jboolean { todo!(); } pub unsafe extern "system" fn NewLocalRef(env: *mut JNIEnv, ref_: jobject) -> jobject { todo!(); } pub unsafe extern "system" fn EnsureLocalCapacity(env: *mut JNIEnv, capacity: jint) -> jint { todo!(); } pub unsafe extern "system" fn AllocObject(env: *mut JNIEnv, clazz: jclass) -> jobject { todo!(); } pub unsafe extern "C" fn NewObject( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jobject { todo!(); } pub unsafe extern "system" fn NewObjectV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jobject { todo!(); } pub unsafe extern "system" fn NewObjectA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jobject { todo!(); } pub unsafe extern "system" fn GetObjectClass(env: *mut JNIEnv, obj: jobject) -> jclass { todo!(); } pub unsafe extern "system" fn IsInstanceOf( env: *mut JNIEnv, obj: jobject, clazz: jclass, ) -> jboolean { todo!(); } pub unsafe extern "system" fn GetMethodID( env: *mut JNIEnv, clazz: jclass, name: *const c_char, sig: *const c_char, ) -> jmethodID { todo!(); } pub unsafe extern "C" fn CallObjectMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jobject { todo!(); } pub unsafe extern "system" fn CallObjectMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jobject { todo!(); } pub unsafe extern "system" fn CallObjectMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jobject { todo!(); } pub unsafe extern "C" fn CallBooleanMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jboolean { todo!(); } pub unsafe extern "system" fn CallBooleanMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jboolean { todo!(); } pub unsafe extern "system" fn CallBooleanMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jboolean { todo!(); } pub unsafe extern "C" fn CallByteMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jbyte { todo!(); } pub unsafe extern "system" fn CallByteMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jbyte { todo!(); } pub unsafe extern "system" fn CallByteMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jbyte { todo!(); } pub unsafe extern "C" fn CallCharMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jchar { todo!(); } pub unsafe extern "system" fn CallCharMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jchar { todo!(); } pub unsafe extern "system" fn CallCharMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jchar { todo!(); } pub unsafe extern "C" fn CallShortMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jshort { todo!(); } pub unsafe extern "system" fn CallShortMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jshort { todo!(); } pub unsafe extern "system" fn CallShortMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jshort { todo!(); } pub unsafe extern "C" fn CallIntMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jint { todo!(); } pub unsafe extern "system" fn CallIntMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jint { todo!(); } pub unsafe extern "system" fn CallIntMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jint { todo!(); } pub unsafe extern "C" fn CallLongMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jlong { todo!(); } pub unsafe extern "system" fn CallLongMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jlong { todo!(); } pub unsafe extern "system" fn CallLongMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jlong { todo!(); } pub unsafe extern "C" fn CallFloatMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jfloat { todo!(); } pub unsafe extern "system" fn CallFloatMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jfloat { todo!(); } pub unsafe extern "system" fn CallFloatMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jfloat { todo!(); } pub unsafe extern "C" fn CallDoubleMethod( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ... ) -> jdouble { todo!(); } pub unsafe extern "system" fn CallDoubleMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) -> jdouble { todo!(); } pub unsafe extern "system" fn CallDoubleMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) -> jdouble { todo!(); } pub unsafe extern "C" fn CallVoidMethod(env: *mut JNIEnv, obj: jobject, methodID: jmethodID, ...) { todo!(); } pub unsafe extern "system" fn CallVoidMethodV( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: va_list, ) { todo!(); } pub unsafe extern "system" fn CallVoidMethodA( env: *mut JNIEnv, obj: jobject, methodID: jmethodID, args: *const jvalue, ) { todo!(); } pub unsafe extern "C" fn CallNonvirtualObjectMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jobject { todo!(); } pub unsafe extern "system" fn CallNonvirtualObjectMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jobject { todo!(); } pub unsafe extern "system" fn CallNonvirtualObjectMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jobject { todo!(); } pub unsafe extern "C" fn CallNonvirtualBooleanMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jboolean { todo!(); } pub unsafe extern "system" fn CallNonvirtualBooleanMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jboolean { todo!(); } pub unsafe extern "system" fn CallNonvirtualBooleanMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jboolean { todo!(); } pub unsafe extern "C" fn CallNonvirtualByteMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jbyte { todo!(); } pub unsafe extern "system" fn CallNonvirtualByteMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jbyte { todo!(); } pub unsafe extern "system" fn CallNonvirtualByteMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jbyte { todo!(); } pub unsafe extern "C" fn CallNonvirtualCharMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jchar { todo!(); } pub unsafe extern "system" fn CallNonvirtualCharMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jchar { todo!(); } pub unsafe extern "system" fn CallNonvirtualCharMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jchar { todo!(); } pub unsafe extern "C" fn CallNonvirtualShortMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jshort { todo!(); } pub unsafe extern "system" fn CallNonvirtualShortMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jshort { todo!(); } pub unsafe extern "system" fn CallNonvirtualShortMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jshort { todo!(); } pub unsafe extern "C" fn CallNonvirtualIntMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jint { todo!(); } pub unsafe extern "system" fn CallNonvirtualIntMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jint { todo!(); } pub unsafe extern "system" fn CallNonvirtualIntMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jint { todo!(); } pub unsafe extern "C" fn CallNonvirtualLongMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jlong { todo!(); } pub unsafe extern "system" fn CallNonvirtualLongMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jlong { todo!(); } pub unsafe extern "system" fn CallNonvirtualLongMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jlong { todo!(); } pub unsafe extern "C" fn CallNonvirtualFloatMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jfloat { todo!(); } pub unsafe extern "system" fn CallNonvirtualFloatMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jfloat { todo!(); } pub unsafe extern "system" fn CallNonvirtualFloatMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jfloat { todo!(); } pub unsafe extern "C" fn CallNonvirtualDoubleMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) -> jdouble { todo!(); } pub unsafe extern "system" fn CallNonvirtualDoubleMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jdouble { todo!(); } pub unsafe extern "system" fn CallNonvirtualDoubleMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jdouble { todo!(); } pub unsafe extern "C" fn CallNonvirtualVoidMethod( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, ... ) { todo!(); } pub unsafe extern "system" fn CallNonvirtualVoidMethodV( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: va_list, ) { todo!(); } pub unsafe extern "system" fn CallNonvirtualVoidMethodA( env: *mut JNIEnv, obj: jobject, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) { todo!(); } pub unsafe extern "system" fn GetFieldID( env: *mut JNIEnv, clazz: jclass, name: *const c_char, sig: *const c_char, ) -> jfieldID { todo!(); } pub unsafe extern "system" fn GetObjectField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jobject { todo!(); } pub unsafe extern "system" fn GetBooleanField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jboolean { todo!(); } pub unsafe extern "system" fn GetByteField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jbyte { todo!(); } pub unsafe extern "system" fn GetCharField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jchar { todo!(); } pub unsafe extern "system" fn GetShortField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jshort { todo!(); } pub unsafe extern "system" fn GetIntField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jint { todo!(); } pub unsafe extern "system" fn GetLongField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jlong { todo!(); } pub unsafe extern "system" fn GetFloatField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jfloat { todo!(); } pub unsafe extern "system" fn GetDoubleField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, ) -> jdouble { todo!(); } pub unsafe extern "system" fn SetObjectField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jobject, ) { todo!(); } pub unsafe extern "system" fn SetBooleanField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jboolean, ) { todo!(); } pub unsafe extern "system" fn SetByteField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jbyte, ) { todo!(); } pub unsafe extern "system" fn SetCharField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jchar, ) { todo!(); } pub unsafe extern "system" fn SetShortField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jshort, ) { todo!(); } pub unsafe extern "system" fn SetIntField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jint, ) { todo!(); } pub unsafe extern "system" fn SetLongField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jlong, ) { todo!(); } pub unsafe extern "system" fn SetFloatField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jfloat, ) { todo!(); } pub unsafe extern "system" fn SetDoubleField( env: *mut JNIEnv, obj: jobject, fieldID: jfieldID, val: jdouble, ) { todo!(); } pub unsafe extern "system" fn GetStaticMethodID( env: *mut JNIEnv, clazz: jclass, name: *const c_char, sig: *const c_char, ) -> jmethodID { todo!(); } pub unsafe extern "C" fn CallStaticObjectMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jobject { todo!(); } pub unsafe extern "system" fn CallStaticObjectMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jobject { todo!(); } pub unsafe extern "system" fn CallStaticObjectMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jobject { todo!(); } pub unsafe extern "C" fn CallStaticBooleanMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jboolean { todo!(); } pub unsafe extern "system" fn CallStaticBooleanMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jboolean { todo!(); } pub unsafe extern "system" fn CallStaticBooleanMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jboolean { todo!(); } pub unsafe extern "C" fn CallStaticByteMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jbyte { todo!(); } pub unsafe extern "system" fn CallStaticByteMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jbyte { todo!(); } pub unsafe extern "system" fn CallStaticByteMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jbyte { todo!(); } pub unsafe extern "C" fn CallStaticCharMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jchar { todo!(); } pub unsafe extern "system" fn CallStaticCharMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jchar { todo!(); } pub unsafe extern "system" fn CallStaticCharMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jchar { todo!(); } pub unsafe extern "C" fn CallStaticShortMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jshort { todo!(); } pub unsafe extern "system" fn CallStaticShortMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jshort { todo!(); } pub unsafe extern "system" fn CallStaticShortMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jshort { todo!(); } pub unsafe extern "C" fn CallStaticIntMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jint { todo!(); } pub unsafe extern "system" fn CallStaticIntMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jint { todo!(); } pub unsafe extern "system" fn CallStaticIntMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jint { todo!(); } pub unsafe extern "C" fn CallStaticLongMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jlong { todo!(); } pub unsafe extern "system" fn CallStaticLongMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jlong { todo!(); } pub unsafe extern "system" fn CallStaticLongMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jlong { todo!(); } pub unsafe extern "C" fn CallStaticFloatMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jfloat { todo!(); } pub unsafe extern "system" fn CallStaticFloatMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jfloat { todo!(); } pub unsafe extern "system" fn CallStaticFloatMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jfloat { todo!(); } pub unsafe extern "C" fn CallStaticDoubleMethod( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, ... ) -> jdouble { todo!(); } pub unsafe extern "system" fn CallStaticDoubleMethodV( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: va_list, ) -> jdouble { todo!(); } pub unsafe extern "system" fn CallStaticDoubleMethodA( env: *mut JNIEnv, clazz: jclass, methodID: jmethodID, args: *const jvalue, ) -> jdouble { todo!(); } pub unsafe extern "C" fn CallStaticVoidMethod( env: *mut JNIEnv, cls: jclass, methodID: jmethodID, ... ) { todo!(); } pub unsafe extern "system" fn CallStaticVoidMethodV( env: *mut JNIEnv, cls: jclass, methodID: jmethodID, args: va_list, ) { todo!(); } pub unsafe extern "system" fn CallStaticVoidMethodA( env: *mut JNIEnv, cls: jclass, methodID: jmethodID, args: *const jvalue, ) { todo!(); } pub unsafe extern "system" fn GetStaticFieldID( env: *mut JNIEnv, clazz: jclass, name: *const c_char, sig: *const c_char, ) -> jfieldID { todo!(); } pub unsafe extern "system" fn GetStaticObjectField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jobject { todo!(); } pub unsafe extern "system" fn GetStaticBooleanField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jboolean { todo!(); } pub unsafe extern "system" fn GetStaticByteField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jbyte { todo!(); } pub unsafe extern "system" fn GetStaticCharField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jchar { todo!(); } pub unsafe extern "system" fn GetStaticShortField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jshort { todo!(); } pub unsafe extern "system" fn GetStaticIntField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jint { todo!(); } pub unsafe extern "system" fn GetStaticLongField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jlong { todo!(); } pub unsafe extern "system" fn GetStaticFloatField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jfloat { todo!(); } pub unsafe extern "system" fn GetStaticDoubleField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, ) -> jdouble { todo!(); } pub unsafe extern "system" fn SetStaticObjectField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jobject, ) { todo!(); } pub unsafe extern "system" fn SetStaticBooleanField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jboolean, ) { todo!(); } pub unsafe extern "system" fn SetStaticByteField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jbyte, ) { todo!(); } pub unsafe extern "system" fn SetStaticCharField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jchar, ) { todo!(); } pub unsafe extern "system" fn SetStaticShortField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jshort, ) { todo!(); } pub unsafe extern "system" fn SetStaticIntField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jint, ) { todo!(); } pub unsafe extern "system" fn SetStaticLongField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jlong, ) { todo!(); } pub unsafe extern "system" fn SetStaticFloatField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jfloat, ) { todo!(); } pub unsafe extern "system" fn SetStaticDoubleField( env: *mut JNIEnv, clazz: jclass, fieldID: jfieldID, value: jdouble, ) { todo!(); } pub unsafe extern "system" fn NewString( env: *mut JNIEnv, unicode: *const jchar, len: jsize, ) -> jstring { todo!(); } pub unsafe extern "system" fn GetStringLength(env: *mut JNIEnv, str: jstring) -> jsize { todo!(); } pub unsafe extern "system" fn GetStringChars( env: *mut JNIEnv, str: jstring, isCopy: *mut jboolean, ) -> *const jchar { todo!(); } pub unsafe extern "system" fn ReleaseStringChars( env: *mut JNIEnv, str: jstring, chars: *const jchar, ) { todo!(); } pub unsafe extern "system" fn NewStringUTF(env: *mut JNIEnv, utf: *const c_char) -> jstring { todo!(); } pub unsafe extern "system" fn GetStringUTFLength(env: *mut JNIEnv, str: jstring) -> jsize { todo!(); } pub unsafe extern "system" fn GetStringUTFChars( env: *mut JNIEnv, str: jstring, isCopy: *mut jboolean, ) -> *const c_char { todo!(); } pub unsafe extern "system" fn ReleaseStringUTFChars( env: *mut JNIEnv, str: jstring, chars: *const c_char, ) { todo!(); } pub unsafe extern "system" fn GetArrayLength(env: *mut JNIEnv, array: jarray) -> jsize { todo!(); } pub unsafe extern "system" fn NewObjectArray( env: *mut JNIEnv, len: jsize, clazz: jclass, init: jobject, ) -> jobjectArray { todo!(); } pub unsafe extern "system" fn GetObjectArrayElement( env: *mut JNIEnv, array: jobjectArray, index: jsize, ) -> jobject { todo!(); } pub unsafe extern "system" fn SetObjectArrayElement( env: *mut JNIEnv, array: jobjectArray, index: jsize, val: jobject, ) { todo!(); } pub unsafe extern "system" fn NewBooleanArray(env: *mut JNIEnv, len: jsize) -> jbooleanArray { todo!(); } pub unsafe extern "system" fn NewByteArray(env: *mut JNIEnv, len: jsize) -> jbyteArray { todo!(); } pub unsafe extern "system" fn NewCharArray(env: *mut JNIEnv, len: jsize) -> jcharArray { todo!(); } pub unsafe extern "system" fn NewShortArray(env: *mut JNIEnv, len: jsize) -> jshortArray { todo!(); } pub unsafe extern "system" fn NewIntArray(env: *mut JNIEnv, len: jsize) -> jintArray { todo!(); } pub unsafe extern "system" fn NewLongArray(env: *mut JNIEnv, len: jsize) -> jlongArray { todo!(); } pub unsafe extern "system" fn NewFloatArray(env: *mut JNIEnv, len: jsize) -> jfloatArray { todo!(); } pub unsafe extern "system" fn NewDoubleArray(env: *mut JNIEnv, len: jsize) -> jdoubleArray { todo!(); } pub unsafe extern "system" fn GetBooleanArrayElements( env: *mut JNIEnv, array: jbooleanArray, isCopy: *mut jboolean, ) -> *mut jboolean { todo!(); } pub unsafe extern "system" fn GetByteArrayElements( env: *mut JNIEnv, array: jbyteArray, isCopy: *mut jboolean, ) -> *mut jbyte { todo!(); } pub unsafe extern "system" fn GetCharArrayElements( env: *mut JNIEnv, array: jcharArray, isCopy: *mut jboolean, ) -> *mut jchar { todo!(); } pub unsafe extern "system" fn GetShortArrayElements( env: *mut JNIEnv, array: jshortArray, isCopy: *mut jboolean, ) -> *mut jshort { todo!(); } pub unsafe extern "system" fn GetIntArrayElements( env: *mut JNIEnv, array: jintArray, isCopy: *mut jboolean, ) -> *mut jint { todo!(); } pub unsafe extern "system" fn GetLongArrayElements( env: *mut JNIEnv, array: jlongArray, isCopy: *mut jboolean, ) -> *mut jlong { todo!(); } pub unsafe extern "system" fn GetFloatArrayElements( env: *mut JNIEnv, array: jfloatArray, isCopy: *mut jboolean, ) -> *mut jfloat { todo!(); } pub unsafe extern "system" fn GetDoubleArrayElements( env: *mut JNIEnv, array: jdoubleArray, isCopy: *mut jboolean, ) -> *mut jdouble { todo!(); } pub unsafe extern "system" fn ReleaseBooleanArrayElements( env: *mut JNIEnv, array: jbooleanArray, elems: *mut jboolean, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseByteArrayElements( env: *mut JNIEnv, array: jbyteArray, elems: *mut jbyte, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseCharArrayElements( env: *mut JNIEnv, array: jcharArray, elems: *mut jchar, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseShortArrayElements( env: *mut JNIEnv, array: jshortArray, elems: *mut jshort, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseIntArrayElements( env: *mut JNIEnv, array: jintArray, elems: *mut jint, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseLongArrayElements( env: *mut JNIEnv, array: jlongArray, elems: *mut jlong, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseFloatArrayElements( env: *mut JNIEnv, array: jfloatArray, elems: *mut jfloat, mode: jint, ) { todo!(); } pub unsafe extern "system" fn ReleaseDoubleArrayElements( env: *mut JNIEnv, array: jdoubleArray, elems: *mut jdouble, mode: jint, ) { todo!(); } pub unsafe extern "system" fn GetBooleanArrayRegion( env: *mut JNIEnv, array: jbooleanArray, start: jsize, l: jsize, buf: *mut jboolean, ) { todo!(); } pub unsafe extern "system" fn GetByteArrayRegion( env: *mut JNIEnv, array: jbyteArray, start: jsize, len: jsize, buf: *mut jbyte, ) { todo!(); } pub unsafe extern "system" fn GetCharArrayRegion( env: *mut JNIEnv, array: jcharArray, start: jsize, len: jsize, buf: *mut jchar, ) { todo!(); } pub unsafe extern "system" fn GetShortArrayRegion( env: *mut JNIEnv, array: jshortArray, start: jsize, len: jsize, buf: *mut jshort, ) { todo!(); } pub unsafe extern "system" fn GetIntArrayRegion( env: *mut JNIEnv, array: jintArray, start: jsize, len: jsize, buf: *mut jint, ) { todo!(); } pub unsafe extern "system" fn GetLongArrayRegion( env: *mut JNIEnv, array: jlongArray, start: jsize, len: jsize, buf: *mut jlong, ) { todo!(); } pub unsafe extern "system" fn GetFloatArrayRegion( env: *mut JNIEnv, array: jfloatArray, start: jsize, len: jsize, buf: *mut jfloat, ) { todo!(); } pub unsafe extern "system" fn GetDoubleArrayRegion( env: *mut JNIEnv, array: jdoubleArray, start: jsize, len: jsize, buf: *mut jdouble, ) { todo!(); } pub unsafe extern "system" fn SetBooleanArrayRegion( env: *mut JNIEnv, array: jbooleanArray, start: jsize, l: jsize, buf: *const jboolean, ) { todo!(); } pub unsafe extern "system" fn SetByteArrayRegion( env: *mut JNIEnv, array: jbyteArray, start: jsize, len: jsize, buf: *const jbyte, ) { todo!(); } pub unsafe extern "system" fn SetCharArrayRegion( env: *mut JNIEnv, array: jcharArray, start: jsize, len: jsize, buf: *const jchar, ) { todo!(); } pub unsafe extern "system" fn SetShortArrayRegion( env: *mut JNIEnv, array: jshortArray, start: jsize, len: jsize, buf: *const jshort, ) { todo!(); } pub unsafe extern "system" fn SetIntArrayRegion( env: *mut JNIEnv, array: jintArray, start: jsize, len: jsize, buf: *const jint, ) { todo!(); } pub unsafe extern "system" fn SetLongArrayRegion( env: *mut JNIEnv, array: jlongArray, start: jsize, len: jsize, buf: *const jlong, ) { todo!(); } pub unsafe extern "system" fn SetFloatArrayRegion( env: *mut JNIEnv, array: jfloatArray, start: jsize, len: jsize, buf: *const jfloat, ) { todo!(); } pub unsafe extern "system" fn SetDoubleArrayRegion( env: *mut JNIEnv, array: jdoubleArray, start: jsize, len: jsize, buf: *const jdouble, ) { todo!(); } pub unsafe extern "system" fn RegisterNatives( env: *mut JNIEnv, clazz: jclass, methods: *const JNINativeMethod, nMethods: jint, ) -> jint { todo!(); } pub unsafe extern "system" fn UnregisterNatives(env: *mut JNIEnv, clazz: jclass) -> jint { todo!(); } pub unsafe extern "system" fn MonitorEnter(env: *mut JNIEnv, obj: jobject) -> jint { todo!(); } pub unsafe extern "system" fn MonitorExit(env: *mut JNIEnv, obj: jobject) -> jint { todo!(); } pub unsafe extern "system" fn GetJavaVM(env: *mut JNIEnv, vm: *mut *mut JavaVM) -> jint { todo!(); } pub unsafe extern "system" fn GetStringRegion( env: *mut JNIEnv, str: jstring, start: jsize, len: jsize, buf: *mut jchar, ) { todo!(); } pub unsafe extern "system" fn GetStringUTFRegion( env: *mut JNIEnv, str: jstring, start: jsize, len: jsize, buf: *mut c_char, ) { todo!(); } pub unsafe extern "system" fn GetPrimitiveArrayCritical( env: *mut JNIEnv, array: jarray, isCopy: *mut jboolean, ) -> *mut c_void { todo!(); } pub unsafe extern "system" fn ReleasePrimitiveArrayCritical( env: *mut JNIEnv, array: jarray, carray: *mut c_void, mode: jint, ) { todo!(); } pub unsafe extern "system" fn GetStringCritical( env: *mut JNIEnv, string: jstring, isCopy: *mut jboolean, ) -> *const jchar { todo!(); } pub unsafe extern "system" fn ReleaseStringCritical( env: *mut JNIEnv, string: jstring, cstring: *const jchar, ) { todo!(); } pub unsafe extern "system" fn NewWeakGlobalRef(env: *mut JNIEnv, obj: jobject) -> jweak { todo!(); } pub unsafe extern "system" fn DeleteWeakGlobalRef(env: *mut JNIEnv, ref_: jweak) { todo!(); } pub unsafe extern "system" fn ExceptionCheck(env: *mut JNIEnv) -> jboolean { todo!(); } pub unsafe extern "system" fn NewDirectByteBuffer( env: *mut JNIEnv, address: *mut c_void, capacity: jlong, ) -> jobject { todo!(); } pub unsafe extern "system" fn GetDirectBufferAddress( env: *mut JNIEnv, buf: jobject, ) -> *mut c_void { todo!(); } pub unsafe extern "system" fn GetDirectBufferCapacity(env: *mut JNIEnv, buf: jobject) -> jlong { todo!(); } pub unsafe extern "system" fn GetObjectRefType(env: *mut JNIEnv, obj: jobject) -> jobjectRefType { todo!(); } ================================================ FILE: libjvm/src/private.rs ================================================ #![allow(unused_variables)] use jni_sys::{jclass, JNIEnv}; /// Used by java launcher as entry point #[no_mangle] pub unsafe extern "system" fn JVM_FindClassFromBootLoader( env: *const JNIEnv, name: *const i8, ) -> jclass { let class_name = std::ffi::CStr::from_ptr(name); let bytes = class_name.to_bytes(); let class = vm::runtime::require_class3(None, bytes); crate::util::class_ref_to_jclass(class) } ================================================ FILE: libjvm/src/util.rs ================================================ #![allow(unused_imports)] /// Conversions between jvm and jni api use jni_sys::{jclass, JNIEnv}; pub fn class_ref_to_jclass(class_ref: Option) -> jclass { if let Some(class_ref) = class_ref { class_ref.as_ref() as *const _ as jclass } else { std::ptr::null_mut() } } ================================================ FILE: note.txt ================================================ char ----------------------- Char in Java: 16-bit unsigned integers char in Rust: four bytes in size problem ---------------------- 1. System.arraycopy The performance of JVM depends on arraycopy. ArrayList list = new ArrayList(); int start = 0; while (start < 50000) { list.add(start++); } It's slow to run the code, but Oracle Java very quick. The reason is that, invoke 'clone' for every OopRef test case: $JDK_TEST/Character/CheckProp.java Key Points: a. create lots of Integer object b. ArrayList inner buf, new bigger buffer when reach limit, then, invoke System.arraycopy to move objects 2. dynamic workers in thread pool spawn & gc worker dynamic indicators: a. system payload b. worker queue c. thread types (io? calculation?) jdk ----------------------- ByteBuffer (along with all IntBuffer, LongBuffer, ...) are generated from X-Buffer.java.template. GC ----------------------- paper: http://users.cecs.anu.edu.au/~steveb/downloads/pdf/rust-ismm-2016.pdf an example of a high performance GC impl in Rust. codes: https://github.com/Manishearth/rust-gc Simple tracing (mark and sweep) garbage collector for Rust Milestones ----------------------- 2020.01.30 Add.java run successfully 2020.02.01 github 100 commits 2020.02.09 code lines reach 10k 2020.02.13 HelloWorld run successfully 2020.02.14 Oop::str replaced by java.lang.String 2020.02.15 github 200 commits, First Watchers, CertainLach, thanks! 2020.02.19 Thank Rust CC(https://rust.cc/),get lots of stars 2020.03.01 extract Ref from Oop, access prime value, no need lock anymore extract DataArea from Frame, printStackTrace has Line Number Included in 科技爱好者周刊 96th,thank yifeng http://www.ruanyifeng.com/blog/2020/02/weekly-issue-96.html 2020.03.03 merge first patch from CertainLach, thanks! 2020.03.04 CertainLach translate README & comments to english 2020.03.05 Switch to Oracle JDK github 300 commits 2020.03.12 support annotation 2020.03.17 extract class-parser crate 2020.03.22 github 400 commits 2020.04.18 github 500 commits 'javap' nearly complete study how to impl java threads signature parser rewrite based nom support generic 2020.05.10 threads supported study gc 2020.06.06 simple gc based Rust Arc avoid lock when access Oop::Ref 2020.06.20 study linux 'perf' && optimize, and performance is getting better and better 2020.06.25 github 600 commits improve vm performance . rm RwLock of Frame's DataArea . replace Slot::Primitive(vec bytes) by Slot::I32..., avoid vec malloc . use jvm_initIDs to cache filed's offset(java_io_FileOutputStream) . reimpl Oop::Ref as Box ptr, avoid lock . ConstantPoolCache for method & field offset . Cargo.toml profile.release conf . cache MethodSignature in Method MyCheckProp: #sum_t_list_add = 5494 -> 1507 #sum_t_map_get = 123 -> 27 #sum_t_map_put = 8 -> 3 #sum_t_parse_int = 626 -> 159 #sum_t_println = 3059 -> 768 #sum_t_int2integer = 3201 -> 1255 ================================================ FILE: tools/javap/Cargo.toml ================================================ [package] name = "javap" version = "0.2.1" authors = ["Dou Chuan <1843657913@qq.com>"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] clap = "2.33.1" classfile = { path = "../../crates/classfile", version = "0.1.0" } class-parser = { path = "../../crates/class-parser", version = "0.1.0" } time = "0.2.16" env_logger = "0.7.1" handlebars = "3.0.1" lazy_static = "1.4.0" log = "0.4.0" md5 = "0.7.0" serde = "1.0.0" serde_derive = "1.0.75" serde_json = "1.0.39" zip = "0.5.4" ================================================ FILE: tools/javap/run.sh ================================================ export RUST_LOG=trace #export RUST_LOG=info #export RUST_LOG=warn export RUST_BACKTRACE=full ### version #cargo run -q -- --version ### line number #cargo run -q -- --cp test -l AbstractGraphicObject #cargo run -q -- --cp test -l GraphicObject #cargo run -q -- --cp test -l HelloWorld #cargo run -q -- --cp test -l EnumMobile #cargo run -q -- --cp test -l Interface1 #cargo run -q -- --cp test -l Hockey #cargo run -q -- --cp test -l WifeAndMother ### disassemble #cargo run -q -- --cp test -c HelloWorld ### disassemble & line number #cargo run -q -- --cp test -c -l HelloWorld #cargo run -q -- --cp test -c -l EnumMobile #cargo run -q -- --cp test --constants HelloWorld #cargo run -q -- --cp test --constants Hockey ### access flags #echo "default access flags" #cargo run -q -- --cp test HelloWorld #echo "public" #cargo run -q -- --cp test --public HelloWorld #echo "protected" #cargo run -q -- --cp test --protected HelloWorld #echo "private" #cargo run -q -- --cp test --private HelloWorld ### conflict args, error #cargo run -- --cp test --private --public HelloWorld ### sysinfo #cargo run -q -- --cp test --sysinfo HelloWorld #cargo run -q -- --cp test/testng-6.8.21.jar --sysinfo org.testng.collections.Lists #cargo run -q -- --cp test HelloWorld ### Print internal type signatures #cargo run -q -- --cp test -s HelloWorld ### exception #cargo run -q -- --cp test Ex #cargo run -q -- --cp test -s Ex #cargo run -q -- --cp test -c Ex #cargo run -q -- --cp test -v Ex #cargo run -q -- --cp test -v HelloWorld #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.xml.XmlUtils #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.xml.XmlUtils #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.TestNG ### contains generic params #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.TestNG #cargo run -q -- --cp test/testng-6.8.21.jar -s org.testng.TestNG #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.TestRunner #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.reporters.Files #cargo run -q -- --cp test/testng-6.8.21.jar -v org.testng.collections.Maps cargo run -q -- --cp test -v Generic1 ### test Not Found #cargo run -q -- --cp test/testng-6.8.21.jar -v passed.png ================================================ FILE: tools/javap/src/cmd/disassemble.rs ================================================ use crate::cmd::Cmd; use crate::misc::SysInfo; use crate::sd::{ ClassInfoSerde, ClassVersionSerde, FieldInfoSerde, LineNumberSerde, MethodInfoSerde, StackMapFrameSerde, StackMapTableSerde, SysInfoSerde, }; use crate::template; use crate::trans::{self, AccessFlagHelper, SignatureTypeTranslator}; use clap::ArgMatches; use classfile::flags as access_flags; use classfile::ClassFile; pub struct Disassemble { show_access_flags: u16, enable_verbose: bool, enable_line_number: bool, enable_code: bool, enable_sys_info: bool, enable_inner_signature: bool, } impl Disassemble { pub fn new(m: &ArgMatches) -> Option { let show_access_flags = Self::build_show_access_flags(m); let enable_verbose = m.is_present("verbose"); let enable_line_number = enable_verbose || m.is_present("line_number"); let enable_code = enable_verbose || m.is_present("disassemble"); let enable_sys_info = enable_verbose || m.is_present("sysinfo"); let enable_inner_signature = enable_verbose || m.is_present("signatures"); Some(Self { show_access_flags, enable_verbose, enable_line_number, enable_code, enable_sys_info, enable_inner_signature, }) } } impl Cmd for Disassemble { fn run(&self, si: &SysInfo, cf: ClassFile) { self.do_render(si, cf); } } impl Disassemble { fn do_render(&self, si: &SysInfo, cf: ClassFile) { let reg = template::get_engine(); let sys_info = self.build_sys_info(si, &cf); let version = ClassVersionSerde { minor: cf.version.minor, major: cf.version.major, }; let flags = trans::class_access_flags_name(&cf); let source_file = trans::class_source_file(&cf); let class_head = self.build_class_define(&cf); let fields = self.build_fields(&cf); let methods = self.build_methods(&cf); let cp = if self.enable_verbose { trans::class_constant_pool(&cf) } else { vec![] }; let inner_classes = trans::class_inner_classes(&cf); let has_inner_classes = self.enable_verbose && !inner_classes.is_empty(); let signature = trans::class_signature_raw(&cf).unwrap_or("".to_string()); let has_signature = self.enable_verbose && !signature.is_empty(); let data = ClassInfoSerde { sys_info, version, flags, source_file, class_head, fields, methods, cp, inner_classes, signature, enable_verbose: self.enable_verbose, enable_sys_info: self.enable_sys_info, has_inner_classes, has_signature, }; println!("{}", reg.render_template(template::CLASS, &data).unwrap()); } } impl Disassemble { fn build_show_access_flags(m: &ArgMatches) -> u16 { let mut flags = 0; if m.is_present("public") { flags = access_flags::ACC_PUBLIC; } if m.is_present("protected") { flags = access_flags::ACC_PROTECTED; } if m.is_present("private") { flags = access_flags::ACC_PRIVATE; } flags } fn build_class_define(&self, cf: &ClassFile) -> String { let mut head_parts = vec![]; let class_flags = trans::class_access_flags(&cf); let this_class = trans::class_this_class(&cf); head_parts.push(class_flags); head_parts.push(this_class.clone()); if cf.acc_flags.is_interface() { if cf.interfaces.len() != 0 { head_parts.push("extends".to_string()); let parent_interfaces = trans::class_parent_interfaces(&cf).join(", "); head_parts.push(parent_interfaces); } } else if cf.acc_flags.is_enum() { head_parts.push("extends".to_string()); let super_class = { let mut super_class = trans::class_super_class(&cf); super_class.push_str("<"); super_class.push_str(this_class.as_str()); super_class.push_str(">"); super_class }; head_parts.push(super_class); } else { let class_signature = trans::class_signature(&cf); //build from Signature Attribute match class_signature { Some(cs) => { assert_eq!(cs.len(), cf.interfaces.len() + 1); //1 means super Class let super_class = cs.get(0).unwrap(); head_parts.push("extends".to_string()); head_parts.push(super_class.into_string()); if cs.len() > 1 { head_parts.push("implements".to_string()); for it in &cs[1..] { head_parts.push(it.into_string()); } } } None => { let super_class = trans::class_super_class(&cf); if super_class != "java.lang.Object" { head_parts.push("extends".to_string()); head_parts.push(super_class); } if cf.interfaces.len() != 0 { head_parts.push("implements".to_string()); let parent_interfaces = trans::class_parent_interfaces(&cf).join(", "); head_parts.push(parent_interfaces); } } } } head_parts.join(" ") } fn build_sys_info(&self, si: &SysInfo, cf: &ClassFile) -> SysInfoSerde { if self.enable_sys_info { let source_file = trans::class_source_file(&cf); SysInfoSerde { class_file: si.class_file.clone(), last_modified: si.last_modified.clone(), size: si.size, checksum: si.checksum.clone(), compiled_from: source_file, } } else { SysInfoSerde::default() } } fn build_fields(&self, cf: &ClassFile) -> Vec { let fields = trans::class_fields(&cf, self.show_access_flags); fields .iter() .map(|it| FieldInfoSerde { desc: it.desc.clone(), descriptor: it.descriptor.clone(), signature: it.signature.clone(), flags: it.flags.clone(), constant: it.constant.clone(), enable_descriptor: self.enable_inner_signature, enable_attr_signature: !it.signature.is_empty() && self.enable_verbose, enable_flags: self.enable_verbose, enable_constant: !it.constant.is_empty() && self.enable_verbose, }) .collect() } fn build_methods(&self, cf: &ClassFile) -> Vec { let is_interface = cf.acc_flags.is_interface(); let methods = trans::class_methods( cf, self.enable_line_number, self.enable_code, self.show_access_flags, ); methods .iter() .map(|it| { if is_interface { MethodInfoSerde { desc: it.desc.clone(), line_number_table: vec![], code: Default::default(), descriptor: it.descriptor.clone(), signature: it.signature.clone(), flags: "".to_string(), throws: "".to_string(), ex_table: vec![], stack_map_table: Default::default(), local_var_table: vec![], local_var_type_table: vec![], enable_line_number: false, enable_code: false, enable_descriptor: self.enable_inner_signature, enable_flags: false, enable_throws: false, enable_stack_map: false, enable_local_var_table: false, enable_local_var_type_table: false, enable_attr_signature: false, has_ex_table: false, } } else { let enable_line_number = self.enable_line_number; let enable_code = self.enable_code; let line_number_table: Vec = if enable_line_number { it.line_num_table .iter() .map(|it| LineNumberSerde { start_pc: it.start_pc, line_number: it.number, }) .collect() } else { vec![] }; let code = if enable_code { let mut code = it.code.clone(); code.enable_verbose = self.enable_verbose; code } else { Default::default() }; let stack_map_table = if self.enable_verbose { let frames: Vec = it .stack_map_table .iter() .map(|frame| { let desc = format!("frame_type = {} {}", frame.tag, frame.comment); StackMapFrameSerde { desc, items: frame.items.clone(), } }) .collect(); StackMapTableSerde { number_of_entries: frames.len(), frames, } } else { Default::default() }; let local_var_table = it.local_variable_table.clone(); let local_var_type_table = it.local_variable_type_table.clone(); let enable_stack_map = !stack_map_table.frames.is_empty() && self.enable_verbose; let enable_local_var_table = !local_var_table.is_empty() && (self.enable_verbose || self.enable_line_number); let enable_local_var_type_table = !local_var_type_table.is_empty() && self.enable_verbose; MethodInfoSerde { desc: it.desc.clone(), line_number_table, code, descriptor: it.descriptor.clone(), signature: it.signature.clone(), flags: it.flags.clone(), throws: it.throws.clone(), ex_table: it.ex_table.clone(), stack_map_table, local_var_table, local_var_type_table, enable_line_number, enable_code, enable_descriptor: self.enable_inner_signature, enable_flags: self.enable_verbose, enable_throws: !it.throws.is_empty() && self.enable_verbose, enable_stack_map, enable_local_var_table, enable_local_var_type_table, enable_attr_signature: !it.signature.is_empty() && self.enable_verbose, has_ex_table: !it.ex_table.is_empty() && enable_code, } } }) .collect() } } ================================================ FILE: tools/javap/src/cmd/mod.rs ================================================ use crate::misc::SysInfo; use classfile::ClassFile; mod disassemble; pub use disassemble::Disassemble; pub trait Cmd { fn run(&self, si: &SysInfo, cf: ClassFile); } ================================================ FILE: tools/javap/src/main.rs ================================================ #[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_json; extern crate handlebars; #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; extern crate clap; extern crate env_logger; mod cmd; mod misc; mod sd; mod strategy; mod template; mod trans; mod util; use clap::{App, Arg}; use class_parser::parse_class; /* Usage: javap where possible options include: -help --help -? Print this usage message -version Version information -v -verbose Print additional information -l Print line number and local variable tables -public Show only public classes and members -protected Show protected/public classes and members -package Show package/protected/public classes and members (default) -p -private Show all classes and members -c Disassemble the code -s Print internal type signatures -sysinfo Show system info (path, size, date, MD5 hash) of class being processed -constants Show final constants -classpath Specify where to find user class files -cp Specify where to find user class files -bootclasspath Override location of bootstrap class files */ /* todo: 1. can clap support '-private' style option? 2. line_number::render_enum, Handlebars's render format looks ugly custom control counts of spaces try the other template engine */ fn main() { init(); let matches = App::new("") .arg( Arg::with_name("version") .long("version") .help("Print this usage message"), ) .arg( Arg::with_name("verbose") .long("verbose") .short("v") .help("Print additional information"), ) .arg( Arg::with_name("line_number") .short("l") .help("Print line number and local variable tables"), ) .arg( Arg::with_name("public") .long("public") .conflicts_with_all(&["protected", "package", "private"]) .help("Show only public classes and members"), ) .arg( Arg::with_name("protected") .long("protected") .help("Show protected/public classes and members"), ) .arg( Arg::with_name("package") .long("package") .help("Show package/protected/public classes\nand members (default)"), ) .arg( Arg::with_name("private") .long("private") .short("p") .help("Show all classes and members"), ) .arg( Arg::with_name("disassemble") .short("c") .help("Disassemble the code"), ) .arg( Arg::with_name("signatures") .short("s") .help("Print internal type signatures"), ) .arg( Arg::with_name("sysinfo") .long("sysinfo") .help("Show system info (path, size, date, MD5 hash)\nof class being processed"), ) .arg( Arg::with_name("constants") .long("constants") .help("Show final constants"), ) .arg( Arg::with_name("cp") .long("cp") .help("Specify where to find user class files") .default_value(".") .takes_value(true), ) .arg( Arg::with_name("classpath") .long("classpath") .help("Specify where to find user class files") .default_value(".") .takes_value(true), ) .arg(Arg::with_name("classes").multiple(true).index(1)) .get_matches(); strategy::setup_classpath(&matches); if matches.is_present("version") { println!(env!("CARGO_PKG_VERSION")); } let commander = strategy::choose(&matches); match matches.values_of("classes") { Some(classes) => { for it in classes { match misc::find_class(it) { Ok(r) => { if let Ok((_, cf)) = parse_class(&r.1) { commander.run(&r.0, cf); } else { error!("parse class error: {}", it); } } Err(_e) => { println!("Error: class not found: {}", it); } } } } None => (), } } fn init() { env_logger::init(); misc::cp_manager_init(); } ================================================ FILE: tools/javap/src/misc/class_path_manager.rs ================================================ #![allow(unused)] use crate::misc::SysInfo; use crate::util; use std::fs::File; use std::io::{self, Read}; use std::path::Path; use std::sync::{Arc, Mutex}; use zip::ZipArchive; lazy_static! { static ref CPM: Mutex = { Mutex::new(ClassPathManager::new()) }; } pub fn init() { lazy_static::initialize(&CPM); } pub fn find_class(name: &str) -> Result { let cpm = CPM.lock().unwrap(); cpm.search_class(name) } pub fn add_path(path: &str) { let mut cpm = CPM.lock().unwrap(); let _ = cpm.add_class_path(path); } pub fn add_paths(path: &str) { let mut cpm = CPM.lock().unwrap(); cpm.add_class_paths(path); } #[derive(Debug)] // pub struct ClassPathResult(pub SysInfo, pub Vec); pub struct ClassPathResult(pub SysInfo, pub Vec); type ZipRef = Arc>>>; enum ClassSource { Dir(String), Jar(ZipRef, String), } struct ClassPathManager { runtime_class_path: Vec, } impl ClassPathManager { fn new() -> Self { Self { runtime_class_path: vec![], } } pub fn add_class_path(&mut self, path: &str) -> Result<(), io::Error> { let p = Path::new(path); if p.is_dir() { self.runtime_class_path .push(ClassSource::Dir(path.to_string())); } else { let f = File::open(p)?; let z = ZipArchive::new(f)?; let handle = Arc::new(Mutex::new(Box::new(z))); self.runtime_class_path .push(ClassSource::Jar(handle, path.to_string())); } Ok(()) } pub fn add_class_paths(&mut self, path: &str) { path.split(util::PATH_SEP) .for_each(|p| match self.add_class_path(p) { Err(e) => error!("add class path error, path={}, e={:?}", p, e), _ => (), }); } pub fn search_class(&self, name: &str) -> Result { let name = name.replace("/", util::FILE_SEP); let name = name.replace(".", util::FILE_SEP); // trace!("search_class: {}", name); for it in self.runtime_class_path.iter() { match it { ClassSource::Dir(path) => { let mut p = String::from(path); p.push_str(util::FILE_SEP); p.push_str(&name); p.push_str(".class"); match File::open(&p) { Ok(mut f) => { //todo: process error let meta = f.metadata().unwrap(); let mut v = Vec::with_capacity(meta.len() as usize); let _ = f.read_to_end(&mut v); let sys_info = SysInfo { class_file: util::to_abs_path(&p), last_modified: util::format_time1(meta.modified().unwrap()), size: meta.len() as usize, checksum: util::md5_checksum(v.as_slice()), }; return Ok(ClassPathResult(sys_info, v)); } _ => (), } } ClassSource::Jar(handle, path) => { let mut p = String::from(&name); p.push_str(".class"); let mut handle = handle.lock().unwrap(); let zf = handle.by_name(&p); match zf { Ok(mut zf) => { let mut v = Vec::with_capacity(zf.size() as usize); let r = zf.read_to_end(&mut v); assert!(r.is_ok()); let mut class_file = String::from(util::JAR_FILE_PREFIX); let jar_abs = util::to_abs_path(path); class_file.push_str(jar_abs.as_str()); class_file.push_str("!/"); class_file.push_str(p.as_str()); let t = zf.last_modified().to_time().to_timespec().sec; let sys_info = SysInfo { class_file, last_modified: util::format_time2(t), size: zf.size() as usize, checksum: util::md5_checksum(v.as_slice()), }; return Ok(ClassPathResult(sys_info, v)); } _ => (), } } } } return Err(io::Error::new( io::ErrorKind::NotFound, format!("Search class failed: {}", name), )); } pub fn size(&self) -> usize { self.runtime_class_path.len() } } ================================================ FILE: tools/javap/src/misc/mod.rs ================================================ mod class_path_manager; mod sys_info; pub use class_path_manager::add_path as add_cp_path; pub use class_path_manager::find_class; pub use class_path_manager::init as cp_manager_init; pub use sys_info::SysInfo; ================================================ FILE: tools/javap/src/misc/sys_info.rs ================================================ #[derive(Debug, Clone)] pub struct SysInfo { pub class_file: String, pub last_modified: String, pub size: usize, pub checksum: String, } ================================================ FILE: tools/javap/src/sd/mod.rs ================================================ #[derive(Serialize)] pub struct ClassInfoSerde { pub sys_info: SysInfoSerde, pub version: ClassVersionSerde, pub flags: String, pub source_file: String, pub class_head: String, pub fields: Vec, pub methods: Vec, pub cp: Vec, pub inner_classes: Vec, pub signature: String, pub enable_verbose: bool, pub enable_sys_info: bool, pub has_inner_classes: bool, pub has_signature: bool, } #[derive(Serialize)] pub struct MethodInfoSerde { pub desc: String, pub line_number_table: Vec, pub descriptor: String, pub signature: String, pub code: CodeSerde, pub flags: String, pub throws: String, pub ex_table: Vec, pub stack_map_table: StackMapTableSerde, pub local_var_table: Vec, pub local_var_type_table: Vec, pub enable_line_number: bool, pub enable_code: bool, pub enable_descriptor: bool, pub enable_flags: bool, pub enable_throws: bool, pub enable_stack_map: bool, pub enable_local_var_table: bool, pub enable_local_var_type_table: bool, pub enable_attr_signature: bool, pub has_ex_table: bool, } #[derive(Serialize, Clone)] pub struct CodeSerde { pub max_stack: u16, pub max_locals: u16, pub args_size: usize, pub codes: Vec, pub enable_verbose: bool, } #[derive(Serialize)] pub struct FieldInfoSerde { pub desc: String, pub descriptor: String, //constant pool descriptor pub signature: String, //Attribute Signature pub flags: String, pub constant: String, pub enable_descriptor: bool, pub enable_flags: bool, pub enable_attr_signature: bool, pub enable_constant: bool, } #[derive(Serialize)] pub struct LineNumberSerde { pub start_pc: u16, pub line_number: u16, } #[derive(Serialize)] pub struct SysInfoSerde { pub class_file: String, pub last_modified: String, pub size: usize, pub checksum: String, pub compiled_from: String, } #[derive(Serialize)] pub struct ClassVersionSerde { pub minor: u16, pub major: u16, } #[derive(Serialize)] pub struct StackMapTableSerde { pub number_of_entries: usize, pub frames: Vec, } #[derive(Serialize)] pub struct StackMapFrameSerde { pub desc: String, pub items: Vec, } impl Default for SysInfoSerde { fn default() -> Self { Self { class_file: "".to_string(), last_modified: "".to_string(), size: 0, checksum: "".to_string(), compiled_from: "".to_string(), } } } impl Default for CodeSerde { fn default() -> Self { Self { max_stack: 0, max_locals: 0, args_size: 0, codes: vec![], enable_verbose: false, } } } impl Default for StackMapTableSerde { fn default() -> Self { Self { number_of_entries: 0, frames: vec![], } } } impl Default for StackMapFrameSerde { fn default() -> Self { Self { desc: "".to_string(), items: vec![], } } } ================================================ FILE: tools/javap/src/strategy.rs ================================================ use crate::cmd::{Cmd, Disassemble}; use crate::misc; use crate::util; use clap::ArgMatches; pub fn choose(m: &ArgMatches) -> Box { match Disassemble::new(m) { Some(d) => Box::new(d), None => unimplemented!(), } } pub fn setup_classpath(matches: &ArgMatches) { let mut added = std::collections::HashSet::new(); vec![ matches.value_of("cp").unwrap(), matches.value_of("classpath").unwrap(), ] .iter() .for_each(|&v| { let paths = v.split(util::PATH_SEP); paths.for_each(|path| { if !added.contains(path) { misc::add_cp_path(path); added.insert(path); } }); }); } ================================================ FILE: tools/javap/src/template.rs ================================================ use handlebars::Handlebars; pub const PART_COMPILED_FROM: &str = "Compiled from \"{{source_file}}\""; pub const PART_SYS_INFO: &str = " Classfile {{sys_info.class_file}} Last modified {{sys_info.last_modified}}; size {{sys_info.size}} bytes MD5 checksum {{sys_info.checksum}} Compiled from \"{{sys_info.compiled_from}}\""; pub const PART_FIELDS: &str = " {{~#each fields as |field|}} {{ desc }} {{~#if enable_descriptor}} descriptor: {{descriptor~}} {{/if}} {{~#if enable_flags}} flags: {{flags~}} {{/if}} {{~#if enable_attr_signature}} Signature: {{signature~}} {{/if}} {{~#if enable_constant}} ConstantValue: {{constant~}} {{/if}} {{/each}}"; pub const PART_METHODS: &str = " {{~#each methods}} {{ desc }} {{~#if enable_descriptor}} descriptor: {{descriptor~}} {{/if}} {{~#if enable_flags}} flags: {{flags~}} {{/if}} {{~#if enable_code}} Code: {{~#if code.enable_verbose}} stack={{code.max_stack}}, locals={{code.max_locals}}, args_size={{code.args_size~}} {{/if}} {{~#each code.codes}} {{this ~}} {{/each~}} {{/if}} {{~#if has_ex_table}} Exception table: {{~#each ex_table}} {{this ~}} {{/each}} {{/if}} {{~#if enable_line_number}} LineNumberTable: {{~#each line_number_table}} line {{line_number}}: {{start_pc ~}} {{/each~}} {{/if}} {{~#if enable_local_var_table}} LocalVariableTable: {{~#each local_var_table}} {{this ~}} {{/each~}} {{/if}} {{~#if enable_local_var_type_table}} LocalVariableTypeTable: {{~#each local_var_type_table}} {{this ~}} {{/each~}} {{/if}} {{~#if enable_stack_map}} StackMapTable: number_of_entries = {{stack_map_table.number_of_entries}} {{~#each stack_map_table.frames}} {{desc}} {{~#each items}} {{this ~}} {{/each~}} {{/each~}} {{/if}} {{~#if enable_throws}} Exceptions: throws {{throws}} {{/if}} {{~#if enable_attr_signature}} Signature: {{signature}} {{/if}} {{/each}}"; pub const PART_CP: &str = " Constant pool: {{~#each cp}} {{this ~}} {{/each}} "; pub const CLASS: &str = " {{~#if enable_sys_info}} {{~> sys_info ~}} {{~else~}} {{~> compiled_from}} {{/if}} {{~#if enable_verbose }} {{class_head}} minor version: {{version.minor}} major version: {{version.major}} flags: {{flags}} {{~> constant_pool ~}} { {{~else~}} {{class_head}} { {{/if}} {{~> fields }} {{~> methods }} } {{~#if has_signature}} {{signature~}} {{/if}} {{~#if enable_verbose}} SourceFile: \"{{source_file}}\" {{~/if~}} {{~#if has_inner_classes}} InnerClasses: {{~#each inner_classes}} {{this ~}} {{/each}} {{/if}} "; pub fn get_engine() -> Handlebars<'static> { let mut h = Handlebars::new(); let _ = h.register_partial("compiled_from", PART_COMPILED_FROM); let _ = h.register_partial("sys_info", PART_SYS_INFO); let _ = h.register_partial("fields", PART_FIELDS); let _ = h.register_partial("methods", PART_METHODS); let _ = h.register_partial("constant_pool", PART_CP); // let _ = h.register_partial("stack_map_table", PART_STACK_MAP_TABLE); h.register_escape_fn(handlebars::no_escape); h } ================================================ FILE: tools/javap/src/trans/access_flag.rs ================================================ use classfile::flags as class_flags; pub struct Translator { flags: AccessFlag, } impl Translator { pub fn new(flags: AccessFlag) -> Self { Self { flags } } } impl Translator { pub fn class_access_flags(&self, only_flag: bool) -> String { let mut parts = vec![]; let flags = self.flags; if flags.is_public() { parts.push("public"); } if flags.is_static() { parts.push("static"); } if flags.is_final() { parts.push("final"); } if !only_flag { if flags.is_interface() { parts.push("interface"); } else if flags.is_enum() { parts.push("class"); } else { if flags.is_abstract() { parts.push("abstract class"); } else { parts.push("class") } } } parts.join(" ") } pub fn method_access_flags(&self) -> String { let mut parts = vec![]; let flags = self.flags; if flags.is_public() { parts.push("public"); } else if flags.is_protected() { parts.push("protected"); } else if flags.is_private() { parts.push("private"); } if flags.is_static() { parts.push("static"); } if flags.is_native() { parts.push("native"); } if flags.is_final() { parts.push("final"); } else if flags.is_abstract() { parts.push("abstract"); } parts.join(" ") } pub fn field_access_flags(&self) -> String { let mut parts = vec![]; let flags = self.flags; if flags.is_public() { parts.push("public"); } else if flags.is_protected() { parts.push("protected"); } else if flags.is_private() { parts.push("private"); } if flags.is_static() { parts.push("static"); } if flags.is_final() { parts.push("final"); } parts.join(" ") } pub fn access_flag_inner(&self) -> String { let mut parts = vec![]; let flags = self.flags; if flags.is_public() { parts.push("ACC_PUBLIC"); } if flags.is_protected() { parts.push("ACC_PROTECTED"); } if flags.is_static() { parts.push("ACC_STATIC"); } if flags.is_final() { parts.push("ACC_FINAL"); } if flags.is_super() { parts.push("ACC_SUPER"); } if flags.is_interface() { parts.push("ACC_INTERFACE"); } if flags.is_abstract() { parts.push("ACC_ABSTRACT"); } if flags.is_annotation() { parts.push("ACC_ANNOTATION"); } if flags.is_enum() { parts.push("ACC_ENUM"); } parts.join(", ") } } type AccessFlag = u16; pub trait AccessFlagHelper { fn is_public(&self) -> bool; fn is_final(&self) -> bool; fn is_super(&self) -> bool; fn is_interface(&self) -> bool; fn is_abstract(&self) -> bool; fn is_synthetic(&self) -> bool; fn is_annotation(&self) -> bool; fn is_enum(&self) -> bool; fn is_private(&self) -> bool; fn is_protected(&self) -> bool; fn is_static(&self) -> bool; fn is_synchronized(&self) -> bool; fn is_bridge(&self) -> bool; fn is_varargs(&self) -> bool; fn is_native(&self) -> bool; fn is_strict(&self) -> bool; fn is_package(&self) -> bool; fn compare(&self, other: u16) -> i32; } impl AccessFlagHelper for AccessFlag { fn is_public(&self) -> bool { (*self & class_flags::ACC_PUBLIC) != 0 } fn is_final(&self) -> bool { (*self & class_flags::ACC_FINAL) != 0 } fn is_super(&self) -> bool { (*self & class_flags::ACC_SUPER) != 0 } fn is_interface(&self) -> bool { (*self & class_flags::ACC_INTERFACE) != 0 } fn is_abstract(&self) -> bool { (*self & class_flags::ACC_ABSTRACT) != 0 } fn is_synthetic(&self) -> bool { (*self & class_flags::ACC_SYNTHETIC) != 0 } fn is_annotation(&self) -> bool { (*self & class_flags::ACC_ANNOTATION) != 0 } fn is_enum(&self) -> bool { (*self & class_flags::ACC_ENUM) != 0 } fn is_private(&self) -> bool { (*self & class_flags::ACC_PRIVATE) != 0 } fn is_protected(&self) -> bool { (*self & class_flags::ACC_PROTECTED) != 0 } fn is_static(&self) -> bool { (*self & class_flags::ACC_STATIC) != 0 } fn is_synchronized(&self) -> bool { (*self & class_flags::ACC_SYNCHRONIZED) != 0 } fn is_bridge(&self) -> bool { (*self & class_flags::ACC_BRIDGE) != 0 } fn is_varargs(&self) -> bool { (*self & class_flags::ACC_VARARGS) != 0 } fn is_native(&self) -> bool { (*self & class_flags::ACC_NATIVE) != 0 } fn is_strict(&self) -> bool { (*self & class_flags::ACC_STRICT) != 0 } fn is_package(&self) -> bool { *self == 0 } //compare access permission // *self > other, 1 // *self == other, 0 // *self < other, -1 fn compare(&self, other: u16) -> i32 { let flags = *self; if flags == other { return 0; } if flags == 0 { if other.is_private() { return 1; } } else if flags.is_public() { if !other.is_public() { return 1; } } else if flags.is_protected() { if other == 0 || other.is_private() { return 1; } } -1 } } ================================================ FILE: tools/javap/src/trans/class_file.rs ================================================ use super::FieldTranslator; use super::{MethodTranslation, MethodTranslator}; use crate::trans::AccessFlagsTranslator; use crate::trans::{AccessFlagHelper, FieldTranslation}; use class_parser::ClassSignature; use classfile::AttributeType; use classfile::ClassFile; use classfile::{constant_pool, SignatureType}; const S_UNKNOWN: &str = "unknown"; pub struct Translator<'a> { cf: &'a ClassFile, } impl<'a> Translator<'a> { pub fn new(cf: &'a ClassFile) -> Self { Self { cf } } } impl<'a> Translator<'a> { pub fn source_file(&self) -> String { for it in &self.cf.attrs { match it { AttributeType::SourceFile { source_file_index } => { return constant_pool::get_utf8(&self.cf.cp, *source_file_index as usize) .map_or_else( || S_UNKNOWN.into(), |v| String::from_utf8_lossy(v.as_slice()).into(), ); } _ => (), } } String::from(S_UNKNOWN) } pub fn this_class(&self) -> String { constant_pool::get_class_name(&self.cf.cp, self.cf.this_class as usize).map_or_else( || S_UNKNOWN.into(), |v| String::from_utf8_lossy(v.as_slice()).replace("/", "."), ) } pub fn super_class(&self) -> String { constant_pool::get_class_name(&self.cf.cp, self.cf.super_class as usize).map_or_else( || S_UNKNOWN.into(), |v| String::from_utf8_lossy(v.as_slice()).replace("/", "."), ) } pub fn parent_interfaces(&self) -> Vec { assert_ne!(self.cf.interfaces.len(), 0); let mut interfaces = Vec::with_capacity(self.cf.interfaces.len()); for it in self.cf.interfaces.iter() { let name = constant_pool::get_class_name(&self.cf.cp, *it as usize).map_or_else( || S_UNKNOWN.into(), |v| String::from_utf8_lossy(v.as_slice()).replace("/", "."), ); interfaces.push(name); } interfaces } pub fn access_flags(&self) -> String { let flags = self.cf.acc_flags; let t = AccessFlagsTranslator::new(flags); t.class_access_flags(false) } pub fn access_flags_name(&self) -> String { let flags = self.cf.acc_flags; let t = AccessFlagsTranslator::new(flags); t.access_flag_inner() } pub fn signature_raw(&self) -> Option { self.cf.signature().map(|idx| { let v = constant_pool::get_utf8(&self.cf.cp, idx).unwrap(); let signature = String::from_utf8_lossy(v.as_slice()); format!("Signature: #{:<28} // {}", idx, signature) }) } pub fn signature(&self) -> Option> { self.cf.signature().map(|idx| { let v = constant_pool::get_utf8(&self.cf.cp, idx).unwrap(); let v = ClassSignature::new(v.as_slice()); v.items.clone() }) } /* desc = public HelloWorld();, acc_flags = 1 desc = public static void main(java.lang.String[]);, acc_flags = 9 desc = private void private_method();, acc_flags = 2 desc = protected void protected_method();, acc_flags = 4 desc = void package_method();, acc_flags = 0 desc = public void public_method();, acc_flags = 1 */ pub fn methods( &self, with_line_num: bool, with_code: bool, flags: u16, ) -> Vec { let mut methods = Vec::with_capacity(self.cf.methods.len()); for it in self.cf.methods.iter() { if it.acc_flags.is_bridge() { continue; } if flags.compare(it.acc_flags) > 0 { continue; } let t = MethodTranslator::new(self.cf, it); methods.push(t.get(with_line_num, with_code)); } methods } pub fn fields(&self, flags: u16) -> Vec { let mut fields = Vec::with_capacity(self.cf.fields.len()); for it in self.cf.fields.iter() { let t = FieldTranslator::new(self.cf, it); if it.acc_flags.is_synthetic() { continue; } if flags.compare(it.acc_flags) > 0 { continue; } fields.push(t.get()); } fields } pub fn inner_classes(&self) -> Vec { let mut r = vec![]; match self.cf.inner_classes() { Some(inners) => { for it in inners.iter() { let inner_class_info_index = it.inner_class_info_index; let outer_class_info_index = it.outer_class_info_index; let inner_name_index = it.inner_name_index; let inner_class_access_flags = it.inner_class_access_flags; let flags = AccessFlagsTranslator::new(inner_class_access_flags); //top-level class or interface if outer_class_info_index == 0 { let inner_class_info = constant_pool::get_class_name( &self.cf.cp, inner_class_info_index as usize, ) .unwrap(); let v = format!( "#{}; //class {}", inner_class_info_index, String::from_utf8_lossy(inner_class_info.as_slice()) ); r.push(v); } else { if inner_class_access_flags.is_public() { let inner_class_info = constant_pool::get_class_name( &self.cf.cp, inner_class_info_index as usize, ) .unwrap(); let inner_class_info = String::from_utf8_lossy(inner_class_info.as_slice()); let inner_name = constant_pool::get_utf8(&self.cf.cp, inner_name_index as usize) .unwrap(); let inner_name = String::from_utf8_lossy(inner_name.as_slice()); let outer_class_info = constant_pool::get_class_name( &self.cf.cp, outer_class_info_index as usize, ) .unwrap(); let outer_class_info = String::from_utf8_lossy(outer_class_info.as_slice()); let flags = flags.class_access_flags(true); let v = format!( "{} #{}= #{} of #{}; //{}=class {} of class {}", flags, inner_name_index, inner_class_info_index, outer_class_info_index, inner_name, inner_class_info, outer_class_info ); r.push(v); } } } } None => (), } r } } ================================================ FILE: tools/javap/src/trans/code.rs ================================================ use super::instruction::{get_instructions, InstructionInfo}; use classfile::{attributes::Code, ClassFile, OpCode}; pub struct Translator<'a> { pub cf: &'a ClassFile, pub code: &'a Code, } impl<'a> Translator<'a> { pub fn get(&self) -> Vec { let codes = self.code.code.as_slice(); let infos = self.interp(); infos .iter() .map(|it| it.assemble(codes, &self.cf.cp)) .collect() } } impl<'a> Translator<'a> { fn interp(&self) -> Vec { let mut infos = vec![]; let mut instructions = get_instructions(); let codes = self.code.code.as_slice(); let codes_len = codes.len(); let mut pc = 0; let mut enable_wide = false; loop { if pc >= codes_len { break; } let instruction = instructions.get_mut(codes[pc] as usize).unwrap(); if enable_wide { instruction.set_wide(true); } let (info, new_pc) = instruction.run(codes, pc); if enable_wide { instruction.set_wide(false); enable_wide = false; } if info.op_code == OpCode::wide { enable_wide = true; } pc = new_pc; infos.push(info); } infos } } ================================================ FILE: tools/javap/src/trans/constant_pool_trans.rs ================================================ use classfile::constant_pool::{self, Type}; use classfile::{ClassFile, ConstantPoolType}; pub struct Translator<'a> { pub cf: &'a ClassFile, } impl<'a> Translator<'a> { pub fn get(&self) -> Vec { let mut pool = Vec::with_capacity(self.cf.cp.len()); for (cp_idx, it) in self.cf.cp.iter().enumerate() { let pos = format!("#{}", cp_idx); match it { ConstantPoolType::Nop => (), Type::Class { name_index } => { let index = format!("#{}", *name_index); let name = constant_pool::get_utf8(&self.cf.cp, *name_index as usize).unwrap(); let v = format!( "{:>6} = {:18} {:14} // {}", pos, "Class", index, String::from_utf8_lossy(name.as_slice()) ); pool.push(v); } Type::FieldRef { class_index, name_and_type_index, } => { let index = format!("#{}.#{}", *class_index, *name_and_type_index); let class_name = constant_pool::get_class_name(&self.cf.cp, *class_index as usize).unwrap(); let (name, desc) = constant_pool::get_name_and_type( &self.cf.cp, *name_and_type_index as usize, ); let name = name.unwrap(); let desc = desc.unwrap(); let class_name = String::from_utf8_lossy(class_name.as_slice()); let name = String::from_utf8_lossy(name.as_slice()); let desc = String::from_utf8_lossy(desc.as_slice()); let v = format!( "{:>6} = {:18} {:14} // {}.{}:{}", pos, "Fieldref", index, class_name, name, desc ); pool.push(v); } Type::MethodRef { class_index, name_and_type_index, } => { let index = format!("#{}.#{}", *class_index, *name_and_type_index); let class_name = constant_pool::get_class_name(&self.cf.cp, *class_index as usize).unwrap(); let (name, desc) = constant_pool::get_name_and_type( &self.cf.cp, *name_and_type_index as usize, ); let name = name.unwrap(); let desc = desc.unwrap(); let class_name = String::from_utf8_lossy(class_name.as_slice()); let name = String::from_utf8_lossy(name.as_slice()); let desc = String::from_utf8_lossy(desc.as_slice()); let v = if name.as_bytes() == b"" { format!( "{:>6} = {:18} {:14} // {}.\"\":{}", pos, "Methodref", index, class_name, desc ) } else { format!( "{:>6} = {:18} {:14} // {}.{}:{}", pos, "Methodref", index, class_name, name, desc ) }; pool.push(v); } Type::InterfaceMethodRef { class_index, name_and_type_index, } => { let index = format!("#{}.#{}", *class_index, *name_and_type_index); let class_name = constant_pool::get_class_name(&self.cf.cp, *class_index as usize).unwrap(); let (name, desc) = constant_pool::get_name_and_type( &self.cf.cp, *name_and_type_index as usize, ); let name = name.unwrap(); let desc = desc.unwrap(); let class_name = String::from_utf8_lossy(class_name.as_slice()); let name = String::from_utf8_lossy(name.as_slice()); let desc = String::from_utf8_lossy(desc.as_slice()); let v = format!( "{:>6} = {:18} {:14} // {}.{}:{}", pos, "InterfaceMethodref", index, class_name, name, desc ); pool.push(v); } Type::String { string_index } => { let index = format!("#{}", *string_index); let constant_val = constant_pool::get_string(&self.cf.cp, cp_idx).unwrap(); let v = format!( "{:>6} = {:18} {:14} // {}", pos, "String", index, constant_val.escape_default() ); pool.push(v); } Type::Integer { v } => { let value = i32::from_be_bytes([v[0], v[1], v[2], v[3]]); let v = format!("{:>6} = {:18} {}", pos, "Int", value); pool.push(v); } Type::Float { v } => { let value = u32::from_be_bytes([v[0], v[1], v[2], v[3]]); let value = f32::from_bits(value); let v = format!("{:>6} = {:18} {}f", pos, "Float", value); pool.push(v); } Type::Long { v } => { let value = i64::from_be_bytes([v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]]); let v = format!("{:>6} = {:18} {}l", pos, "Long", value,); pool.push(v); } Type::Double { v } => { let value = u64::from_be_bytes([v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]]); let value = f64::from_bits(value); let v = format!("{:>6} = {:18} {}d", pos, "Double", value,); pool.push(v); } Type::NameAndType { name_index, desc_index, } => { let index = format!("#{}.#{}", *name_index, *desc_index); let name = constant_pool::get_utf8(&self.cf.cp, *name_index as usize).unwrap(); let is_ctor = name.as_slice() == b""; let name = String::from_utf8_lossy(name.as_ref()); let desc = constant_pool::get_utf8(&self.cf.cp, *desc_index as usize).unwrap(); let desc = String::from_utf8_lossy(desc.as_ref()); let v = if is_ctor { format!( "{:>6} = {:18} {:14} // \"{}\":{}", pos, "NameAndType", index, name, desc ) } else { format!( "{:>6} = {:18} {:14} // {}:{}", pos, "NameAndType", index, name, desc ) }; pool.push(v); } Type::Utf8 { bytes } => { let v = format!( "{:>6} = {:18} {}", pos, "Utf8", String::from_utf8_lossy(bytes.as_slice()).escape_default() ); pool.push(v); } Type::MethodHandle { ref_kind: _, ref_index: _, } => { pool.push("todo: MethodType".to_string()); } Type::MethodType { desc_index: _ } => { pool.push("todo: MethodType".to_string()); } Type::InvokeDynamic { bootstrap_method_attr_index: _, name_and_type_index: _, } => { pool.push("todo: InvokeDynamic".to_string()); } Type::Unknown => (), } } pool } } ================================================ FILE: tools/javap/src/trans/field.rs ================================================ use crate::trans::AccessFlagsTranslator; use crate::trans::SignatureTypeTranslator; use class_parser::FieldSignature; use classfile::{ constant_pool, constant_pool::Type as ConstantPoolType, BytesRef, ClassFile, FieldInfo, }; use handlebars::Handlebars; pub struct FieldTranslation { pub desc: String, pub descriptor: String, pub signature: String, pub flags: String, pub constant: String, } pub struct Translator<'a> { cf: &'a ClassFile, field: &'a FieldInfo, } impl<'a> Translator<'a> { pub fn new(cf: &'a ClassFile, field: &'a FieldInfo) -> Self { Self { cf, field } } } impl<'a> Translator<'a> { pub fn get(&self) -> FieldTranslation { let reg = Handlebars::new(); let flags = self.access_flags(); let desc = match flags.is_empty() { true => { let data = json!({ "type": self.field_type(), "name": self.name(), }); let tp = "{{type}} {{name}};"; reg.render_template(tp, &data).unwrap() } false => { let data = json!({ "flags": flags, "type": self.field_type(), "name": self.name(), }); let tp = "{{flags}} {{type}} {{name}};"; reg.render_template(tp, &data).unwrap() } }; let descriptor = self.descriptor(); let signature = self.signature(); let flags = AccessFlagsTranslator::new(self.field.acc_flags).access_flag_inner(); let constant = self.attr_constant_value().unwrap_or("".to_string()); FieldTranslation { desc, descriptor, signature, flags, constant, } } } impl<'a> Translator<'a> { fn access_flags(&self) -> String { let flags = self.field.acc_flags; let t = AccessFlagsTranslator::new(flags); t.field_access_flags() } fn field_type(&self) -> String { let desc = constant_pool::get_utf8(&self.cf.cp, self.field.desc_index as usize).unwrap(); let signature = FieldSignature::new(desc.as_slice()); signature.field_type.into_string() } fn name(&self) -> String { let name = constant_pool::get_utf8(&self.cf.cp, self.field.name_index as usize).unwrap(); String::from_utf8_lossy(name.as_slice()).to_string() } fn descriptor(&self) -> String { let desc = constant_pool::get_utf8(&self.cf.cp, self.field.desc_index as usize).unwrap(); String::from_utf8_lossy(desc.as_slice()).to_string() } fn signature(&self) -> String { self.attr_signature().map_or("".to_string(), |(idx, v)| { format!("#{:<28} // {}", idx, String::from_utf8_lossy(v.as_slice())) }) } fn attr_signature(&self) -> Option<(usize, BytesRef)> { self.field.attrs.iter().find_map(|v| { if let classfile::attributes::Type::Signature { signature_index } = v { let signature_index = *signature_index as usize; let v = constant_pool::get_utf8(&self.cf.cp, signature_index).unwrap(); Some((signature_index, v)) } else { None } }) } fn attr_constant_value(&self) -> Option { let idx = self.field.attrs.iter().find_map(|v| { if let classfile::attributes::Type::ConstantValue { constant_value_index, } = v { Some(*constant_value_index as usize) } else { None } }); idx.map(|idx| match self.cf.cp.get(idx) { Some(ConstantPoolType::Long { v }) => { let v = i64::from_be_bytes([v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]]); format!("long {}", v) } Some(ConstantPoolType::Float { v }) => { let v = u32::from_be_bytes([v[0], v[1], v[2], v[3]]); let v = f32::from_bits(v); format!("float {}", v) } Some(ConstantPoolType::Double { v }) => { let v = u64::from_be_bytes([v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]]); let v = f64::from_bits(v); format!("double {}", v) } Some(ConstantPoolType::Integer { v }) => { let v = i32::from_be_bytes([v[0], v[1], v[2], v[3]]); format!("int {}", v) } Some(ConstantPoolType::String { string_index: _ }) => { let v = constant_pool::get_string(&self.cf.cp, idx).unwrap(); format!("String {}", v.escape_default()) } _ => format!("todo"), }) } } ================================================ FILE: tools/javap/src/trans/instruction/aaload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aaload; impl Instruction for Aaload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aaload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/aastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aastore; impl Instruction for Aastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/aconst_null.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aconst_Null; impl Instruction for Aconst_Null { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aconst_null, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/aload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aload { pub wide: bool, } impl Instruction for Aload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::aload, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/aload_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aload_0; impl Instruction for Aload_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aload_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/aload_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aload_1; impl Instruction for Aload_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aload_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/aload_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aload_2; impl Instruction for Aload_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aload_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/aload_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Aload_3; impl Instruction for Aload_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::aload_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/anewarray.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Anewarray; impl Instruction for Anewarray { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::anewarray, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/areturn.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Areturn; impl Instruction for Areturn { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::areturn, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/arraylength.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Arraylength; impl Instruction for Arraylength { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::arraylength, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/astore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Astore { pub wide: bool, } impl Instruction for Astore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::astore, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/astore_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Astore_0; impl Instruction for Astore_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::astore_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/astore_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Astore_1; impl Instruction for Astore_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::astore_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/astore_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Astore_2; impl Instruction for Astore_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::astore_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/astore_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Astore_3; impl Instruction for Astore_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::astore_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/athrow.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Athrow; impl Instruction for Athrow { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::athrow, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/baload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Baload; impl Instruction for Baload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::baload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/bastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Bastore; impl Instruction for Bastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::bastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/bipush.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Bipush; impl Instruction for Bipush { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::bipush, icp: 0, wide: false, }; (info, pc + 2) } } ================================================ FILE: tools/javap/src/trans/instruction/caload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Caload; impl Instruction for Caload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::caload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/castore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Castore; impl Instruction for Castore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::castore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/checkcast.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Checkcast; impl Instruction for Checkcast { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::checkcast, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/d2f.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct D2F; impl Instruction for D2F { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::d2f, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/d2i.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct D2I; impl Instruction for D2I { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::d2i, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/d2l.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct D2L; impl Instruction for D2L { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::d2l, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dadd.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dadd; impl Instruction for Dadd { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dadd, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/daload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Daload; impl Instruction for Daload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::daload, icp: 0, wide: false, }; (info, pc + 2) } } ================================================ FILE: tools/javap/src/trans/instruction/dastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dastore; impl Instruction for Dastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dcmpg.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dcmpg; impl Instruction for Dcmpg { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dcmpg, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dcmpl.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dcmpl; impl Instruction for Dcmpl { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dcmpl, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dconst_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dconst_0; impl Instruction for Dconst_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dconst_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dconst_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dconst_1; impl Instruction for Dconst_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dconst_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ddiv.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ddiv; impl Instruction for Ddiv { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ddiv, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dload { pub wide: bool, } impl Instruction for Dload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::dload, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/dload_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dload_0; impl Instruction for Dload_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dload_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dload_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dload_1; impl Instruction for Dload_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dload_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dload_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dload_2; impl Instruction for Dload_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dload_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dload_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dload_3; impl Instruction for Dload_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dload_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dmul.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dmul; impl Instruction for Dmul { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dmul, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dneg.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dneg; impl Instruction for Dneg { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dneg, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/drem.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Drem; impl Instruction for Drem { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::drem, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dreturn.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dreturn; impl Instruction for Dreturn { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dreturn, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dstore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dstore { pub wide: bool, } impl Instruction for Dstore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::dstore, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/dstore_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dstore_0; impl Instruction for Dstore_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dstore_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dstore_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dstore_1; impl Instruction for Dstore_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dstore_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dstore_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dstore_2; impl Instruction for Dstore_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dstore_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dstore_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dstore_3; impl Instruction for Dstore_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dstore_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dsub.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dsub; impl Instruction for Dsub { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dsub, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dup.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dup; impl Instruction for Dup { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dup, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dup2.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dup2; impl Instruction for Dup2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dup2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dup2_x1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dup2_X1; impl Instruction for Dup2_X1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dup2_x1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dup2_x2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dup2_X2; impl Instruction for Dup2_X2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dup2_x2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dup_x1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dup_X1; impl Instruction for Dup_X1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dup_x1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/dup_x2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Dup_X2; impl Instruction for Dup_X2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::dup_x2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/f2d.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct F2D; impl Instruction for F2D { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::f2d, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/f2i.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct F2I; impl Instruction for F2I { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::f2i, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/f2l.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct F2L; impl Instruction for F2L { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::f2l, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fadd.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fadd; impl Instruction for Fadd { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fadd, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/faload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Faload; impl Instruction for Faload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::faload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fastore; impl Instruction for Fastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fcmpg.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fcmpg; impl Instruction for Fcmpg { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fcmpg, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fcmpl.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fcmpl; impl Instruction for Fcmpl { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fcmpl, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fconst_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fconst_0; impl Instruction for Fconst_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fconst_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fconst_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fconst_1; impl Instruction for Fconst_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fconst_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fconst_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fconst_2; impl Instruction for Fconst_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fconst_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fdiv.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fdiv; impl Instruction for Fdiv { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fdiv, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fload { pub wide: bool, } impl Instruction for Fload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::fload, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/fload_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fload_0; impl Instruction for Fload_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fload_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fload_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fload_1; impl Instruction for Fload_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fload_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fload_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fload_2; impl Instruction for Fload_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fload_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fload_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fload_3; impl Instruction for Fload_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fload_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fmul.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fmul; impl Instruction for Fmul { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fmul, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fneg.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fneg; impl Instruction for Fneg { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fneg, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/frem.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Frem; impl Instruction for Frem { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::frem, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/freturn.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Freturn; impl Instruction for Freturn { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::freturn, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fstore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fstore { pub wide: bool, } impl Instruction for Fstore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::fstore, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/fstore_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fstore_0; impl Instruction for Fstore_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fstore_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fstore_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fstore_1; impl Instruction for Fstore_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fstore_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fstore_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fstore_2; impl Instruction for Fstore_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fstore_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fstore_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fstore_3; impl Instruction for Fstore_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fstore_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/fsub.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Fsub; impl Instruction for Fsub { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::fsub, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/getfield.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Getfield; impl Instruction for Getfield { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::getfield, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/getstatic.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Getstatic; impl Instruction for Getstatic { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::getstatic, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/goto.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Goto; impl Instruction for Goto { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::goto, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/goto_w.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Goto_W; impl Instruction for Goto_W { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::goto_w, icp: 0, wide: false, }; (info, pc + 5) } } ================================================ FILE: tools/javap/src/trans/instruction/i2b.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct I2B; impl Instruction for I2B { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::i2b, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/i2c.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct I2C; impl Instruction for I2C { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::i2c, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/i2d.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct I2D; impl Instruction for I2D { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::i2d, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/i2f.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct I2F; impl Instruction for I2F { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::i2f, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/i2l.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct I2L; impl Instruction for I2L { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::i2l, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/i2s.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct I2S; impl Instruction for I2S { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::i2s, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iadd.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iadd; impl Instruction for Iadd { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iadd, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iaload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iaload; impl Instruction for Iaload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iaload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iand.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iand; impl Instruction for Iand { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iand, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iastore; impl Instruction for Iastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_0; impl Instruction for Iconst_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_1; impl Instruction for Iconst_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_2; impl Instruction for Iconst_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_3; impl Instruction for Iconst_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_4.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_4; impl Instruction for Iconst_4 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_4, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_5.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_5; impl Instruction for Iconst_5 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_5, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iconst_m1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iconst_M1; impl Instruction for Iconst_M1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iconst_m1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/idiv.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Idiv; impl Instruction for Idiv { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::idiv, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/if_acmpeq.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Acmpeq; impl Instruction for If_Acmpeq { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_acmpeq, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_acmpne.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Acmpne; impl Instruction for If_Acmpne { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_acmpne, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_icmpeq.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Icmpeq; impl Instruction for If_Icmpeq { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_icmpeq, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_icmpge.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Icmpge; impl Instruction for If_Icmpge { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_icmpge, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_icmpgt.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Icmpgt; impl Instruction for If_Icmpgt { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_icmpgt, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_icmple.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Icmple; impl Instruction for If_Icmple { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_icmple, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_icmplt.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Icmplt; impl Instruction for If_Icmplt { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_icmplt, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/if_icmpne.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct If_Icmpne; impl Instruction for If_Icmpne { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::if_icmpne, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifeq.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifeq; impl Instruction for Ifeq { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifeq, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifge.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifge; impl Instruction for Ifge { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifge, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifgt.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifgt; impl Instruction for Ifgt { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifgt, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifle.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifle; impl Instruction for Ifle { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifle, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/iflt.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iflt; impl Instruction for Iflt { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iflt, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifne.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifne; impl Instruction for Ifne { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifne, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifnonnull.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifnonnull; impl Instruction for Ifnonnull { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifnonnull, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ifnull.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ifnull; impl Instruction for Ifnull { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ifnull, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/iinc.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iinc { pub wide: bool, } impl Instruction for Iinc { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::iinc, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 5) } else { (info, pc + 3) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/iload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iload { pub wide: bool, } impl Instruction for Iload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::iload, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/iload_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iload_0; impl Instruction for Iload_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iload_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iload_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iload_1; impl Instruction for Iload_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iload_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iload_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iload_2; impl Instruction for Iload_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iload_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iload_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iload_3; impl Instruction for Iload_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iload_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/imul.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Imul; impl Instruction for Imul { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::imul, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ineg.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ineg; impl Instruction for Ineg { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ineg, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/instanceof.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Instanceof; impl Instruction for Instanceof { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::instanceof, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/invokedynamic.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Invokedynamic; impl Instruction for Invokedynamic { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::invokedynamic, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 5) } } ================================================ FILE: tools/javap/src/trans/instruction/invokeinterface.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Invokeinterface; impl Instruction for Invokeinterface { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::invokeinterface, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 5) } } ================================================ FILE: tools/javap/src/trans/instruction/invokespecial.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Invokespecial; impl Instruction for Invokespecial { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::invokespecial, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/invokestatic.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Invokestatic; impl Instruction for Invokestatic { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::invokestatic, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/invokevirtual.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Invokevirtual; impl Instruction for Invokevirtual { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::invokevirtual, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ior.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ior; impl Instruction for Ior { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ior, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/irem.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Irem; impl Instruction for Irem { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::irem, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ireturn.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ireturn; impl Instruction for Ireturn { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ireturn, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ishl.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ishl; impl Instruction for Ishl { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ishl, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ishr.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ishr; impl Instruction for Ishr { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ishr, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/istore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Istore { pub wide: bool, } impl Instruction for Istore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::istore, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/istore_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Istore_0; impl Instruction for Istore_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::istore_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/istore_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Istore_1; impl Instruction for Istore_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::istore_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/istore_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Istore_2; impl Instruction for Istore_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::istore_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/istore_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Istore_3; impl Instruction for Istore_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::istore_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/isub.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Isub; impl Instruction for Isub { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::isub, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/iushr.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Iushr; impl Instruction for Iushr { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::iushr, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ixor.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ixor; impl Instruction for Ixor { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ixor, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/jsr.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Jsr; impl Instruction for Jsr { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::jsr, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/jsr_w.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Jsr_W; impl Instruction for Jsr_W { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::jsr_w, icp: 0, wide: false, }; (info, pc + 5) } } ================================================ FILE: tools/javap/src/trans/instruction/l2d.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct L2D; impl Instruction for L2D { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::l2d, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/l2f.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct L2F; impl Instruction for L2F { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::l2f, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/l2i.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct L2I; impl Instruction for L2I { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::l2i, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ladd.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ladd; impl Instruction for Ladd { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ladd, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/laload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Laload; impl Instruction for Laload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::laload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/land.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Land; impl Instruction for Land { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::land, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lastore; impl Instruction for Lastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lcmp.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lcmp; impl Instruction for Lcmp { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lcmp, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lconst_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lconst_0; impl Instruction for Lconst_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lconst_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lconst_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lconst_1; impl Instruction for Lconst_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lconst_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/ldc.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ldc; impl Instruction for Ldc { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ldc, icp: codes[pc + 1] as usize, wide: false, }; (info, pc + 2) } } ================================================ FILE: tools/javap/src/trans/instruction/ldc2_w.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ldc2_W; impl Instruction for Ldc2_W { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ldc2_w, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ldc_w.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ldc_W; impl Instruction for Ldc_W { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ldc_w, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ldiv.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ldiv; impl Instruction for Ldiv { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::ldiv, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lload { pub wide: bool, } impl Instruction for Lload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::lload, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/lload_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lload_0; impl Instruction for Lload_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lload_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lload_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lload_1; impl Instruction for Lload_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lload_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lload_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lload_2; impl Instruction for Lload_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lload_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lload_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lload_3; impl Instruction for Lload_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lload_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lmul.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lmul; impl Instruction for Lmul { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lmul, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lneg.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lneg; impl Instruction for Lneg { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lneg, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lookupswitch.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lookupswitch; impl Instruction for Lookupswitch { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lookupswitch, icp: 0, wide: false, }; let mut bc = pc; if bc % 4 != 0 { bc += 4 - bc % 4; } else { bc += 4; } let mut ptr = bc as usize; let default_byte = [codes[ptr], codes[ptr + 1], codes[ptr + 2], codes[ptr + 3]]; let _default_byte = u32::from_be_bytes(default_byte); let count = [ codes[ptr + 4], codes[ptr + 5], codes[ptr + 6], codes[ptr + 7], ]; let count = u32::from_be_bytes(count); ptr += 8; ptr += (8 * count) as usize; (info, ptr) } } ================================================ FILE: tools/javap/src/trans/instruction/lor.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lor; impl Instruction for Lor { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lor, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lrem.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lrem; impl Instruction for Lrem { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lrem, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lreturn.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lreturn; impl Instruction for Lreturn { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lreturn, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lshl.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lshl; impl Instruction for Lshl { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lshl, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lshr.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lshr; impl Instruction for Lshr { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lshr, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lstore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lstore { pub wide: bool, } impl Instruction for Lstore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::lstore, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/lstore_0.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lstore_0; impl Instruction for Lstore_0 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lstore_0, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lstore_1.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lstore_1; impl Instruction for Lstore_1 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lstore_1, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lstore_2.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lstore_2; impl Instruction for Lstore_2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lstore_2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lstore_3.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lstore_3; impl Instruction for Lstore_3 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lstore_3, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lsub.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lsub; impl Instruction for Lsub { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lsub, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lushr.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lushr; impl Instruction for Lushr { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lushr, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/lxor.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Lxor; impl Instruction for Lxor { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::lxor, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/mod.rs ================================================ mod aaload; mod aastore; mod aconst_null; mod aload; mod aload_0; mod aload_1; mod aload_2; mod aload_3; mod anewarray; mod areturn; mod arraylength; mod astore; mod astore_0; mod astore_1; mod astore_2; mod astore_3; mod athrow; mod baload; mod bastore; mod bipush; mod caload; mod castore; mod checkcast; mod d2f; mod d2i; mod d2l; mod dadd; mod daload; mod dastore; mod dcmpg; mod dcmpl; mod dconst_0; mod dconst_1; mod ddiv; mod dload; mod dload_0; mod dload_1; mod dload_2; mod dload_3; mod dmul; mod dneg; mod drem; mod dreturn; mod dstore; mod dstore_0; mod dstore_1; mod dstore_2; mod dstore_3; mod dsub; mod dup; mod dup2; mod dup2_x1; mod dup2_x2; mod dup_x1; mod dup_x2; mod f2d; mod f2i; mod f2l; mod fadd; mod faload; mod fastore; mod fcmpg; mod fcmpl; mod fconst_0; mod fconst_1; mod fconst_2; mod fdiv; mod fload; mod fload_0; mod fload_1; mod fload_2; mod fload_3; mod fmul; mod fneg; mod frem; mod freturn; mod fstore; mod fstore_0; mod fstore_1; mod fstore_2; mod fstore_3; mod fsub; mod getfield; mod getstatic; mod goto; mod goto_w; mod i2b; mod i2c; mod i2d; mod i2f; mod i2l; mod i2s; mod iadd; mod iaload; mod iand; mod iastore; mod iconst_0; mod iconst_1; mod iconst_2; mod iconst_3; mod iconst_4; mod iconst_5; mod iconst_m1; mod idiv; mod if_acmpeq; mod if_acmpne; mod if_icmpeq; mod if_icmpge; mod if_icmpgt; mod if_icmple; mod if_icmplt; mod if_icmpne; mod ifeq; mod ifge; mod ifgt; mod ifle; mod iflt; mod ifne; mod ifnonnull; mod ifnull; mod iinc; mod iload; mod iload_0; mod iload_1; mod iload_2; mod iload_3; mod imul; mod ineg; mod instanceof; mod invokedynamic; mod invokeinterface; mod invokespecial; mod invokestatic; mod invokevirtual; mod ior; mod irem; mod ireturn; mod ishl; mod ishr; mod istore; mod istore_0; mod istore_1; mod istore_2; mod istore_3; mod isub; mod iushr; mod ixor; mod jsr; mod jsr_w; mod l2d; mod l2f; mod l2i; mod ladd; mod laload; mod land; mod lastore; mod lcmp; mod lconst_0; mod lconst_1; mod ldc; mod ldc2_w; mod ldc_w; mod ldiv; mod lload; mod lload_0; mod lload_1; mod lload_2; mod lload_3; mod lmul; mod lneg; mod lookupswitch; mod lor; mod lrem; mod lreturn; mod lshl; mod lshr; mod lstore; mod lstore_0; mod lstore_1; mod lstore_2; mod lstore_3; mod lsub; mod lushr; mod lxor; mod monitorenter; mod monitorexit; mod multianewarray; mod new; mod newarray; mod nop; mod pop; mod pop2; mod putfield; mod putstatic; mod ret; mod return_void; mod saload; mod sastore; mod sipush; mod swap; mod tableswitch; mod wide; use aaload::Aaload; use aastore::Aastore; use aconst_null::Aconst_Null; use aload::Aload; use aload_0::Aload_0; use aload_1::Aload_1; use aload_2::Aload_2; use aload_3::Aload_3; use anewarray::Anewarray; use areturn::Areturn; use arraylength::Arraylength; use astore::Astore; use astore_0::Astore_0; use astore_1::Astore_1; use astore_2::Astore_2; use astore_3::Astore_3; use athrow::Athrow; use baload::Baload; use bastore::Bastore; use bipush::Bipush; use caload::Caload; use castore::Castore; use checkcast::Checkcast; use classfile::OpCode; use d2f::D2F; use d2i::D2I; use d2l::D2L; use dadd::Dadd; use daload::Daload; use dastore::Dastore; use dcmpg::Dcmpg; use dcmpl::Dcmpl; use dconst_0::Dconst_0; use dconst_1::Dconst_1; use ddiv::Ddiv; use dload::Dload; use dload_0::Dload_0; use dload_1::Dload_1; use dload_2::Dload_2; use dload_3::Dload_3; use dmul::Dmul; use dneg::Dneg; use drem::Drem; use dreturn::Dreturn; use dstore::Dstore; use dstore_0::Dstore_0; use dstore_1::Dstore_1; use dstore_2::Dstore_2; use dstore_3::Dstore_3; use dsub::Dsub; use dup::Dup; use dup2::Dup2; use dup2_x1::Dup2_X1; use dup2_x2::Dup2_X2; use dup_x1::Dup_X1; use dup_x2::Dup_X2; use f2d::F2D; use f2i::F2I; use f2l::F2L; use fadd::Fadd; use faload::Faload; use fastore::Fastore; use fcmpg::Fcmpg; use fcmpl::Fcmpl; use fconst_0::Fconst_0; use fconst_1::Fconst_1; use fconst_2::Fconst_2; use fdiv::Fdiv; use fload::Fload; use fload_0::Fload_0; use fload_1::Fload_1; use fload_2::Fload_2; use fload_3::Fload_3; use fmul::Fmul; use fneg::Fneg; use frem::Frem; use freturn::Freturn; use fstore::Fstore; use fstore_0::Fstore_0; use fstore_1::Fstore_1; use fstore_2::Fstore_2; use fstore_3::Fstore_3; use fsub::Fsub; use getfield::Getfield; use getstatic::Getstatic; use goto::Goto; use goto_w::Goto_W; use i2b::I2B; use i2c::I2C; use i2d::I2D; use i2f::I2F; use i2l::I2L; use i2s::I2S; use iadd::Iadd; use iaload::Iaload; use iand::Iand; use iastore::Iastore; use iconst_0::Iconst_0; use iconst_1::Iconst_1; use iconst_2::Iconst_2; use iconst_3::Iconst_3; use iconst_4::Iconst_4; use iconst_5::Iconst_5; use iconst_m1::Iconst_M1; use idiv::Idiv; use if_acmpeq::If_Acmpeq; use if_acmpne::If_Acmpne; use if_icmpeq::If_Icmpeq; use if_icmpge::If_Icmpge; use if_icmpgt::If_Icmpgt; use if_icmple::If_Icmple; use if_icmplt::If_Icmplt; use if_icmpne::If_Icmpne; use ifeq::Ifeq; use ifge::Ifge; use ifgt::Ifgt; use ifle::Ifle; use iflt::Iflt; use ifne::Ifne; use ifnonnull::Ifnonnull; use ifnull::Ifnull; use iinc::Iinc; use iload::Iload; use iload_0::Iload_0; use iload_1::Iload_1; use iload_2::Iload_2; use iload_3::Iload_3; use imul::Imul; use ineg::Ineg; use instanceof::Instanceof; use invokedynamic::Invokedynamic; use invokeinterface::Invokeinterface; use invokespecial::Invokespecial; use invokestatic::Invokestatic; use invokevirtual::Invokevirtual; use ior::Ior; use irem::Irem; use ireturn::Ireturn; use ishl::Ishl; use ishr::Ishr; use istore::Istore; use istore_0::Istore_0; use istore_1::Istore_1; use istore_2::Istore_2; use istore_3::Istore_3; use isub::Isub; use iushr::Iushr; use ixor::Ixor; use jsr::Jsr; use jsr_w::Jsr_W; use l2d::L2D; use l2f::L2F; use l2i::L2I; use ladd::Ladd; use laload::Laload; use land::Land; use lastore::Lastore; use lcmp::Lcmp; use lconst_0::Lconst_0; use lconst_1::Lconst_1; use ldc::Ldc; use ldc2_w::Ldc2_W; use ldc_w::Ldc_W; use ldiv::Ldiv; use lload::Lload; use lload_0::Lload_0; use lload_1::Lload_1; use lload_2::Lload_2; use lload_3::Lload_3; use lmul::Lmul; use lneg::Lneg; use lookupswitch::Lookupswitch; use lor::Lor; use lrem::Lrem; use lreturn::Lreturn; use lshl::Lshl; use lshr::Lshr; use lstore::Lstore; use lstore_0::Lstore_0; use lstore_1::Lstore_1; use lstore_2::Lstore_2; use lstore_3::Lstore_3; use lsub::Lsub; use lushr::Lushr; use lxor::Lxor; use monitorenter::Monitorenter; use monitorexit::Monitorexit; use multianewarray::Multianewarray; use new::New; use newarray::Newarray; use nop::Nop; use pop::Pop; use pop2::Pop2; use putfield::Putfield; use putstatic::Putstatic; use ret::Ret; use return_void::Return_Void; use saload::Saload; use sastore::Sastore; use sipush::Sipush; use swap::Swap; use tableswitch::Tableswitch; use wide::Wide; use classfile::constant_pool::Type; use classfile::{constant_pool, ConstantPool, ConstantPoolType}; pub struct InstructionInfo { pub pc: usize, pub op_code: OpCode, pub icp: usize, pub wide: bool, } impl InstructionInfo { pub fn assemble(&self, codes: &[u8], cp: &ConstantPool) -> String { let op_code: &'static str = self.op_code.into(); if self.icp != 0 { let comment = self.comment(cp); match self.op_code { OpCode::invokeinterface => { let count = codes[self.pc + 3]; let stack_info = format!("#{}, {:2}", self.icp, count); format!( "{:>4}: {:15} {:<20} // {}", self.pc, op_code, stack_info, comment ) } _ => format!( "{:>4}: {:15} #{:<20}// {}", self.pc, op_code, self.icp, comment ), } } else { match self.op_code { OpCode::aload | OpCode::iload | OpCode::fload | OpCode::lload | OpCode::dload | OpCode::istore | OpCode::fstore | OpCode::astore | OpCode::lstore | OpCode::dstore | OpCode::ret => { if self.wide { let index = construct_usize(codes, self.pc); format!("{:>4}: {:15} {}", self.pc, op_code, index) } else { let index = codes[self.pc + 1]; format!("{:>4}: {:15} {}", self.pc, op_code, index) } } OpCode::if_acmpeq | OpCode::if_acmpne | OpCode::if_icmpeq | OpCode::if_icmpne | OpCode::if_icmplt | OpCode::if_icmpge | OpCode::if_icmpgt | OpCode::if_icmple | OpCode::ifeq | OpCode::ifne | OpCode::iflt | OpCode::ifge | OpCode::ifgt | OpCode::ifle | OpCode::ifnonnull | OpCode::ifnull | OpCode::goto => { let branch = construct_i16(codes, self.pc); let target = self.pc as i32 + branch as i32; format!("{:>4}: {:15} {}", self.pc, op_code, target) } OpCode::iinc => { if self.wide { let index = construct_usize(codes, self.pc); let const_v = { let pc = self.pc; let constbyte1 = codes[pc + 3] as i16; let constbyte2 = codes[pc + 4] as i16; constbyte1 << 8 | constbyte2 }; format!("{:>4}: {:15} {}, {}", self.pc, op_code, index, const_v) } else { let index = codes[self.pc + 1]; let const_v = (codes[self.pc + 2] as i8) as i32; format!("{:>4}: {:15} {}, {}", self.pc, op_code, index, const_v) } } OpCode::sipush => { let v = construct_i16(codes, self.pc); format!("{:>4}: {:15} {}", self.pc, op_code, v) } _ => format!("{:>4}: {:15}", self.pc, op_code), } } } fn comment(&self, cp: &ConstantPool) -> String { assert_ne!(0, self.icp); match cp.get(self.icp).unwrap() { ConstantPoolType::Class { name_index } => { let class_name = constant_pool::get_utf8(cp, *name_index as usize).unwrap(); let is_ary_class = class_name.starts_with(b"["); let class_name = String::from_utf8_lossy(class_name.as_slice()).to_string(); if is_ary_class { format!("class \"{}\"", class_name) } else { format!("class {}", class_name) } } ConstantPoolType::FieldRef { class_index: _, name_and_type_index, } => { // let class_name = constant_pool::get_class_name(cp, *class_index as usize).unwrap(); // let class_name = String::from_utf8_lossy(class_name.as_slice()); let (name, desc) = constant_pool::get_name_and_type(cp, *name_and_type_index as usize); let name = name.unwrap(); let desc = desc.unwrap(); let name = String::from_utf8_lossy(name.as_slice()); let desc = String::from_utf8_lossy(desc.as_slice()); format!("Field {}:{}", name, desc) } ConstantPoolType::MethodRef { class_index, name_and_type_index, } => { let class_name = constant_pool::get_class_name(cp, *class_index as usize).unwrap(); let is_ary_class = class_name.starts_with(b"["); let class_name = String::from_utf8_lossy(class_name.as_slice()); let (name, desc) = constant_pool::get_name_and_type(cp, *name_and_type_index as usize); let name = name.unwrap(); let desc = desc.unwrap(); let is_ctor = name.as_slice() == b""; let desc = String::from_utf8_lossy(desc.as_slice()); if is_ctor { format!("Method {}.\"\":{}", class_name, desc) } else { let name = String::from_utf8_lossy(name.as_slice()); if is_ary_class { format!("Method \"{}\".{}:{}", class_name, name, desc) } else { format!("Method {}.{}:{}", class_name, name, desc) } } } ConstantPoolType::String { string_index: _ } => { let v = constant_pool::get_string(cp, self.icp).unwrap(); format!("String {}", v.escape_default()) } ConstantPoolType::Float { v } => { let v = u32::from_be_bytes([v[0], v[1], v[2], v[3]]); let v = f32::from_bits(v); format!("float {}f", v) } ConstantPoolType::Double { v } => { let v = u64::from_be_bytes([v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]]); let v = f64::from_bits(v); format!("double {}d", v) } ConstantPoolType::Long { v } => { let v = i64::from_be_bytes([v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]]); format!("long {}l", v) } Type::Nop => unreachable!(), Type::InterfaceMethodRef { class_index, name_and_type_index, } => { let class_name = constant_pool::get_class_name(cp, *class_index as usize).unwrap(); let name_and_type = constant_pool::get_name_and_type(cp, *name_and_type_index as usize); let method_name = name_and_type.0.unwrap(); let method_type = name_and_type.1.unwrap(); format!( "InterfaceMethod {}.{}:{}", String::from_utf8_lossy(class_name.as_slice()), String::from_utf8_lossy(method_name.as_slice()), String::from_utf8_lossy(method_type.as_slice()) ) } Type::Integer { v } => { let v = i32::from_be_bytes([v[0], v[1], v[2], v[3]]); format!("int {}", v) } Type::NameAndType { name_index: _, desc_index: _, } => "todo: NameAndType".to_string(), Type::Utf8 { bytes: _ } => "todo: Utf8".to_string(), Type::MethodHandle { ref_kind: _, ref_index: _, } => "todo: MethodHandle".to_string(), Type::MethodType { desc_index: _ } => "todo: MethodType".to_string(), Type::InvokeDynamic { bootstrap_method_attr_index: _, name_and_type_index: _, } => "todo: InvokeDynamic".to_string(), Type::Unknown => unreachable!(), } } } fn construct_usize(codes: &[u8], pc: usize) -> usize { let indexbyte1 = codes[pc + 1] as u16; let indexbyte2 = codes[pc + 2] as u16; (indexbyte1 << 8 | indexbyte2) as usize } fn construct_i16(codes: &[u8], pc: usize) -> i16 { let indexbyte1 = codes[pc + 1] as i16; let indexbyte2 = codes[pc + 2] as i16; indexbyte1 << 8 | indexbyte2 } pub trait Instruction { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize); fn calc_cp_index_u16(&self, codes: &[u8], pc: usize) -> usize { construct_usize(codes, pc) } fn set_wide(&mut self, _wide: bool) { unimplemented!() } } pub fn get_instructions() -> Vec> { vec![ Box::new(Nop), Box::new(Aconst_Null), Box::new(Iconst_M1), Box::new(Iconst_0), Box::new(Iconst_1), Box::new(Iconst_2), Box::new(Iconst_3), Box::new(Iconst_4), Box::new(Iconst_5), Box::new(Lconst_0), Box::new(Lconst_1), Box::new(Fconst_0), Box::new(Fconst_1), Box::new(Fconst_2), Box::new(Dconst_0), Box::new(Dconst_1), Box::new(Bipush), Box::new(Sipush), Box::new(Ldc), Box::new(Ldc_W), Box::new(Ldc2_W), Box::new(Iload { wide: false }), Box::new(Lload { wide: false }), Box::new(Fload { wide: false }), Box::new(Dload { wide: false }), Box::new(Aload { wide: false }), Box::new(Iload_0), Box::new(Iload_1), Box::new(Iload_2), Box::new(Iload_3), Box::new(Lload_0), Box::new(Lload_1), Box::new(Lload_2), Box::new(Lload_3), Box::new(Fload_0), Box::new(Fload_1), Box::new(Fload_2), Box::new(Fload_3), Box::new(Dload_0), Box::new(Dload_1), Box::new(Dload_2), Box::new(Dload_3), Box::new(Aload_0), Box::new(Aload_1), Box::new(Aload_2), Box::new(Aload_3), Box::new(Iaload), Box::new(Laload), Box::new(Faload), Box::new(Daload), Box::new(Aaload), Box::new(Baload), Box::new(Caload), Box::new(Saload), Box::new(Istore { wide: false }), Box::new(Lstore { wide: false }), Box::new(Fstore { wide: false }), Box::new(Dstore { wide: false }), Box::new(Astore { wide: false }), Box::new(Istore_0), Box::new(Istore_1), Box::new(Istore_2), Box::new(Istore_3), Box::new(Lstore_0), Box::new(Lstore_1), Box::new(Lstore_2), Box::new(Lstore_3), Box::new(Fstore_0), Box::new(Fstore_1), Box::new(Fstore_2), Box::new(Fstore_3), Box::new(Dstore_0), Box::new(Dstore_1), Box::new(Dstore_2), Box::new(Dstore_3), Box::new(Astore_0), Box::new(Astore_1), Box::new(Astore_2), Box::new(Astore_3), Box::new(Iastore), Box::new(Lastore), Box::new(Fastore), Box::new(Dastore), Box::new(Aastore), Box::new(Bastore), Box::new(Castore), Box::new(Sastore), Box::new(Pop), Box::new(Pop2), Box::new(Dup), Box::new(Dup_X1), Box::new(Dup_X2), Box::new(Dup2), Box::new(Dup2_X1), Box::new(Dup2_X2), Box::new(Swap), Box::new(Iadd), Box::new(Ladd), Box::new(Fadd), Box::new(Dadd), Box::new(Isub), Box::new(Lsub), Box::new(Fsub), Box::new(Dsub), Box::new(Imul), Box::new(Lmul), Box::new(Fmul), Box::new(Dmul), Box::new(Idiv), Box::new(Ldiv), Box::new(Fdiv), Box::new(Ddiv), Box::new(Irem), Box::new(Lrem), Box::new(Frem), Box::new(Drem), Box::new(Ineg), Box::new(Lneg), Box::new(Fneg), Box::new(Dneg), Box::new(Ishl), Box::new(Lshl), Box::new(Ishr), Box::new(Lshr), Box::new(Iushr), Box::new(Lushr), Box::new(Iand), Box::new(Land), Box::new(Ior), Box::new(Lor), Box::new(Ixor), Box::new(Lxor), Box::new(Iinc { wide: false }), Box::new(I2L), Box::new(I2F), Box::new(I2D), Box::new(L2I), Box::new(L2F), Box::new(L2D), Box::new(F2I), Box::new(F2L), Box::new(F2D), Box::new(D2I), Box::new(D2L), Box::new(D2F), Box::new(I2B), Box::new(I2C), Box::new(I2S), Box::new(Lcmp), Box::new(Fcmpl), Box::new(Fcmpg), Box::new(Dcmpl), Box::new(Dcmpg), Box::new(Ifeq), Box::new(Ifne), Box::new(Iflt), Box::new(Ifge), Box::new(Ifgt), Box::new(Ifle), Box::new(If_Icmpeq), Box::new(If_Icmpne), Box::new(If_Icmplt), Box::new(If_Icmpge), Box::new(If_Icmpgt), Box::new(If_Icmple), Box::new(If_Acmpeq), Box::new(If_Acmpne), Box::new(Goto), Box::new(Jsr), Box::new(Ret { wide: false }), Box::new(Tableswitch), Box::new(Lookupswitch), Box::new(Ireturn), Box::new(Lreturn), Box::new(Freturn), Box::new(Dreturn), Box::new(Areturn), Box::new(Return_Void), Box::new(Getstatic), Box::new(Putstatic), Box::new(Getfield), Box::new(Putfield), Box::new(Invokevirtual), Box::new(Invokespecial), Box::new(Invokestatic), Box::new(Invokeinterface), Box::new(Invokedynamic), Box::new(New), Box::new(Newarray), Box::new(Anewarray), Box::new(Arraylength), Box::new(Athrow), Box::new(Checkcast), Box::new(Instanceof), Box::new(Monitorenter), Box::new(Monitorexit), Box::new(Wide), Box::new(Multianewarray), Box::new(Ifnull), Box::new(Ifnonnull), Box::new(Goto_W), Box::new(Jsr_W), ] } ================================================ FILE: tools/javap/src/trans/instruction/monitorenter.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Monitorenter; impl Instruction for Monitorenter { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::monitorenter, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/monitorexit.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Monitorexit; impl Instruction for Monitorexit { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::monitorexit, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/multianewarray.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Multianewarray; impl Instruction for Multianewarray { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::multianewarray, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 4) } } ================================================ FILE: tools/javap/src/trans/instruction/new.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct New; impl Instruction for New { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::new, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/newarray.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Newarray; impl Instruction for Newarray { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::newarray, icp: 0, wide: false, }; (info, pc + 2) } } ================================================ FILE: tools/javap/src/trans/instruction/nop.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Nop; impl Instruction for Nop { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::nop, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/pop.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Pop; impl Instruction for Pop { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::pop, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/pop2.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Pop2; impl Instruction for Pop2 { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::pop2, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/putfield.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Putfield; impl Instruction for Putfield { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::putfield, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/putstatic.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Putstatic; impl Instruction for Putstatic { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::putstatic, icp: self.calc_cp_index_u16(codes, pc), wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/ret.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Ret { pub wide: bool, } impl Instruction for Ret { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let mut info = InstructionInfo { pc, op_code: OpCode::ret, icp: 0, wide: false, }; if self.wide { info.wide = self.wide; (info, pc + 3) } else { (info, pc + 2) } } fn set_wide(&mut self, wide: bool) { self.wide = wide; } } ================================================ FILE: tools/javap/src/trans/instruction/return_void.rs ================================================ #![allow(non_camel_case_types)] use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Return_Void; impl Instruction for Return_Void { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::return_void, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/saload.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Saload; impl Instruction for Saload { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::saload, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/sastore.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Sastore; impl Instruction for Sastore { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::sastore, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/sipush.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Sipush; impl Instruction for Sipush { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::sipush, icp: 0, wide: false, }; (info, pc + 3) } } ================================================ FILE: tools/javap/src/trans/instruction/swap.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Swap; impl Instruction for Swap { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::swap, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/instruction/tableswitch.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Tableswitch; impl Instruction for Tableswitch { fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::tableswitch, icp: 0, wide: false, }; let mut bc = pc; if bc % 4 != 0 { bc += 4 - bc % 4; } else { bc += 4; } let mut ptr = bc as usize; let default_byte = [codes[ptr], codes[ptr + 1], codes[ptr + 2], codes[ptr + 3]]; let _default_byte = i32::from_be_bytes(default_byte); let low_byte = [ codes[ptr + 4], codes[ptr + 5], codes[ptr + 6], codes[ptr + 7], ]; let low_byte = i32::from_be_bytes(low_byte); let high_byte = [ codes[ptr + 8], codes[ptr + 9], codes[ptr + 10], codes[ptr + 11], ]; let high_byte = i32::from_be_bytes(high_byte); let num = high_byte - low_byte + 1; ptr += 12; ptr += (4 * num) as usize; (info, ptr) } } ================================================ FILE: tools/javap/src/trans/instruction/wide.rs ================================================ use super::{Instruction, InstructionInfo}; use classfile::OpCode; pub struct Wide; impl Instruction for Wide { fn run(&self, _codes: &[u8], pc: usize) -> (InstructionInfo, usize) { let info = InstructionInfo { pc, op_code: OpCode::wide, icp: 0, wide: false, }; (info, pc + 1) } } ================================================ FILE: tools/javap/src/trans/method.rs ================================================ use crate::sd::CodeSerde; use crate::trans::SignatureTypeTranslator; use crate::trans::{AccessFlagHelper, AccessFlagsTranslator, CodeTranslator}; use class_parser::MethodSignature; use classfile::attributes::LocalVariable; use classfile::{ attributes::LineNumber, attributes::StackMapFrame, attributes::VerificationTypeInfo, constant_pool, BytesRef, ClassFile, MethodInfo, }; use handlebars::Handlebars; pub struct MethodTranslation { pub desc: String, pub line_num_table: Vec, pub code: CodeSerde, pub descriptor: String, pub signature: String, pub flags: String, pub throws: String, pub ex_table: Vec, pub stack_map_table: Vec, pub local_variable_table: Vec, pub local_variable_type_table: Vec, } pub struct StackMapTableTranslation { pub tag: u8, pub comment: &'static str, pub items: Vec, } pub struct Translator<'a> { cf: &'a ClassFile, method: &'a MethodInfo, } impl<'a> Translator<'a> { pub fn new(cf: &'a ClassFile, method: &'a MethodInfo) -> Self { Self { cf, method } } } impl<'a> Translator<'a> { pub fn get(&self, with_line_num: bool, with_code: bool) -> MethodTranslation { let desc = self.build_desc(); let line_num_table = if with_line_num { self.method.get_line_number_table() } else { vec![] }; let code = if with_code { self.code() } else { Default::default() }; let descriptor = self.descriptor(); let descriptor = String::from_utf8_lossy(descriptor.as_slice()).to_string(); let signature = self.attr_signature().map_or_else( || "".to_string(), |(idx, v)| { format!( "#{:<28} // {}", idx, String::from_utf8_lossy(v.as_slice()).to_string() ) }, ); let flags = AccessFlagsTranslator::new(self.method.acc_flags).access_flag_inner(); let throws = self.throws().unwrap_or("".to_string()); let ex_table = self.ex_table(); let stack_map_table = self.stack_map_table(); let local_variable_table = self.local_variable_table(); let local_variable_type_table = self.local_variable_type_table(); MethodTranslation { desc, line_num_table, code, descriptor, signature, flags, throws, ex_table, stack_map_table, local_variable_table, local_variable_type_table, } } } impl<'a> Translator<'a> { fn build_desc(&self) -> String { let name = constant_pool::get_utf8(&self.cf.cp, self.method.name_index as usize).unwrap(); let mut desc = match name.as_slice() { b"" => "static {}".to_string(), _ => { let mut reg = Handlebars::new(); reg.register_escape_fn(handlebars::no_escape); let name = match name.as_slice() { b"" => { let class_name = constant_pool::get_class_name(&self.cf.cp, self.cf.this_class as usize) .unwrap(); String::from_utf8_lossy(class_name.as_slice()).replace("/", ".") } _ => String::from_utf8_lossy(name.as_slice()).to_string(), }; let flags = self.access_flags(); match flags.is_empty() { true => { let data = json!({ "return_type": self.return_type(), "name": name, "args": self.args().join(", ") }); let tp = "{{return_type}} {{name}}({{args}})"; reg.render_template(tp, &data).unwrap() } false => { let data = json!({ "flags": self.access_flags(), "return_type": self.return_type(), "name": name, "args": self.args().join(", ") }); let tp = "{{flags}} {{return_type}} {{name}}({{args}})"; reg.render_template(tp, &data).unwrap() } } } }; match self.throws() { Some(ex) => { desc.push_str(" throws"); desc.push_str(" "); desc.push_str(ex.as_str()); desc.push_str(";") } _ => desc.push_str(";"), } desc } } impl<'a> Translator<'a> { fn access_flags(&self) -> String { let flags = self.method.acc_flags; let t = AccessFlagsTranslator::new(flags); t.method_access_flags() } fn return_type(&self) -> String { let signature = self.method_signature(); let mut retype = String::new(); /* build generics, if exists for example: TestNG, org.testng.collections.Maps (Ljava/util/Map;)Ljava/util/Map; public static java.util.Map newHashMap(java.util.Map); */ if !signature.generics.is_empty() { retype.push_str("<"); let lst: Vec = signature .generics .iter() .map(|(holder, t)| { let mut s = String::from_utf8_lossy(holder.as_slice()).to_string(); s.push_str(" extends "); let t = t.into_string(); s.push_str(&t); s }) .collect(); let s = lst.join(", "); retype.push_str(&s); retype.push_str("> "); } let s = signature.retype.into_string(); retype.push_str(&s); retype } fn args(&self) -> Vec { let signature = self.method_signature(); signature.args.iter().map(|it| it.into_string()).collect() } fn code(&self) -> CodeSerde { match self.method.get_code() { Some(code) => { let t = CodeTranslator { cf: self.cf, code: &code, }; let codes = t.get(); let args_size = if self.method.acc_flags.is_static() { self.args().len() } else { self.args().len() + 1 }; CodeSerde { max_stack: code.max_stack, max_locals: code.max_locals, args_size, codes, enable_verbose: false, } } None => Default::default(), } } fn throws(&self) -> Option { self.method.get_throws().map(|v| { let exs: Vec = v .iter() .map(|it| { let name = constant_pool::get_class_name(&self.cf.cp, *it as usize).unwrap(); String::from_utf8_lossy(name.as_slice()).replace("/", ".") }) .collect(); exs.join(", ") }) } fn ex_table(&self) -> Vec { let ext = self.method.get_ex_table(); match ext { Some(ext) => { let mut table = Vec::with_capacity(1 + ext.len()); let v = format!("{:5} {:>5} {:6} {:4}", "from", "to ", "target", "type"); table.push(v); for ex in ext.iter() { let t = if ex.is_finally() { "any".to_string() } else { let name = constant_pool::get_class_name(&self.cf.cp, ex.catch_type as usize) .unwrap(); format!("Class {}", String::from_utf8_lossy(name.as_slice())) }; let v = format!( "{:>5} {:>5} {:>5} {}", ex.start_pc, ex.end_pc, ex.handler_pc, t ); table.push(v) } table } _ => vec![], } } fn stack_map_table(&self) -> Vec { match self.method.get_stack_map_table() { Some(t) => { let mut table = Vec::with_capacity(t.len()); for it in t.iter() { match it { StackMapFrame::Append { tag, offset_delta, locals, } => { let infos = self.build_verification_type_infos(locals); let items = vec![ format!("offset_delta = {}", *offset_delta), format!("locals = [ {} ]", infos.join(", ")), ]; table.push(StackMapTableTranslation { tag: *tag, comment: "/* append */", items, }); } StackMapFrame::Same { tag, offset_delta: _, } => { table.push(StackMapTableTranslation { tag: *tag, comment: "/* same */", items: vec![], }); } StackMapFrame::SameLocals1StackItem { tag, offset_delta: _, stack, } => { let stack = stack.to_vec(); let infos = self.build_verification_type_infos(&stack); table.push(StackMapTableTranslation { tag: *tag, comment: "/* same_locals_1_stack_item */", items: vec![format!("stack = [ {} ]", infos.join(", "))], }); } StackMapFrame::SameLocals1StackItemExtended { tag, offset_delta: _, stack: _, } => { trace!("todo: StackMapFrame::SameLocals1StackItemExtended"); table.push(StackMapTableTranslation { tag: *tag, comment: "/* todo: SameLocals1StackItemExtended */", items: vec![], }); } StackMapFrame::Chop { tag, offset_delta } => { table.push(StackMapTableTranslation { tag: *tag, comment: "/* chop */", items: vec![format!("offset_delta = {}", *offset_delta)], }); } StackMapFrame::SameExtended { tag, offset_delta: _, } => { trace!("todo: StackMapFrame::SameExtended"); table.push(StackMapTableTranslation { tag: *tag, comment: "/* todo: SameExtended */", items: vec![], }); } StackMapFrame::Full { tag, offset_delta, locals, stack, } => { let locals = self.build_verification_type_infos(locals); let stack = self.build_verification_type_infos(stack); let mut items = Vec::with_capacity(3); items.push(format!("offset_delta = {}", *offset_delta)); items.push(format!("locals = [ {} ]", locals.join(", "))); items.push(format!("stack = [ {} ]", stack.join(", "))); table.push(StackMapTableTranslation { tag: *tag, comment: "/* full_frame */", items, }); } StackMapFrame::Reserved(_) => {} } } table } None => vec![], } } fn local_variable_table(&self) -> Vec { match self.method.get_local_variable_table() { Some(vars) => self.build_variable_table(&vars), None => vec![], } } fn local_variable_type_table(&self) -> Vec { match self.method.get_local_variable_type_table() { Some(vars) => self.build_variable_table(&vars), None => vec![], } } } impl<'a> Translator<'a> { fn build_verification_type_infos(&self, locals: &Vec) -> Vec { let mut infos = Vec::with_capacity(locals.len()); for it in locals.iter() { match it { VerificationTypeInfo::Float => { infos.push("float".to_string()); } VerificationTypeInfo::Top => { infos.push("top".to_string()); } VerificationTypeInfo::Integer => { infos.push("int".to_string()); } VerificationTypeInfo::Long => { infos.push("long".to_string()); } VerificationTypeInfo::Double => { infos.push("double".to_string()); } VerificationTypeInfo::Null => { infos.push("null".to_string()); } VerificationTypeInfo::UninitializedThis => { infos.push("UninitializedThis".to_string()); } VerificationTypeInfo::Object { cpool_index } => { let name = constant_pool::get_class_name(&self.cf.cp, *cpool_index as usize).unwrap(); let name = String::from_utf8_lossy(name.as_slice()); let v = if name.starts_with("[") { format!("class \"{}\"", name) } else { format!("class {}", name) }; infos.push(v); } VerificationTypeInfo::Uninitialized { offset: _ } => { infos.push("Uninitialized".to_string()); } } } infos } fn build_variable_table(&self, local_vars: &Vec) -> Vec { let mut table = Vec::with_capacity(1 + local_vars.len()); table.push(format!( "{:5} {:6} {:4} {:>5} {}", "Start", "Length", "Slot", "Name", "Signature" )); for it in local_vars.iter() { let name = constant_pool::get_utf8(&self.cf.cp, it.name_index as usize).unwrap(); let name = String::from_utf8_lossy(name.as_slice()); let signature = constant_pool::get_utf8(&self.cf.cp, it.signature_index as usize).unwrap(); let signature = String::from_utf8_lossy(signature.as_slice()); let v = format!( "{:>5} {:>6} {:>4} {:>5} {}", it.start_pc, it.length, it.index, name, signature ); table.push(v); } table } //Attribute::Signature contains signature details //such as, generics info; but constant pool only contains container info // // Attribute::Signature //()Ljava/util/List; // //constant pool //()Ljava/util/List; fn attr_signature(&self) -> Option<(usize, BytesRef)> { for it in self.method.attrs.iter() { match it { classfile::attributes::Type::Signature { signature_index } => { let signature_index = *signature_index as usize; let v = constant_pool::get_utf8(&self.cf.cp, signature_index).unwrap(); return Some((signature_index, v)); } _ => (), } } None } fn descriptor(&self) -> BytesRef { constant_pool::get_utf8(&self.cf.cp, self.method.desc_index as usize).unwrap() } fn method_signature(&self) -> MethodSignature { let desc = match self.attr_signature() { Some((_, v)) => v, None => self.descriptor(), }; MethodSignature::new(desc.as_slice()) } } ================================================ FILE: tools/javap/src/trans/mod.rs ================================================ use classfile::{ClassFile, SignatureType}; mod access_flag; mod class_file; mod code; mod constant_pool_trans; mod field; mod instruction; mod method; mod signature_type; pub use self::access_flag::AccessFlagHelper; pub use self::access_flag::Translator as AccessFlagsTranslator; pub use self::class_file::Translator as ClassFileTranslator; pub use self::code::Translator as CodeTranslator; pub use self::constant_pool_trans::Translator as ConstantPoolTranslator; pub use self::field::FieldTranslation; pub use self::field::Translator as FieldTranslator; pub use self::method::MethodTranslation; pub use self::method::Translator as MethodTranslator; pub use self::signature_type::Translator as SignatureTypeTranslator; pub fn class_source_file(cf: &ClassFile) -> String { let x = ClassFileTranslator::new(cf); x.source_file() } pub fn class_this_class(cf: &ClassFile) -> String { let x = ClassFileTranslator::new(cf); x.this_class() } pub fn class_super_class(cf: &ClassFile) -> String { let x = ClassFileTranslator::new(cf); x.super_class() } pub fn class_access_flags(cf: &ClassFile) -> String { let x = ClassFileTranslator::new(cf); x.access_flags() } pub fn class_access_flags_name(cf: &ClassFile) -> String { let x = ClassFileTranslator::new(cf); x.access_flags_name() } pub fn class_signature_raw(cf: &ClassFile) -> Option { let x = ClassFileTranslator::new(cf); x.signature_raw() } pub fn class_signature(cf: &ClassFile) -> Option> { let x = ClassFileTranslator::new(cf); x.signature() } pub fn class_fields(cf: &ClassFile, flags: u16) -> Vec { let x = ClassFileTranslator::new(cf); x.fields(flags) } pub fn class_methods( cf: &ClassFile, with_line_num: bool, with_code: bool, flags: u16, ) -> Vec { let x = ClassFileTranslator::new(cf); x.methods(with_line_num, with_code, flags) } pub fn class_parent_interfaces(cf: &ClassFile) -> Vec { let x = ClassFileTranslator::new(cf); x.parent_interfaces() } pub fn class_constant_pool(cf: &ClassFile) -> Vec { let x = ConstantPoolTranslator { cf }; x.get() } pub fn class_inner_classes(cf: &ClassFile) -> Vec { let x = ClassFileTranslator::new(cf); x.inner_classes() } ================================================ FILE: tools/javap/src/trans/signature_type.rs ================================================ use classfile::SignatureType; pub trait Translator { fn into_string(&self) -> String; } impl Translator for SignatureType { fn into_string(&self) -> String { match self { SignatureType::Int => "int".into(), SignatureType::Byte => "byte".into(), SignatureType::Char => "char".into(), SignatureType::Double => "double".into(), SignatureType::Float => "float".into(), SignatureType::Long => "long".into(), SignatureType::Object(desc, generics, prefix) => match generics { Some(generics) => { let generics: Vec = generics.iter().map(|it| it.into_string()).collect(); let generics = generics.join(", "); let mut s = to_java_style(&desc); s.push('<'); match prefix { Some(b'+') => s.push_str("? extends "), _ => (), } s.push_str(&generics); s.push('>'); s } None => to_java_style(&desc), }, SignatureType::Short => "short".into(), SignatureType::Boolean => "boolean".into(), SignatureType::Array(desc) => to_java_style(&desc), SignatureType::Void => "void".into(), } } } fn to_java_style(desc: &[u8]) -> String { //calc array dimensions let mut i = 0; while desc[i] == b'[' { i += 1; } let ary_size = i; let mut name = if desc[i] == b'L' || desc[i] == b'T' { let desc = &desc[(i + 1)..(desc.len() - 1)]; String::from_utf8_lossy(desc).replace("/", ".") } else { match desc[i] { b'B' => "byte".into(), b'C' => "char".into(), b'D' => "double".into(), b'F' => "float".into(), b'I' => "int".into(), b'J' => "long".into(), b'S' => "short".into(), b'Z' => "boolean".into(), _ => unreachable!(), } }; for _ in 0..ary_size { name.push_str("[]"); } name } #[cfg(test)] mod tests { use super::*; #[test] fn t_parse() { let tests = vec![ ("Ljava/lang/Object;", "java.lang.Object"), ("[Ljava/lang/Object;", "java.lang.Object[]"), ("[[Ljava/lang/Object;", "java.lang.Object[][]"), ("[B", "byte[]"), ("[C", "char[]"), ("[D", "double[]"), ("[F", "float[]"), ("[J", "long[]"), ("[S", "short[]"), ("[Z", "boolean[]"), ]; for it in tests.iter() { assert_eq!(to_java_style(it.0.as_bytes()), it.1); } } } ================================================ FILE: tools/javap/src/util/mod.rs ================================================ mod sys; pub use self::sys::*; pub const JAR_FILE_PREFIX: &str = "jar:file:"; ================================================ FILE: tools/javap/src/util/sys.rs ================================================ #![allow(unused)] use std::time::SystemTime; use time::OffsetDateTime; pub const FILE_SEP: &str = platform::FILE_SEP; pub const PATH_SEP: &str = platform::PATH_SEP; pub const LINE_SEP: &str = "\n"; const LAST_MODIFIED_FORMAT: &str = "%b %-d, %Y"; #[cfg(unix)] mod platform { pub const FILE_SEP: &str = "/"; pub const PATH_SEP: &str = ":"; } #[cfg(windows)] mod platform { pub const FILE_SEP: &str = "\\"; pub const PATH_SEP: &str = ";"; } pub fn to_abs_path(src: &str) -> String { let src = std::path::PathBuf::from(src); match std::fs::canonicalize(&src) { Ok(pb) => pb.to_string_lossy().to_string(), Err(_) => String::new(), } } pub fn md5_checksum(data: &[u8]) -> String { let digest = md5::compute(data); format!("{:x}", digest) } pub fn format_time1(t: SystemTime) -> String { match t.duration_since(std::time::SystemTime::UNIX_EPOCH) { Ok(t) => { let odt = OffsetDateTime::from_unix_timestamp(t.as_secs() as i64); odt.format(LAST_MODIFIED_FORMAT) } Err(_) => "".to_string(), } } pub fn format_time2(sec: i64) -> String { let odt = OffsetDateTime::from_unix_timestamp(sec); odt.format(LAST_MODIFIED_FORMAT) } ================================================ FILE: tools/javap/test/AbstractGraphicObject.java ================================================ abstract class AbstractGraphicObject { // declare fields // declare nonabstract methods abstract void draw(); } ================================================ FILE: tools/javap/test/EnumMobile.java ================================================ import java.lang.*; // enum showing Mobile prices public enum EnumMobile { Samsung(400), Nokia(250); int price; EnumMobile(int p) { price = p; } int showPrice() { return price; } } ================================================ FILE: tools/javap/test/Football.java ================================================ public interface Football extends Sports { public void homeTeamScored(int points); public void visitingTeamScored(int points); public void endOfQuarter(int quarter); } ================================================ FILE: tools/javap/test/HelloWorld.java ================================================ public final class HelloWorld { float v_float = 2.5f; double v_double = 2.0; int v_int = 100; long v_long = 20000l; int count; String name; public HelloWorld() { } public static void main(String[] args) { System.out.println("Hello, World!"); System.out.println("args: " + args); for (String s: args) { System.out.println("arg: " + s); } if (args != null) { for (int i = 0; i < args.length; i++) { System.out.println("arg[" + i + "] = " + args[i]); } } } private void private_method() { System.out.println("I'm private method"); } protected void protected_method() { System.out.println("I'm protected method"); } void package_method() { System.out.println("I'm package method"); } public void public_method() { System.out.println("I'm public method"); } } ================================================ FILE: tools/javap/test/Hockey.java ================================================ public interface Hockey extends Sports { public void homeGoalScored(); public void visitingGoalScored(); public void endOfPeriod(int period); public void overtimePeriod(int ot); } ================================================ FILE: tools/javap/test/Interface1.java ================================================ import java.io.InputStream; public interface Interface1 { double E = 2.718282; void print(byte[] b1, Object o1); InputStream get_is(); Object[][] get_ary1(); int[][] get_ary2(); } ================================================ FILE: tools/javap/test/Sports.java ================================================ public interface Sports { public void setHomeTeam(String name); public void setVisitingTeam(String name); } ================================================ FILE: tools/misc/instruction.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys # (name, 1 + encoded_value_length, bytecode value) # encoded_value to calc index in constant pool instructions = [ ("aaload", 1, 50), ("aastore", 1, 83), ("aconst_null", 1, 1), ("aload", 2, 25), ("aload_0", 1, 42), ("aload_1", 1, 43), ("aload_2", 1, 44), ("aload_3", 1, 45), ("anewarray", 3, 189), ("areturn", 1, 176), ("arraylength", 1, 190), ("astore", 2, 58), ("astore_0", 1, 75), ("astore_1", 1, 76), ("astore_2", 1, 77), ("astore_3", 1, 78), ("athrow", 1, 191), ("baload", 1, 51), ("bastore", 1, 84), ("bipush", 2, 16), ("caload", 1, 52), ("castore", 1, 85), ("checkcast", 3, 192), ("d2f", 1, 144), ("d2i", 1, 142), ("d2l", 1, 143), ("dadd", 1, 99), ("daload", 2, 49), ("dastore", 1, 82), ("dcmpg", 1, 152), ("dcmpl", 1, 151), ("dconst_0", 1, 14), ("dconst_1", 1, 15), ("ddiv", 1, 111), ("dload", 2, 24), ("dload_0", 1, 38), ("dload_1", 1, 39), ("dload_2", 1, 40), ("dload_3", 1, 41), ("dmul", 1, 107), ("dneg", 1, 119), ("drem", 1, 115), ("dreturn", 1, 175), ("dstore", 2, 57), ("dstore_0", 1, 71), ("dstore_1", 1, 72), ("dstore_2", 1, 73), ("dstore_3", 1, 74), ("dsub", 1, 103), ("dup", 1, 89), ("dup2", 1, 92), ("dup2_x1", 1, 93), ("dup2_x2", 1, 94), ("dup_x1", 1, 90), ("dup_x2", 1, 91), ("f2d", 1, 141), ("f2i", 1, 139), ("f2l", 1, 140), ("fadd", 1, 98), ("faload", 1, 48), ("fastore", 1, 81), ("fcmpg", 1, 150), ("fcmpl", 1, 149), ("fconst_0", 1, 11), ("fconst_1", 1, 12), ("fconst_2", 1, 13), ("fdiv", 1, 110), ("fload", 2, 23), ("fload_0", 1, 34), ("fload_1", 1, 35), ("fload_2", 1, 36), ("fload_3", 1, 37), ("fmul", 1, 106), ("fneg", 1, 118), ("frem", 1, 114), ("freturn", 1, 174), ("fstore", 2, 56), ("fstore_0", 1, 67), ("fstore_1", 1, 68), ("fstore_2", 1, 69), ("fstore_3", 1, 70), ("fsub", 1, 102), ("getfield", 3, 180), ("getstatic", 3, 178), ("goto", 3, 167), ("goto_w", 5, 200), ("i2b", 1, 145), ("i2c", 1, 146), ("i2d", 1, 135), ("i2f", 1, 134), ("i2l", 1, 133), ("i2s", 1, 147), ("iadd", 1, 96), ("iaload", 1, 46), ("iand", 1, 126), ("iastore", 1, 79), ("iconst_0", 1, 3), ("iconst_1", 1, 4), ("iconst_2", 1, 5), ("iconst_3", 1, 6), ("iconst_4", 1, 7), ("iconst_5", 1, 8), ("iconst_m1", 1, 2), ("idiv", 1, 108), ("if_acmpeq", 3, 165), ("if_acmpne", 3, 166), ("if_icmpeq", 3, 159), ("if_icmpge", 3, 162), ("if_icmpgt", 3, 163), ("if_icmple", 3, 164), ("if_icmplt", 3, 161), ("if_icmpne", 3, 160), ("ifeq", 3, 153), ("ifge", 3, 156), ("ifgt", 3, 157), ("ifle", 3, 158), ("iflt", 3, 155), ("ifne", 3, 154), ("ifnonnull", 3, 199), ("ifnull", 3, 198), ("iinc", 3, 132), ("iload", 2, 21), ("iload_0", 1, 26), ("iload_1", 1, 27), ("iload_2", 1, 28), ("iload_3", 1, 29), ("imul", 1, 104), ("ineg", 1, 116), ("instanceof", 3, 193), ("invokedynamic", 5, 186), ("invokeinterface", 5, 185), ("invokespecial", 3, 183), ("invokestatic", 3, 184), ("invokevirtual", 3, 182), ("ior", 1, 128), ("irem", 1, 112), ("ireturn", 1, 172), ("ishl", 1, 120), ("ishr", 1, 122), ("istore", 2, 54), ("istore_0", 1, 59), ("istore_1", 1, 60), ("istore_2", 1, 61), ("istore_3", 1, 62), ("isub", 1, 100), ("iushr", 1, 124), ("ixor", 1, 130), ("jsr", 3, 168), ("jsr_w", 5, 201), ("l2d", 1, 138), ("l2f", 1, 137), ("l2i", 1, 136), ("ladd", 1, 97), ("laload", 1, 47), ("land", 1, 127), ("lastore", 1, 80), ("lcmp", 1, 148), ("lconst_0", 1, 9), ("lconst_1", 1, 10), ("ldc", 2, 18), ("ldc2_w", 3, 20), ("ldc_w", 3, 19), ("ldiv", 1, 109), ("lload", 2, 22), ("lload_0", 1, 30), ("lload_1", 1, 31), ("lload_2", 1, 32), ("lload_3", 1, 33), ("lmul", 1, 105), ("lneg", 1, 117), ("lookupswitch", "variable-length instruction", 171), ("lor", 1, 129), ("lrem", 1, 113), ("lreturn", 1, 173), ("lshl", 1, 121), ("lshr", 1, 123), ("lstore", 2, 55), ("lstore_0", 1, 63), ("lstore_1", 1, 64), ("lstore_2", 1, 65), ("lstore_3", 1, 66), ("lsub", 1, 101), ("lushr", 1, 125), ("lxor", 1, 131), ("monitorenter", 1, 194), ("monitorexit", 1, 195), ("multianewarray", 4, 197), ("new", 3, 187), ("newarray", 2, 188), ("nop", 1, 0), ("pop", 1, 87), ("pop2", 1, 88), ("putfield", 3, 181), ("putstatic", 3, 179), ("ret", 2, 169), ("return_void", 1, 177), ("saload", 1, 53), ("sastore", 1, 86), ("sipush", 3, 17), ("swap", 1, 95), ("tableswitch", "variable-length instruction", 170), ("wide", 1, 196), ] def create_get_instructions(ary): ary = sorted(ary, key=lambda it: it[2]) with open("mod.get_instructions", "w") as f: f.write("pub fn get_instructions() -> Vec> {\n") f.write("\tvec![\n") for it in ary: name = it[0] f.write("\t\tBox::new(" + name.title() + "),\n") f.write("\t]\n") f.write("}\n") def create_uses(ary): with open("mod.uses", "w") as f: for it in ary: name = it[0] f.write("mod " + name + ";\n") f.write("\n") for it in ary: name = it[0] f.write("use " + name + "::" + name.title() + ";\n") def create_mod(instruction): (name, step, _) = instruction with open(name + ".rs", "w") as f: f.write("#![allow(non_camel_case_types)]\n") f.write("use classfile::OpCode;\n") f.write("use super::{Instruction, InstructionInfo};\n") f.write("\n") f.write("pub struct " + name.title() + ";\n") f.write("\n") f.write("impl Instruction for " + name.title() + " {\n") f.write(" fn run(&self, codes: &[u8], pc: usize) -> (InstructionInfo, usize) {\n") f.write(" let info = InstructionInfo {\n") f.write(" op_code: OpCode::" + name + ",\n") if name in ["instanceof", "multianewarray", "invokevirtual", "anewarray", "checkcast", "putfield", "getfield", "getstatic", "invokespecial", "ldc2_w", "invokeinterface", "new", "invokestatic", "ldc_w", "putstatic", "invokedynamic"]: f.write(" icp: self.calc_cp_index_u16(codes, pc)\n") else: f.write(" icp: 0\n") f.write(" };\n") f.write("\n") if name in ["lookupswitch", "tableswitch", "wide"]: f.write("\tunimplemented!(\"" + str(step) + "\")") else: f.write(" (info, pc + " + str(step) + ")\n") f.write(" }\n") f.write("}") if __name__ == '__main__': # create_get_instructions(instructions) # create_uses(instructions) for it in instructions: create_mod(it) ================================================ FILE: tools/misc/native.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys if __name__ == '__main__': desc = sys.argv[1] [package, name, signature] = desc.split(":") mod = package.replace("/", "_") print(mod) print() print("mod " + mod + ";") print() print("(\"" + package + "\", " + mod + "::get_native_methods()),") print() print("#![allow(non_snake_case)]") print() print("use crate::native::{new_fn, JNIEnv, JNINativeMethod, JNIResult};") print("use crate::oop::{self, Oop};") print() print("pub fn get_native_methods() -> Vec {") print("\tvec![") print() print("\t]") print("}") print() print("new_fn(\"" + name + "\", " + "\"" + signature + "\", " + "Box::new(jvm_" + name + ")),") print() print("fn jvm_" + name + "(_env: JNIEnv, _args: &Vec) -> JNIResult {") print("\tOk(None)") print("}")