diff --git a/Cargo.lock b/Cargo.lock index 48a89ef..ba27d60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aho-corasick" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" -dependencies = [ - "memchr", -] - [[package]] name = "anyhow" version = "1.0.75" @@ -18,44 +9,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] -name = "classfile_reader" +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "java_rs" version = "0.1.0" dependencies = [ "anyhow", - "regex", + "zip", ] [[package]] -name = "memchr" -version = "2.6.3" +name = "zip" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "regex" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "byteorder", + "crc32fast", + "crossbeam-utils", ] - -[[package]] -name = "regex-automata" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" diff --git a/Cargo.toml b/Cargo.toml index 33130d1..c585e90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,8 @@ [package] -name = "classfile_reader" +name = "java_rs" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -regex = "1.9.5" -anyhow = { version = "1.0", features = [] } \ No newline at end of file +anyhow = { version = "1.0", features = ["default"] } +zip = { version = "0.6", default-features = false } \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8b400c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/jmods/java.base.jmod b/jmods/java.base.jmod new file mode 100644 index 0000000..528778a Binary files /dev/null and b/jmods/java.base.jmod differ diff --git a/src/class.rs b/src/class.rs index e318983..72b2ba3 100644 --- a/src/class.rs +++ b/src/class.rs @@ -1,10 +1,10 @@ +use crate::classloader::CpEntry; +use crate::heap::Object; +use anyhow::{anyhow, Error}; use std::collections::HashMap; use std::fmt; use std::rc::Rc; use std::sync::Arc; -use anyhow::{anyhow, Error}; -use crate::classloader::CpEntry; -use crate::heap::Object; use crate::io::read_u16; @@ -29,7 +29,9 @@ impl Class { } pub fn get_method(&self, name: &str) -> Result<&Method, Error> { - self.methods.get(name).ok_or(anyhow!("Method {} not found", name)) + self.methods + .get(name) + .ok_or(anyhow!("Method {} not found", name)) } } unsafe impl Send for Class {} @@ -44,18 +46,29 @@ pub struct Method { impl fmt::Debug for Method { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", - self.access_flags, self.name_index, self.descriptor_index, self.attributes) + write!( + f, + "Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", + self.access_flags, self.name_index, self.descriptor_index, self.attributes + ) } } impl Method { - pub fn new(constant_pool: Rc>, - access_flags: u16, - name_index: u16, - descriptor_index: u16, - attributes: HashMap, ) -> Self { - Method { constant_pool, access_flags, name_index, descriptor_index, attributes } + pub fn new( + constant_pool: Rc>, + access_flags: u16, + name_index: u16, + descriptor_index: u16, + attributes: HashMap, + ) -> Self { + Method { + constant_pool, + access_flags, + name_index, + descriptor_index, + attributes, + } } pub fn name(&self) -> String { @@ -67,7 +80,6 @@ impl Method { full_name.push_str(s); } - full_name } } @@ -82,18 +94,29 @@ pub struct Field { impl fmt::Debug for Field { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", - self.access_flags, self.name_index, self.descriptor_index, self.attributes) + write!( + f, + "Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", + self.access_flags, self.name_index, self.descriptor_index, self.attributes + ) } } impl Field { - pub fn new(constant_pool: Rc>, - access_flags: u16, - name_index: u16, - descriptor_index: u16, - attributes: HashMap, ) -> Self { - Field { constant_pool, access_flags, name_index, descriptor_index, attributes } + pub fn new( + constant_pool: Rc>, + access_flags: u16, + name_index: u16, + descriptor_index: u16, + attributes: HashMap, + ) -> Self { + Field { + constant_pool, + access_flags, + name_index, + descriptor_index, + attributes, + } } pub fn name(&self) -> String { @@ -128,17 +151,19 @@ const MODIFIERS: [(u16, &str); 12] = [ (0x0100, "native "), (0x0200, "interface "), (0x0400, "interface "), - (0x0800, "strict ")]; + (0x0800, "strict "), +]; pub fn get_modifier(modifier: u16) -> String { let mut output = String::new(); for m in MODIFIERS { - if modifier & m.0 == m.0 { output.push_str(m.1) } + if modifier & m.0 == m.0 { + output.push_str(m.1) + } } output } - #[derive(Debug)] pub enum AttributeType { ConstantValue(u16), @@ -202,11 +227,20 @@ pub struct MethodCode { } impl MethodCode { - pub(crate) fn new(_max_stack: u16, _max_locals: u16, - code: Vec, - _exception_table: Vec, - _code_attributes: HashMap) -> Self { - Self { _max_stack, _max_locals, opcodes: code, _exception_table, _code_attributes } + pub(crate) fn new( + _max_stack: u16, + _max_locals: u16, + code: Vec, + _exception_table: Vec, + _code_attributes: HashMap, + ) -> Self { + Self { + _max_stack, + _max_locals, + opcodes: code, + _exception_table, + _code_attributes, + } } } @@ -225,4 +259,4 @@ pub enum Value { } unsafe impl Send for Value {} -unsafe impl Sync for Value {} \ No newline at end of file +unsafe impl Sync for Value {} diff --git a/src/classloader.rs b/src/classloader.rs index ff44671..97c3c5d 100644 --- a/src/classloader.rs +++ b/src/classloader.rs @@ -1,8 +1,8 @@ +use crate::class::{AttributeType, Class, Exception, Field, Method, MethodCode}; +use crate::io::{read_f32, read_f64, read_i32, read_i64, read_u16, read_u32}; +use anyhow::Error; use std::collections::HashMap; use std::rc::Rc; -use anyhow::Error; -use crate::io::{read_f32, read_f64, read_i32, read_i64, read_u16, read_u32}; -use crate::class::{AttributeType, Class, MethodCode, Exception, Field, Method}; pub fn load_class(bytecode: Vec) -> Result { check_magic(&bytecode); @@ -10,11 +10,15 @@ pub fn load_class(bytecode: Vec) -> Result { let constant_pool_count = read_u16(&bytecode, 8); // println!("cp count: {}", constant_pool_count); let mut index = 10; - let mut constant_pool: HashMap = HashMap::with_capacity(constant_pool_count as usize); + let mut constant_pool: HashMap = + HashMap::with_capacity(constant_pool_count as usize); let mut cp_index = 1; while cp_index < constant_pool_count { // println!("cp#{}", cp_index); - constant_pool.insert(cp_index, read_constant_pool_entry(&mut cp_index, &mut index, &bytecode)); + constant_pool.insert( + cp_index, + read_constant_pool_entry(&mut cp_index, &mut index, &bytecode), + ); cp_index += 1; } @@ -154,12 +158,15 @@ fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u // 18 InvokeDynamic, // 19 Module, // 20 Package, - - _ => panic!("cp entry type not recognized") + _ => panic!("cp entry type not recognized"), } } -fn read_field(constant_pool: Rc>, index: &mut usize, bytecode: &[u8]) -> Field { +fn read_field( + constant_pool: Rc>, + index: &mut usize, + bytecode: &[u8], +) -> Field { let access_flags = read_u16(bytecode, *index); let name_index = read_u16(bytecode, *index + 2); let descriptor_index = read_u16(bytecode, *index + 4); @@ -182,7 +189,11 @@ fn read_field(constant_pool: Rc>, index: &mut usize, bytec ) } -fn read_method(constant_pool: Rc>, index: &mut usize, bytecode: &[u8]) -> Method { +fn read_method( + constant_pool: Rc>, + index: &mut usize, + bytecode: &[u8], +) -> Method { let access_flags = read_u16(bytecode, *index); let name_index = read_u16(bytecode, *index + 2); let descriptor_index = read_u16(bytecode, *index + 4); @@ -205,7 +216,11 @@ fn read_method(constant_pool: Rc>, index: &mut usize, byte ) } -fn read_attribute(constant_pool: Rc>, bytecode: &[u8], index: &mut usize) -> Option<(String, AttributeType)> { +fn read_attribute( + constant_pool: Rc>, + bytecode: &[u8], + index: &mut usize, +) -> Option<(String, AttributeType)> { let attribute_name_index = read_u16(bytecode, *index); *index += 2; let attribute_length = read_u32(bytecode, *index) as usize; @@ -213,13 +228,15 @@ fn read_attribute(constant_pool: Rc>, bytecode: &[u8], ind let info: Vec = Vec::from(&bytecode[*index..*index + attribute_length]); *index += attribute_length; - if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() { // println!("Att [{}]", s); return match s.as_str() { "ConstantValue" => { assert_eq!(info.len(), 2); - Some(("ConstantValue".into(), AttributeType::ConstantValue(read_u16(&info, 0)))) + Some(( + "ConstantValue".into(), + AttributeType::ConstantValue(read_u16(&info, 0)), + )) } "Code" => { let max_stack = read_u16(&info, 0); @@ -238,15 +255,25 @@ fn read_attribute(constant_pool: Rc>, bytecode: &[u8], ind code_index += 2; let mut code_attributes = HashMap::new(); for _ in 0..attribute_count { - if let Some(att) = read_attribute(constant_pool.clone(), &info, &mut code_index) { + if let Some(att) = read_attribute(constant_pool.clone(), &info, &mut code_index) + { code_attributes.insert(att.0, att.1); } } - Some(("Code".into(), AttributeType::Code(MethodCode::new(max_stack, max_locals, code, exception_table, code_attributes)))) + Some(( + "Code".into(), + AttributeType::Code(MethodCode::new( + max_stack, + max_locals, + code, + exception_table, + code_attributes, + )), + )) } "SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)), "LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)), - _ => None + _ => None, }; } None @@ -266,4 +293,3 @@ pub enum CpEntry { InterfaceMethodref(u16, u16), NameAndType(u16, u16), } - diff --git a/src/heap.rs b/src/heap.rs index 8223e68..ddba19d 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -1,6 +1,6 @@ +use crate::class::{Class, Value}; use std::collections::HashMap; use std::sync::Arc; -use crate::class::{Class, Value}; #[derive(Debug)] pub struct Object { @@ -15,10 +15,7 @@ unsafe impl Sync for Object {} impl Object { pub fn new(_class: Arc, data: HashMap>) -> Self { - Self { - _class, - data, - } + Self { _class, data } } } @@ -28,12 +25,10 @@ pub(crate) struct Heap { impl Heap { pub fn new() -> Self { - Self { - objects: vec![] - } + Self { objects: vec![] } } pub(crate) fn new_object(&mut self, object: Arc) { self.objects.push(object); } -} \ No newline at end of file +} diff --git a/src/io.rs b/src/io.rs index c8dd8da..8079514 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,33 +1,61 @@ +use anyhow::{anyhow, Error}; use std::fs::{self, File}; use std::io::Read; -use anyhow::{anyhow, Error}; pub(crate) fn read_u8(data: &[u8], pos: usize) -> u8 { - u8::from_be_bytes(data[pos..pos + 1].try_into().expect("slice with incorrect length")) + u8::from_be_bytes( + data[pos..pos + 1] + .try_into() + .expect("slice with incorrect length"), + ) } pub(crate) fn read_u16(data: &[u8], pos: usize) -> u16 { - u16::from_be_bytes(data[pos..pos + 2].try_into().expect("slice with incorrect length")) + u16::from_be_bytes( + data[pos..pos + 2] + .try_into() + .expect("slice with incorrect length"), + ) } pub(crate) fn read_i32(data: &[u8], pos: usize) -> i32 { - i32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length")) + i32::from_be_bytes( + data[pos..pos + 4] + .try_into() + .expect("slice with incorrect length"), + ) } pub(crate) fn read_u32(data: &[u8], pos: usize) -> u32 { - u32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length")) + u32::from_be_bytes( + data[pos..pos + 4] + .try_into() + .expect("slice with incorrect length"), + ) } pub(crate) fn read_f32(data: &[u8], pos: usize) -> f32 { - f32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length")) + f32::from_be_bytes( + data[pos..pos + 4] + .try_into() + .expect("slice with incorrect length"), + ) } pub(crate) fn read_i64(data: &[u8], pos: usize) -> i64 { - i64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length")) + i64::from_be_bytes( + data[pos..pos + 8] + .try_into() + .expect("slice with incorrect length"), + ) } pub(crate) fn read_f64(data: &[u8], pos: usize) -> f64 { - f64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length")) + f64::from_be_bytes( + data[pos..pos + 8] + .try_into() + .expect("slice with incorrect length"), + ) } pub fn find_class(classpath: &Vec, class_name: &str) -> Result { @@ -50,4 +78,4 @@ pub fn read_class_file(name: String) -> Result, Error> { let mut buffer = vec![0; metadata.len() as usize]; let _ = f.read(&mut buffer)?; Ok(buffer) -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 9c93bc1..4611d8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ pub mod class; +pub mod classloader; +mod heap; pub mod io; pub mod opcodes; pub mod vm; -mod heap; -pub mod classloader; - diff --git a/src/main.rs b/src/main.rs index 9a67592..7848eb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,9 @@ +use java_rs::vm::Vm; use std::io::Error; -use classfile_reader::vm::Vm; fn main() -> Result<(), Error> { let mut vm = Vm::new("tests"); - vm.execute("Main","public static main([Ljava/lang/String;)V", None).unwrap(); + vm.execute("Main", "public static main([Ljava/lang/String;)V", None) + .unwrap(); Ok(()) } - - diff --git a/src/opcodes.rs b/src/opcodes.rs index 84345d4..92dd499 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -9,22 +9,22 @@ pub const BIPUSH: &u8 = &16; // (0x10) Push byte pub const LDC: &u8 = &18; // (0x12) Push item from run-time pub constant pool pub const LDC_W: &u8 = &19; // (0x13) Push item from run-time constant pool (wide index) pub const LDC2_W: &u8 = &20; // (0x14) Push long or double from run-time constant pool (wide index) -// pub const fload:u8 = 23; // (0x17) Load float from local variable -// pub const dload:u8 = 24; // (0x18) load double from local variable -// pub const aload:u8 = 25; //0x19 -// -// pub const fload_0:u8 = 34; // (0x22) Load float 0 from local variable -// pub const fload_1:u8 = 35; // (0x23) Load float 1 from local variable -// pub const fload_2:u8 = 36; // (0x24) Load float 2 from local variable -// pub const fload_3:u8 = 37; // (0x25) Load float 3 from local variable -// pub const dload_0:u8 = 38; // (0x26) Load double 0 from local variable -// pub const dload_1:u8 = 39; // (0x27) Load double 1 from local variable -// pub const dload_2:u8 = 40; // (0x28) Load double 2 from local variable -// pub const dload_3:u8 = 41; // (0x29) Load double 3 from local variable -pub const ALOAD_0:&u8 = &42;// (0x2a) -// pub const aload_1:u8 = 43;// (0x2a) -// pub const aload_2:u8 = 44;// (0x2b) -// pub const aload_3:u8 = 45;// (0x2c) + // pub const fload:u8 = 23; // (0x17) Load float from local variable + // pub const dload:u8 = 24; // (0x18) load double from local variable + // pub const aload:u8 = 25; //0x19 + // + // pub const fload_0:u8 = 34; // (0x22) Load float 0 from local variable + // pub const fload_1:u8 = 35; // (0x23) Load float 1 from local variable + // pub const fload_2:u8 = 36; // (0x24) Load float 2 from local variable + // pub const fload_3:u8 = 37; // (0x25) Load float 3 from local variable + // pub const dload_0:u8 = 38; // (0x26) Load double 0 from local variable + // pub const dload_1:u8 = 39; // (0x27) Load double 1 from local variable + // pub const dload_2:u8 = 40; // (0x28) Load double 2 from local variable + // pub const dload_3:u8 = 41; // (0x29) Load double 3 from local variable +pub const ALOAD_0: &u8 = &42; // (0x2a) + // pub const aload_1:u8 = 43;// (0x2a) + // pub const aload_2:u8 = 44;// (0x2b) + // pub const aload_3:u8 = 45;// (0x2c) // pub const faload: u8 = 48; // (0x30) Load float from array // pub const daload:u8 = 49; // (0x31) load double from array @@ -51,50 +51,51 @@ pub const ALOAD_0:&u8 = &42;// (0x2a) // pub const bastore:u8 = 84; // (0x54) // // pub const castore:u8 = 85; // (0x55) -// pub const dup:u8 = 89; // (0x59) duplicate the top operand stack value -// pub const dup_x1: u8 = 90; // (0x5a) Duplicate the top operand stack value and insert two values down -// pub const dup_x2: u8 = 91; // (0x5b) Duplicate the top operand stack value and insert two or three values down -// pub const dup2: u8 = 92; // (0x5c) Duplicate the top one or two operand stack values -// pub const dup2_x1: u8 = 93; //(0x5d) Duplicate the top one or two operand stack values and insert two or three values down -// pub const dup2_x2:u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down -// pub const fadd: u8 = 98; // (0x62) Add float -// pub const dadd: u8 = 99; // (0x63) add double -// -// pub const dsub:u8 = 103; // (0x67) subtract double -// pub const fmul: u8 = 106; // (0x6a) Multiply float -// pub const dmul: u8 = 107; // (0x6b) Multiply double -// -// pub const fdiv: u8 = 110; // (0x6e) Divide float -// pub const ddiv:u8 = 111; // (0x6f) divide double -// pub const frem: u8 = 114; // (0x72) Remainder float -// pub const drem: u8 = 115; // (0x73) remainder double -// pub const fneg: u8 = 118; // (0x76) Negate float -// pub const dneg: u8 = 119; // (0x77) Negate double -// pub const f2i: u8 = 139; // (0x8b) Convert float to int -// pub const f2l: u8 = 140; // (0x8c) Convert float to long -// pub const f2d: u8 = 141; // (0x8d) Convert float to double -// pub const d2i:u8 = 142; // (0x8e) double to int -// pub const d2l:u8 = 143; // (0x8f) double to long -// pub const d2f: u8 = 144; // (0x90) double to float -// pub const fcmpl:u8 = 149; // (0x95) Compare float (less than) -// pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than) -// pub const dcmpl:u8 = 151; // (0x97) compare double (less than) -// pub const dcmpg:u8 = 152; // (0x98) compare double (greater than) -// +pub const DUP: &u8 = &89; // (0x59) duplicate the top operand stack value + // pub const dup_x1: u8 = 90; // (0x5a) Duplicate the top operand stack value and insert two values down + // pub const dup_x2: u8 = 91; // (0x5b) Duplicate the top operand stack value and insert two or three values down + // pub const dup2: u8 = 92; // (0x5c) Duplicate the top one or two operand stack values + // pub const dup2_x1: u8 = 93; //(0x5d) Duplicate the top one or two operand stack values and insert two or three values down + // pub const dup2_x2:u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down + // pub const fadd: u8 = 98; // (0x62) Add float + // pub const dadd: u8 = 99; // (0x63) add double + // + // pub const dsub:u8 = 103; // (0x67) subtract double + // pub const fmul: u8 = 106; // (0x6a) Multiply float + // pub const dmul: u8 = 107; // (0x6b) Multiply double + // + // pub const fdiv: u8 = 110; // (0x6e) Divide float + // pub const ddiv:u8 = 111; // (0x6f) divide double + // pub const frem: u8 = 114; // (0x72) Remainder float + // pub const drem: u8 = 115; // (0x73) remainder double + // pub const fneg: u8 = 118; // (0x76) Negate float + // pub const dneg: u8 = 119; // (0x77) Negate double + // pub const f2i: u8 = 139; // (0x8b) Convert float to int + // pub const f2l: u8 = 140; // (0x8c) Convert float to long + // pub const f2d: u8 = 141; // (0x8d) Convert float to double + // pub const d2i:u8 = 142; // (0x8e) double to int + // pub const d2l:u8 = 143; // (0x8f) double to long + // pub const d2f: u8 = 144; // (0x90) double to float + // pub const fcmpl:u8 = 149; // (0x95) Compare float (less than) + // pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than) + // pub const dcmpl:u8 = 151; // (0x97) compare double (less than) + // pub const dcmpg:u8 = 152; // (0x98) compare double (greater than) + // pub const IRETURN: &u8 = &172; // (0xac) ireturn pub const FRETURN: &u8 = &174; // (0xae) Return float from method pub const DRETURN: &u8 = &175; // (0xaf) Return double from method -// pub const areturn: u8 = 176; //(0xb0) return reference -// pub const return_v: u8 = 177; // (0xb1) Return void from method (actually 'return' but that's a keyword) + // pub const areturn: u8 = 176; //(0xb0) return reference + // pub const return_v: u8 = 177; // (0xb1) Return void from method (actually 'return' but that's a keyword) + // pub const getstatic: u8 = 178; // (0xb2) Get static field from class pub const GETFIELD: &u8 = &180; // (0xb4) Fetch field from object3 pub const NEW: &u8 = &187; // (0xbb) Create new object -// pub const invokevirtual: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class -// -// pub const getstatic: u8 = 178; // (0xb2) Get static field from class -// pub const anewarray: u8 = 189; // (0xbd) -// -// pub const arraylength: u8 = 190; // (0xbe) -// -// pub const athrow: u8 = 191; // (0xbf) -// -// pub const checkcast: u8 = 192; // (0xc0) + // pub const invokevirtual: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class + // +pub const INVOKESPECIAL: &u8 = &183; // (0xb7) // nvoke instance method; direct invocation of instance initialization methods and methods of the current class and its supertypes + // pub const anewarray: u8 = 189; // (0xbd) + // + // pub const arraylength: u8 = 190; // (0xbe) + // + // pub const athrow: u8 = 191; // (0xbf) + // + // pub const checkcast: u8 = 192; // (0xc0) diff --git a/src/vm.rs b/src/vm.rs index 572a245..61b30ba 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -4,10 +4,10 @@ use std::sync::Arc; use anyhow::{anyhow, Error}; use crate::class::{AttributeType, Class, Value}; -use crate::classloader::{CpEntry, load_class}; +use crate::classloader::{load_class, CpEntry}; use crate::heap::{Heap, Object}; use crate::io::*; -use crate::opcodes; +use crate::opcodes::*; struct StackFrame { data: Vec>, @@ -15,9 +15,7 @@ struct StackFrame { impl StackFrame { fn new() -> Self { - Self { - data: vec![] - } + Self { data: vec![] } } fn push(&mut self, val: Arc) { @@ -57,7 +55,6 @@ impl Vm { Ok(entry.clone()) } - pub fn new_instance(&self, class: Arc) -> Object { let mut data = HashMap::new(); for f in &class.fields { @@ -70,14 +67,19 @@ impl Vm { "F" => Value::F32(0.0), "D" => Value::F64(0.0), "L" => Value::Null, - _ => Value::Void + _ => Value::Void, }; data.insert(f.name_index, Arc::new(value)); } Object::new(class.clone(), data) } - pub fn execute(&mut self, class_name: &str, method_name: &str, instance: Option>) -> Result, Error> { + pub fn execute( + &mut self, + class_name: &str, + method_name: &str, + instance: Option>, + ) -> Result, Error> { let class = self.get_class(class_name)?; let method = class.get_method(method_name)?; if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() { @@ -86,14 +88,14 @@ impl Vm { while pc < code.opcodes.len() { let opcode = &code.opcodes[pc]; pc += 1; - println!("{}", opcode); + println!("opcode {}", opcode); match opcode { - opcodes::BIPUSH => { + BIPUSH => { let c = code.opcodes[pc] as i32; stack.push(Arc::new(Value::I32(c))); pc += 1; } - opcodes::LDC => { + LDC => { let cp_index = read_u8(&code.opcodes, pc) as u16; match method.constant_pool.get(&cp_index).unwrap() { CpEntry::Integer(i) => { @@ -106,7 +108,7 @@ impl Vm { } pc += 1; } - opcodes::LDC_W => { + LDC_W => { let cp_index = read_u16(&code.opcodes, pc); match method.constant_pool.get(&cp_index).unwrap() { CpEntry::Integer(i) => { @@ -115,11 +117,13 @@ impl Vm { CpEntry::Float(f) => { stack.push(Arc::new(Value::F32(*f))); } - _ => { panic!("unexpected") } + _ => { + panic!("unexpected") + } } pc += 2; } - opcodes::LDC2_W => { + LDC2_W => { let cp_index = read_u16(&code.opcodes, pc); match method.constant_pool.get(&cp_index).unwrap() { CpEntry::Double(d) => { @@ -128,33 +132,46 @@ impl Vm { CpEntry::Long(l) => { stack.push(Arc::new(Value::I64(*l))); } - _ => { panic!("unexpected") } + _ => { + panic!("unexpected") + } } pc += 2; } - opcodes::ALOAD_0 => { - match instance.clone() { - Some(r) => { - stack.push(Arc::new(Value::Ref(r))); - } - None => { panic!("static context") } + ALOAD_0 => match instance.clone() { + Some(r) => { + stack.push(Arc::new(Value::Ref(r))); } + None => { + panic!("static context") + } + }, + DUP => { + println!("DUP"); + let value = stack.pop().expect("Stack empty"); + stack.push(value.clone()); + stack.push(value); } - opcodes::IRETURN => { + IRETURN => { return stack.pop(); } - opcodes::DRETURN => { + DRETURN => { return stack.pop(); } - opcodes::FRETURN => { + FRETURN => { return stack.pop(); } - opcodes::GETFIELD => { + GETFIELD => { let cp_index = read_u16(&code.opcodes, pc); - if let CpEntry::Fieldref(_class_index, name_and_type_index) = method.constant_pool.get(&cp_index).unwrap() { - if let Value::Ref(inst) = &*stack.pop()? { //TODO smell? - if let CpEntry::NameAndType(name, _) = method.constant_pool.get(name_and_type_index).unwrap() { + if let CpEntry::Fieldref(_class_index, name_and_type_index) = + method.constant_pool.get(&cp_index).unwrap() + { + if let Value::Ref(inst) = &*stack.pop()? { + //TODO smell? + if let CpEntry::NameAndType(name, _) = + method.constant_pool.get(name_and_type_index).unwrap() + { let value = inst.data.get(name).unwrap(); // println!("{:?}", value); stack.push(value.clone()); @@ -163,22 +180,37 @@ impl Vm { } pc += 2; } - opcodes::NEW => { + INVOKESPECIAL => { + let ref_index = read_u16(&code.opcodes, pc); + if let CpEntry::MethodRef(_class_index, name_and_type_index) = + method.constant_pool.get(&ref_index).unwrap() + {} + pc += 2; + } + NEW => { + println!("new"); let cp_index = read_u16(&code.opcodes, pc); - if let CpEntry::ClassRef(class_name_index) = method.constant_pool.get(&cp_index).unwrap() { - if let CpEntry::Utf8(_) = method.constant_pool.get(class_name_index).unwrap() { + if let CpEntry::ClassRef(class_name_index) = + method.constant_pool.get(&cp_index).unwrap() + { + if let CpEntry::Utf8(_) = + method.constant_pool.get(class_name_index).unwrap() + { let class = self.get_class(class_name)?; let object = Arc::new(self.new_instance(class)); stack.push(Arc::new(Value::Ref(object.clone()))); self.heap.new_object(object); } } + pc += 2; } //TODO implement all opcodes - _ => { panic!("opcode not implemented") } + _ => { + panic!("opcode not implemented") + } } } } Err(anyhow!("should not happen")) } -} \ No newline at end of file +} diff --git a/tests/class_tests.rs b/tests/class_tests.rs index 2f3d1ee..344e4e9 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,9 +1,9 @@ mod test { - use std::rc::Rc; - use std::sync::Arc; - use classfile_reader::{classloader::load_class, io}; use classfile_reader::class::Value; use classfile_reader::vm::Vm; + use classfile_reader::{classloader::load_class, io}; + use std::rc::Rc; + use std::sync::Arc; #[test] fn get_constant_int() { @@ -47,14 +47,17 @@ mod test { fn get_float() { // assert_eq!((55, 0), class.get_version()); - let mut vm = Vm::new("/Users/FJ19WK/RustroverProjects/classfile_reader/tests"); + let mut vm = Vm::new("/Users/FJ19WK/RustroverProjects/java_rs/tests"); let c = vm.get_class("Float").unwrap(); let object = Arc::new(vm.new_instance(c)); - if let Value::F32(v) = *vm.execute("Float","public getF2()F", Some(object)).unwrap() { + if let Value::F32(v) = *vm + .execute("Float", "public getF2()F", Some(object)) + .unwrap() + { assert_eq!(v, 0.0); } else { panic!("fail"); } } -} \ No newline at end of file +}