Repository: ozy/ToyJVM Branch: master Commit: 65be158f9ddd Files: 44 Total size: 105.8 KB Directory structure: gitextract_j969pa58/ ├── .gitignore ├── LICENSE ├── README.md ├── include/ │ ├── attribute.h │ ├── classFile.h │ ├── constantPool.h │ ├── debug.h │ ├── endianness.h │ ├── field.h │ ├── frame.h │ ├── heap.h │ ├── javaClass.h │ ├── machine.h │ ├── method.h │ ├── opcode.h │ ├── printStream.h │ ├── stack.h │ └── stringBuilder.h ├── makefile ├── readme.md ├── src/ │ ├── attribute.c │ ├── classFile.c │ ├── constantPool.c │ ├── field.c │ ├── frame.c │ ├── heap.c │ ├── javaClass.c │ ├── machine.c │ ├── main.c │ ├── opcode.c │ ├── printStream.c │ ├── stack.c │ └── stringBuilder.c ├── test/ │ ├── Factorial.class.txt │ ├── Factorial.java │ ├── HelloWorld.class.txt │ ├── HelloWorld.java │ ├── InstanceTest.class.txt │ ├── InstanceTest.java │ ├── StringExample.class.txt │ ├── StringExample.java │ ├── returnTest.class.txt │ └── returnTest.java └── test.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Ignore the build and lib dirs build dep/* # Ignore any executables bin/* # Ignore Mac specific files .DS_Store # Ignore valgrind dumps vgcore.* # Ignore OpenJDK classes java/* # Ignore Class Files test/*.class # Ignore test outputs test/*.out .vscode/* ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE 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 Lesser 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. GNU GENERAL PUBLIC LICENSE 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. 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. , 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 Lesser General Public License instead of this License. ================================================ FILE: README.md ================================================ # ToyJVM Experimental Java Bytecode Interpreter written in C to understand Java concepts better. ## Getting Started These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ## Prerequisites ```Javac``` is required to compile Java codes into Java bytecodes. ## Running the tests You can run the tests with the ```test.sh``` script. As the project grows up, new tests will be added. ## Running the software You need to give the class name without extension to the executable. For example ```./bin/main test/HelloWorld``` will run the test/HelloWorld.class file. ================================================ FILE: include/attribute.h ================================================ #ifndef ATTRIBUTE_H_ #define ATTRIBUTE_H_ #include #include typedef struct attribute_info { uint16_t attribute_name_index; uint32_t attribute_length; uint8_t* info; }attribute_info; typedef struct exception_table{ // aligned. uint16_t start_pc; uint16_t end_pc; uint16_t handler_pc; uint16_t catch_type; } exception_table; //If the method is either native or abstract, its method_info structure must not have a //Code attribute in its attributes table. Otherwise, its method_info structure must have //exactly one Code attribute in its attributes table typedef struct Code_attribute { uint16_t attribute_name_index; uint32_t attribute_length; uint16_t max_stack; uint16_t max_locals; uint32_t code_length; uint8_t* code; uint16_t exception_table_lenght; exception_table* exceptionTable; uint16_t attributes_count; attribute_info* attributes; }Code_attribute; attribute_info getAttribute_Info(FILE* fd); Code_attribute getCode_AttributeFromAttribute_info(attribute_info fd); void destroyAttribute_Info(attribute_info* attribute); #endif ================================================ FILE: include/classFile.h ================================================ #ifndef CLASSFILE_H_ #define CLASSFILE_H_ #include #include "machine.h" #include "constantPool.h" #include "field.h" #include "method.h" typedef struct field_info field_info; typedef struct ClassFile{ uint32_t magic; uint16_t minor_version; uint16_t major_version; uint16_t constant_pool_count; cp_info* constant_pool; //[constant_pool_count-1] uint16_t access_flags; uint16_t this_class; uint16_t super_class; uint16_t interfaces_count; uint16_t* interfaces; uint16_t fields_count; field_info* fields; uint16_t methods_count; method_info* methods; uint16_t attributes_count; attribute_info* attributes; uint8_t initalized; }ClassFile; ClassFile classFromFile(const char* filename); ClassFile* getClassFromUtf8(CONSTANT_Utf8_info className_utf8, Machine* machine); ClassFile* getClassFromName(const char* className, Machine* machine); int getNumArgs(ClassFile* cf, CONSTANT_Ref_info methodOrInterfaceRef); int isUtf8Equal(CONSTANT_Utf8_info s1, CONSTANT_Utf8_info s2); int isUtf8EqualsToString(CONSTANT_Utf8_info s1, const char* s2); void initClass(ClassFile* cf, Frame* frame); void destroyClass(ClassFile* cf); method_info* getMethodByName(ClassFile* cf, const char* name, const char* desc); method_info* canClassHandleMethod(ClassFile* cf, CONSTANT_Utf8_info name_utf8, CONSTANT_Utf8_info descriptor_utf8); int checkFormat(ClassFile* cf); /* Returns 1 if the class file passes the test. Anything else if not. 1-The first four bytes must contain the right magic number.+ 2-All recognized attributes must be of the proper length.? 3-The class file must not be truncated or have extra bytes at the end.- 4-The constant pool must satisfy the constraints documented throughout §4.4.? For example, each CONSTANT_Class_info structure in the constant pool must contain in its name_index item a valid constant pool index for a CONSTANT_Utf8_info structure. 6-All field references and method references in the constant pool must have valid names, valid classes, and valid descriptors (§4.3). 7-Format checking does not ensure that the given field or method actually exists in the given class, nor that the descriptors given refer to real classes. Format checking ensures only that these items are well formed. More detailed checking is performed when the bytecodes themselves are verified, and during resolution. */ #endif ================================================ FILE: include/constantPool.h ================================================ #ifndef CONSTANTPOOL_H_ #define CONSTANTPOOL_H_ #include #include typedef enum cp_tags{ CONSTANT_Class=7, CONSTANT_Fieldref=9, CONSTANT_Methodref=10, CONSTANT_InterfaceMethodref=11, CONSTANT_String=8, CONSTANT_Integer=3, CONSTANT_Float=4, CONSTANT_Long=5, CONSTANT_Double=6, CONSTANT_NameAndType=12, CONSTANT_Utf8=1, CONSTANT_MethodHandle=15, CONSTANT_MethodType=16, CONSTANT_InvokeDynamic=18 }cp_tags; ////////////////////////////// // CONSTANT INFO STRUCTURES // ////////////////////////////// // the data written directly into the structures. // padding across the platforms should be // checked. typedef struct CONSTANT_Class_info { uint16_t name_index; }CONSTANT_Class_info; typedef struct CONSTANT_Ref_info{ // also applies for The CONSTANT_Fieldref_info, CONSTANT_Methodref_info, and CONSTANT_InterfaceMethodref_info Structures uint16_t class_index; uint16_t name_and_type_index; }CONSTANT_Ref_info; typedef struct CONSTANT_String_info { uint16_t string_index; }CONSTANT_String_info; typedef struct CONSTANT_4BYTES_info { uint32_t bytes; }CONSTANT_4BYTES_info; typedef struct CONSTANT_8BYTES_info { uint64_t bytes; }CONSTANT_8BYTES_info; typedef struct CONSTANT_NameAndType_info { uint16_t name_index; uint16_t descriptor_index; }CONSTANT_NameAndType_info; typedef struct CONSTANT_Utf8_info { // not aligned uint16_t length; // 6 bytes padding uint8_t* bytes; }CONSTANT_Utf8_info; typedef struct CONSTANT_MethodHandle_info { // not aligned uint8_t reference_kind; // ref to method handle. // 1 byte compiler padding uint16_t reference_index; }CONSTANT_MethodHandle_info; typedef struct CONSTANT_MethodType_info { uint16_t descriptor_index; //The constant_pool entry at that index must be a CONSTANT_Utf8_info structure (§4.4.7) //representing a method descriptor (§4.3.3). }CONSTANT_MethodType_info; typedef struct CONSTANT_InvokeDynamic_info { uint16_t bootstrap_method_attr_index; uint16_t name_and_type_index; }CONSTANT_InvokeDynamic_info; typedef enum array_type{ T_BOOLEAN =4, T_CHAR =5, T_FLOAT =6, T_DOUBLE =7, T_BYTE =8, T_SHORT =9, T_INT =10, T_LONG =11 }array_type; typedef struct CONSTANT_Array_info{ // custom int32_t size; // array size is signed 32 bit int. array_type atype; uint8_t* ref; }CONSTANT_Array_info; union CONSTANT_INFO { CONSTANT_Class_info class_info; CONSTANT_Ref_info ref_info; CONSTANT_String_info string_info; CONSTANT_4BYTES_info _4BYTES_info; CONSTANT_8BYTES_info _8BYTES_info; CONSTANT_NameAndType_info nameAndType_info; CONSTANT_Utf8_info utf8_info; CONSTANT_MethodHandle_info methodHandle_info; CONSTANT_MethodType_info methodType_info; CONSTANT_InvokeDynamic_info invodeDynamic_info; }; // unified for runtime constant pool typedef struct cp_info { uint8_t tag; union CONSTANT_INFO info; }cp_info; cp_info cp_infoFromFile(FILE* fd); void destroyCp_Info(cp_info* info); #endif ================================================ FILE: include/debug.h ================================================ #ifdef DEBUG # define DEBUG_PRINT(x) printf x #else # define DEBUG_PRINT(x) do {} while(0) #endif ================================================ FILE: include/endianness.h ================================================ #ifndef ENDIAN_H_ #define ENDIAN_H_ // endian.h was not cross platform and was under a different // name in different OS'es. Assuming most of the desktop cpu's // are little endian, I forged these functions into internal swap // functions in gcc and clang. The visual studio users may use // the _byteswap_ushort, _byteswap_ulong, _byteswap_uint64 functions // respectively. #define be16toh(x) __builtin_bswap16(x) #define be32toh(x) __builtin_bswap32(x) #define be64toh(x) __builtin_bswap64(x) #endif ================================================ FILE: include/field.h ================================================ #ifndef FIELD_H_ #define FIELD_H_ #include #include "attribute.h" #include #include "classFile.h" #include "constantPool.h" typedef struct JavaClass JavaClass; typedef enum field_access_flags{ F_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package. F_ACC_PRIVATE = 0x0002, // Declared private; usable only within the defining class. F_ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses. F_ACC_STATIC = 0x0008, // Declared static. F_ACC_FINAL = 0x0010, // Declared final; never directly assigned to after object construction (JLS §17.5). F_ACC_VOLATILE = 0x0040, // Declared volatile; cannot be cached. F_ACC_TRANSIENT = 0x0080, // Declared transient; not written or read by a persistent object manager. F_ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code. F_ACC_ENUM = 0x4000 // Declared as an element of an enum. }access_flags; typedef struct field_info { uint16_t access_flags; uint16_t name_index; uint16_t descriptor_index; uint16_t attributes_count; attribute_info* attributes; uint64_t value; }field_info; field_info getField_Info(FILE* fd); field_info* getStaticField(ClassFile* cf, CONSTANT_Utf8_info fieldName, CONSTANT_Utf8_info fieldDesc); field_info* getField(JavaClass* jc, CONSTANT_Utf8_info fieldName, CONSTANT_Utf8_info fieldDesc); void putField(JavaClass* instance, CONSTANT_Utf8_info fieldName, CONSTANT_Utf8_info fieldDesc, uint64_t val); void destroyField_Info(field_info* field); #endif ================================================ FILE: include/frame.h ================================================ #ifndef FRAME_H_ #define FRAME_H_ #include //#include "stack.h" #include "attribute.h" #include "constantPool.h" typedef struct ClassFile ClassFile; typedef struct Machine Machine; typedef uint64_t LocalVariable; // A single local variable can hold a value of type boolean, byte, char, short, int, float, reference, or returnAddress. // A pair of local variables can hold a value of type long or double. // But we wont implement this, instead a local variable in our jvm can hold up to 64 bits. typedef uint64_t Operand; // Each entry on the operand stack can hold a value of any Java Virtual Machine type, including a value of type long or type double. typedef struct Frame{ LocalVariable* localVariables; struct Stack* operandStack; struct Machine* machine; struct ClassFile* classRef; uint32_t pc; // program counter Code_attribute* code; //A run-time constant pool is a per-class or per-interface run-time representation of the constant_pool table in a class file (§4.4). //It contains several kinds of constants, ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time. } Frame; Frame createNewFrame (Code_attribute code, ClassFile* cf, Machine* machine); void destroyFrame (Frame* frame); #endif ================================================ FILE: include/heap.h ================================================ #ifndef HEAP_H_ #define HEAP_H_ #include #include typedef struct HeapObject{ void* addr; size_t size; uint8_t isUsed; }HeapObject; typedef struct Heap{ uint64_t size; uint64_t top; HeapObject* heap; }Heap; Heap initHeap(size_t size); void* hGet(uint64_t addr, Heap* heap); uint64_t hAlloc(size_t size, Heap* heap); uint64_t hExtend(uint64_t addr, size_t size, Heap* heap); void* hFree(uint64_t addr, Heap* heap); void destroyHeap(Heap* heap); #endif ================================================ FILE: include/javaClass.h ================================================ #ifndef JAVACLASS_H_ #define JAVACLASS_H_ #include "classFile.h" typedef struct JavaClass{ ClassFile* classFile; field_info* fields; }JavaClass; void initInstanceFields(JavaClass* instance); #endif ================================================ FILE: include/machine.h ================================================ #ifndef MACHINE_H_ #define MACHINE_H_ #include "opcode.h" #include "stack.h" #include "heap.h" typedef struct Machine{ struct ClassFile* classFiles; uint64_t numClasses; Stack* JVMSTACK; Heap* heap; }Machine; void* executeCode(Machine* machine, OPCODE** opcodes); void destroyMachine(Machine* machine); #endif ================================================ FILE: include/method.h ================================================ #ifndef METHOD_H_ #define METHOD_H_ #include "attribute.h" #include "field.h" typedef struct field_info method_info; // same as field info typedef enum method_access_flags{ M_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package. M_ACC_PRIVATE = 0x0002, // Declared private; accessible only within the defining class. M_ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses. M_ACC_STATIC = 0x0008, // Declared static. M_ACC_FINAL = 0x0010, // Declared final; must not be overridden (§5.4.5). M_ACC_SYNCHRONIZED = 0x0020, // Declared synchronized; invocation is wrapped by a monitor use. M_ACC_BRIDGE = 0x0040, // A bridge method, generated by the compiler. M_ACC_VARARGS = 0x0080, // Declared with variable number of arguments. M_ACC_NATIVE = 0x0100, // Declared native; implemented in a language other than Java. M_ACC_ABSTRACT = 0x0400, // Declared abstract; no implementation is provided. M_ACC_STRICT = 0x0800, // Declared strictfp; floating-point mode is FP-strict. M_ACC_SYNTHETIC = 0x1000 // Declared synthetic; not present in the source code. }method_access_flags; #define getMethod_Info(fd) getField_Info(fd) // same too, no need to reimplement #define destroyMethod_Info(info) destroyField_Info(info) #endif ================================================ FILE: include/opcode.h ================================================ #ifndef OPCODE_H_ #define OPCODE_H_ #include #include "frame.h" typedef void* OPCODE(struct Frame*); OPCODE** initOpcodes(); typedef enum OPCODENAMES{ ICONST_M1 = 0x02, ICONST_0 = 0x03, ICONST_1 = 0x04, ICONST_2 = 0x05, ICONST_3 = 0x06, ICONST_4 = 0x07, ICONST_5 = 0x08, LCONST_0 = 0x09, LCONST_1 = 0x0A, DCONST_0 = 0x0E, DCONST_1 = 0x0F, BIPUSH = 0x10, SIPUSH = 0x11, LDC = 0x12, LDC2_W = 0x14, ILOAD = 0x15, LLOAD = 0x16, DLOAD = 0x18, ILOAD_0 = 0x1A, ILOAD_1 = 0x1B, ILOAD_2 = 0x1C, ILOAD_3 = 0x1D, LLOAD_0 = 0x1E, LLOAD_1 = 0x1F, LLOAD_2 = 0x20, LLOAD_3 = 0x21, DLOAD_3 = 0x29, ALOAD_0 = 0x2A, ALOAD_1 = 0x2B, ALOAD_2 = 0x2C, ALOAD_3 = 0x2D, IALOAD = 0x2E, AALOAD = 0x32, ISTORE = 0x36, LSTORE = 0x37, DSTORE = 0x39, ISTORE_0 = 0x3B, ISTORE_1 = 0x3C, ISTORE_2 = 0x3D, ISTORE_3 = 0x3E, LSTORE_0 = 0x3F, LSTORE_1 = 0x40, LSTORE_2 = 0x41, LSTORE_3 = 0x42, DSTORE_3 = 0x4A, ASTORE_0 = 0x4B, ASTORE_1 = 0x4C, ASTORE_2 = 0x4D, ASTORE_3 = 0x4E, IASTORE = 0x4F, AASTORE = 0x53, POP = 0x57, DUP = 0x59, IADD = 0x60, LADD = 0x61, DADD = 0x63, ISUB = 0x64, DSUB = 0x67, IMUL = 0x68, DMUL = 0x6B, DDIV = 0x6F, IREM = 0x70, IINC = 0x84, I2D = 0x87, I2C = 0x92, DCMPG = 0x98, IFNE = 0x9A, IFLT = 0x9B, IFGE = 0x9C, IFLE = 0x9E, IF_ICMPLT = 0xA1, IF_ICMPGE = 0xA2, IF_ICMPGT = 0xA3, IF_ICMPLE = 0xA4, GOTO = 0xA7, IRET = 0xAC, LRET = 0xAD, DRETURN = 0xAF, ARETURN = 0xB0, RETURN = 0xB1, GETSTATIC = 0xB2, PUTSTATIC = 0xB3, GETFIELD = 0xB4, PUTFIELD = 0xB5, INVOKEVIRTUAL = 0xB6, INVOKESPECIAL = 0xB7, INVOKESTATIC = 0xB8, NEW = 0xBB, NEWARRAY = 0xBC, ANEWARRAY = 0xBD, ARRAYLENGTH = 0xBE }OPCODENAMES; #endif ================================================ FILE: include/printStream.h ================================================ #ifndef PRINTSTREAM_H_ #define PRINTSTREAM_H_ #include #include "frame.h" #include "classFile.h" void handlePrintStream(uint64_t* localVariables, Frame* frame, CONSTANT_Utf8_info name_utf8, CONSTANT_Utf8_info desc_utf8); #endif ================================================ FILE: include/stack.h ================================================ #ifndef STACK_H_ #define STACK_H_ #include #include "frame.h" typedef enum StackType{ TYPE_OPERANDSTACK = sizeof(uint64_t), TYPE_JVMSTACK = sizeof(struct Frame) }StackType; typedef struct Stack{ unsigned char* stack; StackType stackType; uint64_t top; uint64_t maxSize; }Stack; Stack initStack(uint64_t numItems, StackType stackType); // size is given as numItems here void* popStack(Stack* stack); void pushStack(void* val, Stack* stack); void* peekStack(Stack* stack); void destroyStack(Stack* stack); #endif ================================================ FILE: include/stringBuilder.h ================================================ #ifndef STRINGBUILDER_H_ #define STRINGBUILDER_H_ #include #include "frame.h" #include "classFile.h" void handleStringBuilder(uint64_t* localVariables, Frame* frame, CONSTANT_Utf8_info name_utf8, CONSTANT_Utf8_info desc_utf8); #endif ================================================ FILE: makefile ================================================ SRCDIR = src HDRDIR = include OBJDIR = build BINDIR = bin DEPDIR = dep CC = gcc TARGET = main CFLAGS = -g -O0 -std=c99 -I$(HDRDIR) #-DDEBUG=1 LFLAGS = -lm SOURCES := $(wildcard $(SRCDIR)/*.c) HEADERS := $(wildcard $(HDRDIR)/*.h) OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) DEPS := $(OBJECTS:$(OBJDIR)/%.o=$(DEPDIR)/%.d) REMOVE := rm -rf # Linking $(BINDIR)/$(TARGET): $(OBJECTS) $(CMN_OBJ) mkdir -p $(BINDIR) $(CC) $(LFLAGS) -o $@ $(OBJECTS) $(CMN_OBJ) @echo "Linking complete" -include $(DEPS) # Compilation $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c mkdir -p $(OBJDIR) mkdir -p $(DEPDIR) $(CC) -c $(CFLAGS) $< -o $@ $(CC) -I$(HDRDIR) -MM -MT '$(OBJDIR)/$*.o' $(SRCDIR)/$*.c > $(DEPDIR)/$*.d @echo "Compiled $<" # Generate file list for cscope cscope.files: $(SOURCES) $(HEADERS) echo $(SOURCES) $(HEADERS) > cscope.files # Generate cscope database cscope.out: cscope.files cscope -q -R -b -i cscope.files .PHONY: clean clean: $(REMOVE) $(OBJECTS) $(OBJDIR) $(BINDIR) $(DEPDIR) @echo "Deleted $<" .PHONY: remove remove: $(REMOVE) $(BINDIR)/$(TARGET) $(REMOVE) $(OBJECTS) $(REMOVE) $(DEPS) $(REMOVE) cscope.* @echo "Deleted $<" .PHONY: cscope cscope: cscope.out .PHONY: all all: $(BINDIR)/$(TARGET) ================================================ FILE: readme.md ================================================ # ToyJVM Experimental Java Bytecode Interpreter written in C to understand Java concepts better. ## Getting Started These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ## Prerequisites ```Javac``` is required to compile Java codes into Java bytecodes. ## Running the tests You can run the tests with the ```test.sh``` script. As the project grows up, new tests will be added. ## Running the software You need to give the class name without extension to the executable. For example ```./bin/main test/HelloWorld``` will run the HelloWorld.class file. ================================================ FILE: src/attribute.c ================================================ #include "attribute.h" #include #include "endianness.h" #include attribute_info getAttribute_Info(FILE* fd){ attribute_info attribute; fread(&attribute.attribute_name_index,sizeof(attribute.attribute_name_index),1,fd); attribute.attribute_name_index = be16toh(attribute.attribute_name_index); fread(&attribute.attribute_length,sizeof(attribute.attribute_length),1,fd); attribute.attribute_length = be32toh(attribute.attribute_length); attribute.info = malloc(attribute.attribute_length); // leak fread(attribute.info,attribute.attribute_length,1,fd); return attribute; } void destroyAttribute_Info(attribute_info* attribute){ free(attribute->info); } Code_attribute getCode_AttributeFromAttribute_info(attribute_info attributeInfo){ Code_attribute attribute; /* uint16_t attribute_name_index; uint32_t attribute_length; uint16_t max_stack; uint16_t max_locals; uint32_t code_length; uint8_t* code; uint16_t exception_table_lenght; exception_table* exceptionTable; uint16_t attributes_count; attribute_info* attributes; */ attribute.attribute_name_index = attributeInfo.attribute_name_index; attribute.attribute_length = attributeInfo.attribute_length; int memOffset=0; memcpy(&attribute.max_stack, attributeInfo.info+memOffset,sizeof(attribute.max_stack)); attribute.max_stack = be16toh(attribute.max_stack); memOffset +=sizeof(attribute.max_stack); memcpy(&attribute.max_locals, attributeInfo.info+memOffset,sizeof(attribute.max_locals)); attribute.max_locals = be16toh(attribute.max_locals); memOffset +=sizeof(attribute.max_locals); memcpy(&attribute.code_length, attributeInfo.info+memOffset,sizeof(attribute.code_length)); attribute.code_length = be32toh(attribute.code_length); memOffset +=sizeof(attribute.code_length); attribute.code = attributeInfo.info+memOffset; // leak memOffset +=attribute.code_length; // remainings will be implemented later. return attribute; } ================================================ FILE: src/classFile.c ================================================ #include "classFile.h" #include #include #include "endianness.h" #include "field.h" #include "method.h" #include "constantPool.h" #include "string.h" #include "debug.h" ClassFile classFromFile(const char* filename){ FILE *fd; fd = fopen(filename, "r"); if (fd == NULL){ printf("File: %s\n",filename); perror("Error while opening the file.\n"); exit(EXIT_FAILURE); } /* uint32_t magic; uint16_t minor_version; uint16_t major_version; uint16_t constant_pool_count; cp_info constant_pool[constant_pool_count-1]; */ ClassFile classFile; fread(&classFile.magic,sizeof(classFile.magic),1,fd); classFile.magic = be32toh(classFile.magic); // 0xCAFEBABE fread(&classFile.minor_version,sizeof(classFile.minor_version),1,fd); classFile.minor_version = be16toh(classFile.minor_version); fread(&classFile.major_version,sizeof(classFile.major_version),1,fd); classFile.major_version = be16toh(classFile.major_version); fread(&classFile.constant_pool_count,sizeof(classFile.constant_pool_count),1,fd); classFile.constant_pool_count = be16toh(classFile.constant_pool_count); classFile.constant_pool = malloc (sizeof(cp_info) * (classFile.constant_pool_count-1)); // leak // The value of the constant_pool_count item is equal to the number of entries in the constant_pool table plus one. //A constant_pool index is considered valid if it is greater than zero and less than constant_pool_count, //with the exception for constants of type long and double noted in §4.4.5. for (int poolCount=0; poolCount < classFile.constant_pool_count-1; poolCount++){ // -1 same reason above classFile.constant_pool[poolCount] = cp_infoFromFile(fd); // leak if (classFile.constant_pool[poolCount].tag == CONSTANT_Long || classFile.constant_pool[poolCount].tag == CONSTANT_Double){ // all 8 byte types in cp takes two indexes. So increase extra. classFile.constant_pool[++poolCount].tag=0; } } /* u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; */ fread(&classFile.access_flags,sizeof(classFile.access_flags),1,fd); classFile.access_flags = be16toh(classFile.access_flags); fread(&classFile.this_class,sizeof(classFile.this_class),1,fd); classFile.this_class = be16toh(classFile.this_class); fread(&classFile.super_class,sizeof(classFile.super_class),1,fd); classFile.super_class = be16toh(classFile.super_class); fread(&classFile.interfaces_count,sizeof(classFile.interfaces_count),1,fd); classFile.interfaces_count = be16toh(classFile.interfaces_count); classFile.interfaces = malloc(classFile.interfaces_count * sizeof (uint16_t)); // leak for (int interfaceCount=0; interfaceCount < classFile.interfaces_count; interfaceCount++){ uint16_t interface; fread(&interface,sizeof(interface),1,fd); classFile.interfaces[interfaceCount] = be16toh(interface); } fread(&classFile.fields_count,sizeof(classFile.fields_count),1,fd); classFile.fields_count = be16toh(classFile.fields_count); classFile.fields = malloc(sizeof(field_info) * classFile.fields_count); // leak for (int fieldCount=0; fieldCount < classFile.fields_count; fieldCount++){ classFile.fields[fieldCount] = getField_Info(fd); // leak } /* u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; */ fread(&classFile.methods_count,sizeof(classFile.methods_count),1,fd); classFile.methods_count = be16toh(classFile.methods_count); classFile.methods = malloc (sizeof(method_info) * classFile.methods_count); // leak for (int methodCount = 0; methodCountconstant_pool[methodOrInterfaceRef.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info descriptor_utf8 = cf->constant_pool[nameAndType->descriptor_index-1].info.utf8_info; int numArgs = 0; for (int index=0; index < descriptor_utf8.length; index++){ switch (descriptor_utf8.bytes[index]){ // todo check for array types case 'L': for (; descriptor_utf8.bytes[index] != ';'; index++){ } numArgs++; break; case '(': break; case ')': return numArgs; default: numArgs++; break; } } return numArgs; } ClassFile* getClassFromUtf8(CONSTANT_Utf8_info className_utf8, Machine* machine){ // to C string char className[className_utf8.length+1]; memcpy(&className,className_utf8.bytes,className_utf8.length); className[className_utf8.length] = '\0'; return getClassFromName(className, machine); } ClassFile* getClassFromName(const char* className, Machine* machine){ // todo BTree for (int classId=0; classIdnumClasses; classId++){ ClassFile* class = &machine->classFiles[classId]; CONSTANT_Class_info classInfo = class->constant_pool[class->this_class - 1].info.class_info; CONSTANT_Utf8_info _className = class->constant_pool[classInfo.name_index-1].info.utf8_info; if (isUtf8EqualsToString(_className, className)){ return class; } //printf("checked a class, id: %d : %.*s\n",class->this_class,class->constant_pool[class->this_class-1].info.utf8_info.length,class->constant_pool[class->this_class-1].info.utf8_info.bytes); // todo } // This is a hack in order to run System.out.println() // check if its a native class if (!strcmp(className, "java/lang/System") || !strcmp(className, "java/io/PrintStream") || !strcmp(className, "java/lang/StringBuilder")) return NULL; // create if not exists char buf[strlen(className)+strlen(".class")+1]; strcpy(buf, className); strcat(buf, ".class"); machine->classFiles[machine->numClasses++] = classFromFile(buf); return &machine->classFiles[machine->numClasses-1]; } method_info* getMethodByName(ClassFile* cf, const char* name, const char* desc){ for (int methodId=0; methodId < cf->methods_count; methodId++){ CONSTANT_Utf8_info classMethodName_utf8 = cf->constant_pool[cf->methods[methodId].name_index-1].info.utf8_info; // todo possibly optimize this func CONSTANT_Utf8_info classMethodDesc_utf8 = cf->constant_pool[cf->methods[methodId].descriptor_index-1].info.utf8_info; // todo possibly optimize this func if (isUtf8EqualsToString(classMethodName_utf8, name) && isUtf8EqualsToString(classMethodDesc_utf8, desc)){ DEBUG_PRINT( ("Found Method Name: %.*s\n",classMethodName_utf8.length,classMethodName_utf8.bytes)); return &cf->methods[methodId]; } } return NULL; } void initClass(ClassFile* cf, Frame* frame){ if (!cf->initalized){ method_info* method = getMethodByName(cf, "","()V"); //DEBUG_PRINT( ("Method's Class Name: %.*s\n",name_utf8.length,name_utf8.bytes)); Code_attribute code; if (method != NULL){ code = getCode_AttributeFromAttribute_info(method->attributes[0]); //method->attributes[0] cf->initalized = 1; Frame newFrame = createNewFrame(code, cf, frame->machine); pushStack(&newFrame, frame->machine->JVMSTACK); frame->pc++; }else{ // todo method not found DEBUG_PRINT((" method not found \n")); } } } void destroyClass(ClassFile* cf){ for (--cf->constant_pool_count; cf->constant_pool_count>0; cf->constant_pool_count--) destroyCp_Info(&cf->constant_pool[cf->constant_pool_count-1]); free(cf->constant_pool); free(cf->interfaces); for (; cf->fields_count>0; cf->fields_count--) destroyField_Info(&cf->fields[cf->fields_count-1]); free(cf->fields); for (; cf->methods_count>0; cf->methods_count--) destroyMethod_Info(&cf->methods[cf->methods_count-1]); free(cf->methods); for (; cf->attributes_count>0; cf->attributes_count--) destroyAttribute_Info(&cf->attributes[cf->attributes_count-1]); free(cf->attributes); } method_info* canClassHandleMethod(ClassFile* cf, CONSTANT_Utf8_info name_utf8, CONSTANT_Utf8_info descriptor_utf8){ DEBUG_PRINT( ("Invoked Method Name: %.*s, %.*s\n",name_utf8.length,name_utf8.bytes, descriptor_utf8.length,descriptor_utf8.bytes)); for (int methodId=0; methodId < cf->methods_count; methodId++){ CONSTANT_Utf8_info classMethodName_utf8 = cf->constant_pool[cf->methods[methodId].name_index-1].info.utf8_info; // todo possibly optimize this func CONSTANT_Utf8_info classMethodDesc_utf8 = cf->constant_pool[cf->methods[methodId].descriptor_index-1].info.utf8_info; // todo possibly optimize this func if (isUtf8Equal(name_utf8, classMethodName_utf8) && isUtf8Equal(descriptor_utf8, classMethodDesc_utf8)){ DEBUG_PRINT( ("Found Method Name: %.*s\n",classMethodName_utf8.length,classMethodName_utf8.bytes)); return &cf->methods[methodId]; } } return NULL; // method is not in this class } int checkFormat(ClassFile* cf){ //1 if (cf->magic != 0xCAFEBABE) return 0; //2 for (int attributeCount=0; attributeCountattributes_count; attributeCount++){ uint16_t utf8Index = cf->attributes[attributeCount].attribute_name_index; if (cf->constant_pool[utf8Index-1].tag != CONSTANT_Utf8){ // constant pool starts from 1 (lol, bcz oracle said) //The constant_pool entry at attribute_name_index must be a CONSTANT_Utf8_info structure return 0; } } //4 for (int constantPoolCount=0; constantPoolCount < cf->constant_pool_count-1; constantPoolCount++){ switch (cf->constant_pool[constantPoolCount].tag){ case CONSTANT_Double: case CONSTANT_Long: // oracle's stupid ideas. see double_info in constantpool.c constantPoolCount++; break; case CONSTANT_Class:; // empty statement ; uint16_t utf8Index = cf->constant_pool[constantPoolCount].info.class_info.name_index; if (cf->constant_pool[utf8Index-1].tag != CONSTANT_Utf8) return 0; break; case CONSTANT_Fieldref:; // The class_index item of a CONSTANT_Fieldref_info structure may be either a class type or an interface type. CONSTANT_Ref_info fieldref = cf->constant_pool[constantPoolCount].info.ref_info; if (!(cf->constant_pool[fieldref.class_index-1].tag == CONSTANT_Class || cf->constant_pool[fieldref.class_index-1].tag == CONSTANT_InterfaceMethodref)) return 0; break; case CONSTANT_InterfaceMethodref:; CONSTANT_Ref_info interfaceMethodref = cf->constant_pool[constantPoolCount].info.ref_info; if (cf->constant_pool[interfaceMethodref.class_index-1].tag != CONSTANT_InterfaceMethodref) return 0; break; case CONSTANT_Methodref:; // The class_index item of a CONSTANT_Fieldref_info structure may be either a class type or an interface type. CONSTANT_Ref_info methodref = cf->constant_pool[constantPoolCount].info.ref_info; if (cf->constant_pool[methodref.class_index-1].tag != CONSTANT_Class) return 0; break; default: break; } } return 1; } ================================================ FILE: src/constantPool.c ================================================ #include "constantPool.h" #include #include #include "endianness.h" /* Constant Pool Indexes written in the class file starts from 1; to access a constant, please get the (index - 1)th value. */ cp_info cp_infoFromFile(FILE* fd){ cp_info info; fread(&info.tag,1,1,fd); // tag len is 1 switch (info.tag){ case CONSTANT_Class: fread(&info.info.class_info.name_index,sizeof(info.info.class_info.name_index),1,fd); info.info.class_info.name_index = be16toh(info.info.class_info.name_index); // big order to host break; case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: fread(&info.info.ref_info.class_index,sizeof(info.info.ref_info.class_index),1,fd); fread(&info.info.ref_info.name_and_type_index,sizeof(info.info.ref_info.name_and_type_index),1,fd); info.info.ref_info.class_index = be16toh(info.info.ref_info.class_index); info.info.ref_info.name_and_type_index = be16toh(info.info.ref_info.name_and_type_index); break; case CONSTANT_String: fread(&info.info.string_info.string_index,sizeof(info.info.string_info.string_index),1,fd); info.info.string_info.string_index = be16toh(info.info.string_info.string_index); break; case CONSTANT_Integer: case CONSTANT_Float: fread(&info.info._4BYTES_info.bytes,sizeof(info.info._4BYTES_info.bytes),1,fd); info.info._4BYTES_info.bytes = be32toh(info.info._4BYTES_info.bytes); break; case CONSTANT_Long: case CONSTANT_Double: // 8 byte types takes two indexes in constant pool. So increase the counter by one extra. fread(&info.info._8BYTES_info.bytes,sizeof(info.info._8BYTES_info.bytes),1,fd); info.info._8BYTES_info.bytes = be64toh(info.info._8BYTES_info.bytes); break; case CONSTANT_NameAndType: fread(&info.info.nameAndType_info.name_index,sizeof(info.info.nameAndType_info.name_index),1,fd); fread(&info.info.nameAndType_info.descriptor_index,sizeof(info.info.nameAndType_info.descriptor_index),1,fd); info.info.nameAndType_info.name_index = be16toh(info.info.nameAndType_info.name_index); info.info.nameAndType_info.descriptor_index = be16toh(info.info.nameAndType_info.descriptor_index); break; case CONSTANT_Utf8:; // compiler wants a statement after label. fread(&info.info.utf8_info.length,sizeof(info.info.utf8_info.length),1,fd); // len info.info.utf8_info.length = be16toh(info.info.utf8_info.length); info.info.utf8_info.bytes = malloc(info.info.utf8_info.length); //leak fread(info.info.utf8_info.bytes,info.info.utf8_info.length,1,fd); // offsetting 2 bytes + reading utf8 //printf("len: %d, s: %.*s\n",info.info.utf8_info.length,info.info.utf8_info.length, info.info.utf8_info.bytes); break; case CONSTANT_MethodHandle:; // compiler wants a statement after label. fread(&info.info.methodHandle_info.reference_kind,sizeof(info.info.methodHandle_info.reference_kind),1,fd); // reference kind fread(&info.info.methodHandle_info.reference_index,sizeof(info.info.methodHandle_info.reference_index),1,fd); // ref index info.info.methodHandle_info.reference_index = be16toh(info.info.methodHandle_info.reference_index); break; case CONSTANT_MethodType: fread(&info.info.methodType_info.descriptor_index,sizeof(info.info.methodType_info.descriptor_index),1,fd); info.info.methodType_info.descriptor_index = be16toh(info.info.methodType_info.descriptor_index); break; case CONSTANT_InvokeDynamic: fread(&info.info.invodeDynamic_info.bootstrap_method_attr_index,sizeof(info.info.invodeDynamic_info.bootstrap_method_attr_index),1,fd); fread(&info.info.invodeDynamic_info.name_and_type_index,sizeof(info.info.invodeDynamic_info.name_and_type_index),1,fd); info.info.invodeDynamic_info.bootstrap_method_attr_index = be16toh(info.info.invodeDynamic_info.bootstrap_method_attr_index); info.info.invodeDynamic_info.name_and_type_index = be16toh(info.info.invodeDynamic_info.name_and_type_index); break; default: //tag err break; } return info; } void destroyCp_Info(cp_info* info){ if (info->tag == CONSTANT_Utf8) free(info->info.utf8_info.bytes); } ================================================ FILE: src/field.c ================================================ #include "field.h" #include #include "endianness.h" #include #include "classFile.h" #include "constantPool.h" #include "javaClass.h" field_info getField_Info(FILE* fd){ field_info field; fread(&field.access_flags,sizeof(field.access_flags),1,fd); field.access_flags = be16toh(field.access_flags); fread(&field.name_index,sizeof(field.name_index),1,fd); field.name_index = be16toh(field.name_index); fread(&field.descriptor_index,sizeof(field.descriptor_index),1,fd); field.descriptor_index = be16toh(field.descriptor_index); fread(&field.attributes_count,sizeof(field.attributes_count),1,fd); field.attributes_count = be16toh(field.attributes_count); field.attributes = malloc (sizeof(attribute_info) * field.attributes_count); for (int attributeCount=0; attributeCountattributes_count > 0; field->attributes_count--) destroyAttribute_Info(&field->attributes[field->attributes_count-1]); free(field->attributes); } void putField(JavaClass* instance, CONSTANT_Utf8_info fieldName, CONSTANT_Utf8_info fieldDesc, uint64_t val){ field_info* field = getField(instance, fieldName, fieldDesc); field->value = val; } field_info* getField(JavaClass* jc, CONSTANT_Utf8_info fieldName, CONSTANT_Utf8_info fieldDesc){ for (uint16_t fieldId=0; fieldIdclassFile->fields_count; fieldId++){ field_info* field = &jc->fields[fieldId]; CONSTANT_Utf8_info _fieldName = jc->classFile->constant_pool[field->name_index-1].info.utf8_info; CONSTANT_Utf8_info _fieldDesc = jc->classFile->constant_pool[field->descriptor_index-1].info.utf8_info; if (isUtf8Equal(fieldName,_fieldName) && isUtf8Equal(fieldDesc,_fieldDesc)){ return field; } } return NULL; } field_info* getStaticField(ClassFile* cf, CONSTANT_Utf8_info fieldName, CONSTANT_Utf8_info fieldDesc){ for (uint16_t fieldId=0; fieldIdfields_count; fieldId++){ field_info* field = &cf->fields[fieldId]; CONSTANT_Utf8_info _fieldName = cf->constant_pool[field->name_index-1].info.utf8_info; CONSTANT_Utf8_info _fieldDesc = cf->constant_pool[field->descriptor_index-1].info.utf8_info; if (isUtf8Equal(fieldName,_fieldName) && isUtf8Equal(fieldDesc,_fieldDesc)){ return field; } } return NULL; } ================================================ FILE: src/frame.c ================================================ #include "frame.h" #include #include "classFile.h" #include "attribute.h" Frame createNewFrame (Code_attribute code, ClassFile* cf, Machine* machine){ Frame newFrame; newFrame.code = malloc (sizeof(Code_attribute)); *newFrame.code = code; newFrame.localVariables = malloc (sizeof(LocalVariable) * newFrame.code->max_locals); newFrame.operandStack = malloc (sizeof(Stack)); *newFrame.operandStack = initStack(newFrame.code->max_stack, TYPE_OPERANDSTACK); newFrame.pc = 0; newFrame.classRef = cf; newFrame.machine = machine; return newFrame; } void destroyFrame (Frame* frame){ free(frame->localVariables); free(frame->code); destroyStack(frame->operandStack); // dont free the frame struct itself. It contains an address into JVMStack. // JVMStack is responsible for freeing or overwriting it. } ================================================ FILE: src/heap.c ================================================ #include "heap.h" Heap initHeap(size_t size){ Heap heap; heap.heap = calloc(size,sizeof(HeapObject)); heap.size = size; heap.top = 1; // 0 is for null object refs. return heap; } void* hGet(uint64_t addr, Heap* heap){ return heap->heap[addr].addr; } uint64_t hAlloc(size_t size, Heap* heap){ for (size_t mem=0; memtop; mem++){ // this gets slower and sloooower as the number of object in heap increases. if (!heap->heap[mem].isUsed){ heap->heap[mem].isUsed=1; heap->heap[mem].size = size; free(heap->heap[mem].addr); heap->heap[mem].addr = malloc(heap->heap[mem].size); return (mem); } } if(heap->top + 1 > heap->size){ heap->heap = realloc(heap->heap,heap->size+=1024); // realloc 1024 more } heap->heap[heap->top].isUsed = 1; heap->heap[heap->top].size = size; heap->heap[heap->top].addr = malloc(size); return (heap->top++); } uint64_t hExtend(uint64_t addr, size_t size, Heap* heap){ heap->heap[addr].size += size; heap->heap[addr].addr = realloc(heap->heap[addr].addr,heap->heap[addr].size); return addr; } void* hFree(uint64_t addr, Heap* heap){ heap->heap[addr].isUsed = 0; return NULL; } void destroyHeap(Heap* heap){ for(;heap->top > 0; heap->top--) free(heap->heap[heap->top -1].addr); free(heap->heap); free(heap); } ================================================ FILE: src/javaClass.c ================================================ #include "javaClass.h" #include #include "heap.h" #include void initInstanceFields(JavaClass* instance){ instance->fields = malloc(instance->classFile->fields_count * sizeof(field_info)); memcpy(instance->fields, instance->classFile->fields, instance->classFile->fields_count * sizeof(field_info)); } ================================================ FILE: src/machine.c ================================================ #include "machine.h" #include "frame.h" #include "opcode.h" #include "stack.h" #include "debug.h" #include "classFile.h" void* executeCode(Machine* JVM, OPCODE** opcodes){ // anything can be returned from an execution Frame* frame; void* retCode; while (1){ frame = (Frame*)peekStack(JVM->JVMSTACK); DEBUG_PRINT(("---------------\n")); DEBUG_PRINT(("peeked stack is %p\n",frame)); #ifdef DEBUG printf("---Code Dump---\n"); for (int qq=0; qqcode->code_length; qq++) printf("%d: 0x%1x\n", qq,frame->code->code[qq]); printf("-----DUMP------\n"); #endif for (; frame->pc < frame->code->code_length; frame->pc++){ retCode = NULL; DEBUG_PRINT(("pc: %d -> 0x%1x, opStackTop: %d\n",frame->pc , frame->code->code[frame->pc],frame->operandStack->top)); retCode = opcodes[frame->code->code[frame->pc]](frame); if (retCode != NULL){ // the opcode returned something to push // to the invoker operand stack //DEBUG_PRINT(("retcode is not null\n")); break; } if (frame != (Frame*)peekStack(JVM->JVMSTACK)){ break; // frame changed, break! } } if (retCode != NULL){ DEBUG_PRINT(("returned something, in num: %d\n", *(uint64_t*) retCode)); destroyFrame((Frame*)popStack(JVM->JVMSTACK)); //destroy completely Frame* frameInvoker = (Frame*)peekStack(JVM->JVMSTACK); pushStack(retCode,frameInvoker->operandStack); // frame returned something to push free(retCode); // iret mallocs it. // into previous frames operand stack }else if (frame->pc > frame->code->code_length){ DEBUG_PRINT(("no code left to execute in this frame pc: %d, codelen: %d\n", frame->pc, frame->code->code_length)); destroyFrame((Frame*)popStack(JVM->JVMSTACK)); //destroy completely // this frame finished without returning anything } if (peekStack(JVM->JVMSTACK) == NULL){ DEBUG_PRINT(("no frame left to execute\n")); free(opcodes); return NULL; } } } void destroyMachine(Machine* machine){ for (;machine->numClasses>0; machine->numClasses--) destroyClass(&machine->classFiles[machine->numClasses-1]); free(machine->classFiles); destroyStack(machine->JVMSTACK); destroyHeap(machine->heap); } ================================================ FILE: src/main.c ================================================ #include #include #include "machine.h" #include "classFile.h" #include #include "opcode.h" // gecici #include "stack.h" #include "attribute.h" #include "constantPool.h" #include "machine.h" #include "heap.h" #include "debug.h" void main(int argc, char* argv[]){ Machine JVM; JVM.numClasses = 0; JVM.classFiles = malloc(sizeof(ClassFile) * 255); ClassFile* mainClass = getClassFromName(argv[1], &JVM); assert(checkFormat(mainClass)); // file format check //DEBUG_PRINT(("%x\n",mainClass->magic)); DEBUG_PRINT(("Minor: %d, Major: %d\n", mainClass->minor_version,mainClass->major_version)); DEBUG_PRINT(("constant_pool_count: %d\n",mainClass->constant_pool_count)); DEBUG_PRINT(("access_flags: %d\n",mainClass->access_flags)); DEBUG_PRINT(("this_class: %d\n",mainClass->this_class)); DEBUG_PRINT(("super_class: %d\n",mainClass->super_class)); DEBUG_PRINT(("interfaces_count: %d\n",mainClass->interfaces_count)); DEBUG_PRINT(("fields_count: %d\n",mainClass->fields_count)); DEBUG_PRINT(("methods_count: %d\n",mainClass->methods_count)); DEBUG_PRINT(("attributes_count: %d\n",mainClass->attributes_count)); cp_info info = mainClass->constant_pool[mainClass->this_class-1]; CONSTANT_Class_info classInfo = info.info.class_info; if (info.tag == CONSTANT_Class){ CONSTANT_Utf8_info utf8 = mainClass->constant_pool[classInfo.name_index-1].info.utf8_info; DEBUG_PRINT( ("This Class Name: %.*s\n",utf8.length,utf8.bytes)); } JVM.heap = malloc(sizeof(Heap)); *JVM.heap = initHeap(512); JVM.JVMSTACK = malloc(sizeof(Stack)); *JVM.JVMSTACK = initStack(255,TYPE_JVMSTACK); Frame newFrame; method_info* method = getMethodByName(mainClass, "main","([Ljava/lang/String;)V"); Code_attribute* code = malloc(sizeof(Code_attribute)); if (method != NULL){ *code = getCode_AttributeFromAttribute_info(method->attributes[0]); //method->attributes[0] }else{ printf("main method not found \n"); return; } newFrame.code = code; newFrame.localVariables = malloc (sizeof(LocalVariable) * code->max_locals); //Stack* operandStack = ; newFrame.operandStack = malloc(sizeof(Stack)); *newFrame.operandStack = initStack(code->max_stack, TYPE_OPERANDSTACK); newFrame.pc = 0; newFrame.classRef = mainClass; newFrame.machine = &JVM; pushStack(&newFrame, JVM.JVMSTACK); void* retCode = executeCode(&JVM, initOpcodes()); if (retCode == NULL){ DEBUG_PRINT(("Execution Stack Finished\n")); } destroyMachine(&JVM); } ================================================ FILE: src/opcode.c ================================================ #include "opcode.h" #include #include #include "stack.h" #include "endianness.h" #include "constantPool.h" #include "frame.h" #include "classFile.h" #include #include "javaClass.h" #include "debug.h" #include "printStream.h" #include "stringBuilder.h" #include uint8_t read1Byte(Frame* frame){ return *(uint8_t*)&frame->code->code[++frame->pc]; // to get the first arg. of the opcode } uint16_t read2Bytes(Frame* frame){ /* OP A1 A2 00 01 02 ^ */ uint16_t val=be16toh(*(uint16_t*)&frame->code->code[frame->pc+1]); frame->pc += 2; return val; } uint32_t read4Bytes(Frame* frame){ uint32_t val=be32toh(*(uint32_t*)&frame->code->code[frame->pc+1]); frame->pc += 4; return val; } uint64_t read8Bytes(Frame* frame){ uint64_t val=be64toh(*(uint64_t*)&frame->code->code[frame->pc+1]); frame->pc += 8; return val; } void* OPCODE_ICONST_M1(Frame* frame){ uint64_t val = -1; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_ICONST_0(Frame* frame){ uint64_t val = 0; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_ICONST_1(Frame* frame){ uint64_t val = 1; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_ICONST_2(Frame* frame){ uint64_t val = 2; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_ICONST_3(Frame* frame){ uint64_t val = 3; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_ICONST_4(Frame* frame){ uint64_t val = 4; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_ICONST_5(Frame* frame){ uint64_t val = 5; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_DCONST_0(Frame* frame){ double val = 0.0; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_DCONST_1(Frame* frame){ double val = 1.0; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_DCONST_2(Frame* frame){ double val = 2.0; pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_BIPUSH(Frame* frame){ uint64_t val = read1Byte(frame); pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_SIPUSH(Frame* frame){ uint64_t val = read2Bytes(frame); pushStack(&val,frame->operandStack); return NULL; } void* OPCODE_LDC(Frame* frame){ uint8_t index = read1Byte(frame); cp_info* aConstant = &frame->classRef->constant_pool[index-1]; if (aConstant->tag == CONSTANT_Integer || aConstant->tag == CONSTANT_Float){ uint64_t val; val = aConstant->info._4BYTES_info.bytes; pushStack(&val, frame->operandStack); }else{ pushStack(&aConstant, frame->operandStack); /* Otherwise, if the run-time constant pool entry is a reference to an instance of class String representing a string literal (§5.1), then a reference to that instance, value, is pushed onto the operand stack. Otherwise, if the run-time constant pool entry is a symbolic reference to a class (§5.1), then the named class is resolved (§5.4.3.1) and a reference to the Class object representing that class, value, is pushed onto the operand stack. Otherwise, the run-time constant pool entry must be a symbolic reference to a method type or a method handle (§5.1). The method type or method handle is resolved (§5.4.3.5) and a reference to the resulting instance of java.lang.invoke.MethodType or java.lang.invoke.MethodHandle, value, is pushed onto the operand stack. */ } return NULL; } void* OPCODE_LDC_W(Frame* frame){ uint16_t index = read2Bytes(frame); cp_info* aConstant = &frame->classRef->constant_pool[index-1]; if (aConstant->tag == CONSTANT_Integer || aConstant->tag == CONSTANT_Float){ uint64_t val; val = aConstant->info._4BYTES_info.bytes; pushStack(&val, frame->operandStack); }else if(aConstant->tag == CONSTANT_Long || aConstant->tag == CONSTANT_Double){ uint64_t val; val = aConstant->info._8BYTES_info.bytes; pushStack(&val, frame->operandStack); }else{ pushStack(&aConstant, frame->operandStack); } return NULL; } void* OPCODE_NLOAD(Frame* frame){ // N represents I,L or D uint8_t index = read1Byte(frame); pushStack(&frame->localVariables[index], frame->operandStack); return NULL; } void* OPCODE_NLOAD_0(Frame* frame){ pushStack(&frame->localVariables[0], frame->operandStack); return NULL; } void* OPCODE_NLOAD_1(Frame* frame){ pushStack(&frame->localVariables[1], frame->operandStack); return NULL; } void* OPCODE_NLOAD_2(Frame* frame){ pushStack(&frame->localVariables[2], frame->operandStack); return NULL; } void* OPCODE_NLOAD_3(Frame* frame){ pushStack(&frame->localVariables[3], frame->operandStack); return NULL; } void* OPCODE_NSTORE(Frame* frame){ uint8_t index = read1Byte(frame); uint64_t val = *(uint64_t*)popStack(frame->operandStack); frame->localVariables[index] = val; return NULL; } void* OPCODE_NSTORE_0(Frame* frame){ uint64_t val = *(uint64_t*)popStack(frame->operandStack); frame->localVariables[0] = val; return NULL; } void* OPCODE_NSTORE_1(Frame* frame){ uint64_t val = *(uint64_t*)popStack(frame->operandStack); frame->localVariables[1] = val; return NULL; } void* OPCODE_NSTORE_2(Frame* frame){ uint64_t val = *(uint64_t*)popStack(frame->operandStack); frame->localVariables[2] = val; return NULL; } void* OPCODE_NSTORE_3(Frame* frame){ uint64_t val = *(uint64_t*)popStack(frame->operandStack); frame->localVariables[3] = val; return NULL; } void* OPCODE_POP(Frame* frame){ //popStack(frame->operandStack); int64_t discardedRetVal = *(int64_t*) popStack(frame->operandStack); DEBUG_PRINT(("Discarded Return Value is %d\n",discardedRetVal)); return NULL; } void* OPCODE_DUP(Frame* frame){ uint64_t val = *(uint64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_DUP_X1(Frame* frame){ uint64_t val1 = *(uint64_t*)popStack(frame->operandStack); uint64_t val2 = *(uint64_t*)popStack(frame->operandStack); pushStack(&val1, frame->operandStack); pushStack(&val2, frame->operandStack); pushStack(&val1, frame->operandStack); return NULL; } ///////////// // INTEGER // ///////////// void* OPCODE_IADD(Frame* frame){ int64_t val = (int32_t)*(int64_t*)popStack(frame->operandStack) + (int32_t)*(int64_t*)popStack(frame->operandStack); // java has no unsigned types, lol // the stacks are 64 bit but the int size is 32 bit pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_ISUB(Frame* frame){ int32_t val2 = *(int64_t*) popStack(frame->operandStack); int32_t val1 = *(int64_t*) popStack(frame->operandStack); int64_t val = val1 - val2; pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_IMUL(Frame* frame){ int64_t val = (int32_t)*(int64_t*)popStack(frame->operandStack) * (int32_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_IDIV(Frame* frame){ int32_t val2 = *(int64_t*) popStack(frame->operandStack); int32_t val1 = *(int64_t*) popStack(frame->operandStack); int64_t val = val1 / val2; pushStack(&val, frame->operandStack); return NULL; } ///////////// /// LONG /// ///////////// void* OPCODE_LADD(Frame* frame){ int64_t val = *(int64_t*)popStack(frame->operandStack) + *(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_LSUB(Frame* frame){ int64_t val2 = *(int64_t*) popStack(frame->operandStack); int64_t val1 = *(int64_t*) popStack(frame->operandStack); int64_t val = val1-val2; pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_LMUL(Frame* frame){ int64_t val = *(int64_t*)popStack(frame->operandStack) * *(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_LDIV(Frame* frame){ int64_t val2 = *(int64_t*) popStack(frame->operandStack); int64_t val1 = *(int64_t*) popStack(frame->operandStack); int64_t val = val1 / val2; pushStack(&val, frame->operandStack); return NULL; } ///////////// /// DOUBLE // ///////////// void* OPCODE_DADD(Frame* frame){ double val = *(double*)popStack(frame->operandStack) + *(double*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_DSUB(Frame* frame){ double val2 = *(double*) popStack(frame->operandStack); double val1 = *(double*) popStack(frame->operandStack); double val = val1-val2; pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_DMUL(Frame* frame){ double val = *(double*)popStack(frame->operandStack) * *(double*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_DDIV(Frame* frame){ double val2 = *(double*) popStack(frame->operandStack); double val1 = *(double*) popStack(frame->operandStack); double val = val1 / val2; pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_D2F(Frame* frame){ float val = (float)*(double*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_D2I(Frame* frame){ int64_t val = (int32_t)*(double*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_D2L(Frame* frame){ int64_t val = (int64_t)*(double*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_DNEG(Frame* frame){ double val = -*(double*) popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_DREM(Frame* frame){ double val2 = *(double*) popStack(frame->operandStack); double val1 = *(double*) popStack(frame->operandStack); double val = fmod(val1,val2); pushStack(&val, frame->operandStack); return NULL; } /////////////// void* OPCODE_I2D(Frame* frame){ double val = (double)(int32_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_I2F(Frame* frame){ float val = (float)(int32_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_I2B(Frame* frame){ int64_t val = (int8_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_I2C(Frame* frame){ //char, whose values are 16-bit unsigned integers representing Unicode code points ... uint64_t val = (uint16_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_I2L(Frame* frame){ uint64_t val = (int32_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_I2S(Frame* frame){ uint64_t val = (int16_t)*(int64_t*)popStack(frame->operandStack); pushStack(&val, frame->operandStack); return NULL; } /////////////// void* OPCODE_LCMP(Frame* frame){ uint64_t val2 = *(uint64_t*) popStack(frame->operandStack); uint64_t val1 = *(uint64_t*) popStack(frame->operandStack); int64_t val = 0; if (val1 > val2){ val = 1; }else if (val1 == val2){ val = 0; }else { val = -1; } pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_IFEQ(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 == 0){ frame->pc += offset - 3; } return NULL; } void* OPCODE_IFNE(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 != 0){ frame->pc += offset - 3; } return NULL; } void* OPCODE_IFLT(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 < 0){ frame->pc += offset - 3; } return NULL; } void* OPCODE_IFLE(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 <= 0){ frame->pc += offset - 3; } return NULL; } void* OPCODE_IFGT(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 > 0){ frame->pc += offset - 3; } return NULL; } void* OPCODE_IFGE(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 >= 0){ frame->pc += offset - 3; } return NULL; } void* OPCODE_DCMPG(Frame* frame){ double val2 = *(double*) popStack(frame->operandStack); double val1 = *(double*) popStack(frame->operandStack); int64_t val = 0; if (val1 > val2){ val = 1; }else if (val1 == val2){ val = 0; }else { val = -1; } pushStack(&val, frame->operandStack); return NULL; } void* OPCODE_IF_ICMPGE(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val2 = (int32_t)*(int64_t*) popStack(frame->operandStack); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 >= val2){ frame->pc += offset - 3; // -1 because the main loop will increase 1 after this opcode to // get the next opcode. Extra -2 because we moved 2 more bytes while // reading the offset } return NULL; } void* OPCODE_IF_ICMPGT(Frame* frame){ uint16_t offset = read2Bytes(frame); int64_t val2 = (int32_t)*(int64_t*) popStack(frame->operandStack); int64_t val1 = (int32_t)*(int64_t*) popStack(frame->operandStack); if (val1 > val2){ frame->pc += offset - 3; // -1 because the main loop will increase 1 after this opcode to // get the next opcode. Extra -2 because we moved 2 more bytes while // reading the offset } return NULL; } void* OPCODE_IINC(Frame* frame){ uint8_t index = read1Byte(frame); uint8_t incrementAmount = read1Byte(frame); *(int64_t*)&frame->localVariables[index] = (int32_t)(*(int64_t*)&frame->localVariables[index] + *(int8_t*)&incrementAmount); return NULL; } void* OPCODE_GOTO(Frame* frame){ uint16_t offset = read2Bytes(frame); // branch offset frame->pc += *(int16_t*)&offset - 3; // -1 because the main loop will increase 1 after this opcode to // get the next opcode Extra -2 because we moved 2 more bytes while // reading the offset return NULL; } void* OPCODE_GETSTATIC(Frame* frame){ uint16_t index = read2Bytes(frame); CONSTANT_Ref_info fieldRefInfo = frame->classRef->constant_pool[index-1].info.ref_info; CONSTANT_NameAndType_info nameAndType = frame->classRef->constant_pool[fieldRefInfo.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info name_utf8 = frame->classRef->constant_pool[nameAndType.name_index-1].info.utf8_info; CONSTANT_Utf8_info desc_utf8 = frame->classRef->constant_pool[nameAndType.descriptor_index-1].info.utf8_info; CONSTANT_Class_info class = frame->classRef->constant_pool[fieldRefInfo.class_index -1].info.class_info; CONSTANT_Utf8_info className_utf8 = frame->classRef->constant_pool[class.name_index-1].info.utf8_info; ClassFile* cf = getClassFromUtf8(className_utf8,frame->machine); if (cf != NULL){ initClass(cf, frame); field_info* field = getStaticField(cf, name_utf8, desc_utf8); pushStack(&field->value, frame->operandStack); }else{ uint64_t val = 0; pushStack(&val, frame->operandStack); } return NULL; } void* OPCODE_NEW(Frame* frame){ uint16_t index = read2Bytes(frame); CONSTANT_Class_info class = frame->classRef->constant_pool[index-1].info.class_info; // class or interface ref info CONSTANT_Utf8_info className_utf8 = frame->classRef->constant_pool[class.name_index-1].info.utf8_info; ClassFile* cf = getClassFromUtf8(className_utf8,frame->machine); uint64_t objectRef; if (cf != NULL){ objectRef = hAlloc(sizeof(JavaClass),frame->machine->heap); JavaClass* newObject = hGet(objectRef,frame->machine->heap); newObject->classFile = cf; initClass(cf, frame); initInstanceFields(newObject); pushStack(&objectRef, frame->operandStack); }else{ uint64_t objectRef = (uint64_t)NULL; if (isUtf8EqualsToString(className_utf8,"java/lang/StringBuilder")){ objectRef = hAlloc(sizeof(cp_info), frame->machine->heap); cp_info* str = hGet(objectRef, frame->machine->heap); str->tag = CONSTANT_Utf8; uint64_t bytesRef = hAlloc(1,frame->machine->heap); str->info.utf8_info.bytes=(char*)bytesRef; char* bytes = hGet(bytesRef, frame->machine->heap); bytes[0]='\0'; str->info.utf8_info.length=1; } pushStack(&objectRef, frame->operandStack); } return NULL; } void* OPCODE_PUTFIELD(Frame* frame){ uint16_t index = read2Bytes(frame); CONSTANT_Ref_info fieldRefInfo = frame->classRef->constant_pool[index-1].info.ref_info; CONSTANT_NameAndType_info nameAndType = frame->classRef->constant_pool[fieldRefInfo.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info name_utf8 = frame->classRef->constant_pool[nameAndType.name_index-1].info.utf8_info; CONSTANT_Utf8_info desc_utf8 = frame->classRef->constant_pool[nameAndType.descriptor_index-1].info.utf8_info; uint64_t val = *(uint64_t*)popStack(frame->operandStack); JavaClass* obj = (JavaClass*)hGet(*(uint64_t*)popStack(frame->operandStack),frame->machine->heap); DEBUG_PRINT(("%x, put val is %d, objRef is %p \n", obj->classFile->magic, val, obj)); putField(obj, name_utf8, desc_utf8, val); return NULL; } void* OPCODE_NRETURN(Frame* frame){ uint64_t* val = malloc(sizeof(val)); // freed later *val = *(uint64_t*)popStack(frame->operandStack); DEBUG_PRINT(("iret val : %d, stackTop: %d\n", *val, frame->operandStack->top)); return val; } void* OPCODE_RETURN(Frame* frame){ frame->pc = frame->code->code_length; // current frame is discarded return NULL; } void* OPCODE_GETFIELD(Frame* frame){ JavaClass* obj = (JavaClass*)hGet(*(uint64_t*)popStack(frame->operandStack),frame->machine->heap); uint16_t index = read2Bytes(frame); CONSTANT_Ref_info fieldRef = frame->classRef->constant_pool[index-1].info.ref_info; CONSTANT_NameAndType_info* nameAndType = &frame->classRef->constant_pool[fieldRef.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info name_utf8 = frame->classRef->constant_pool[nameAndType->name_index-1].info.utf8_info; CONSTANT_Utf8_info desc_utf8 = frame->classRef->constant_pool[nameAndType->descriptor_index-1].info.utf8_info; // different type fields can have the same name in bytecode but not in java. field_info* field = getField(obj,name_utf8, desc_utf8); pushStack(&field->value, frame->operandStack); return NULL; } void* OPCODE_INVOKEVIRTUAL(Frame* frame){ uint16_t index = read2Bytes(frame); if (!(frame->classRef->constant_pool[index-1].tag == CONSTANT_Methodref || frame->classRef->constant_pool[index-1].tag == CONSTANT_InterfaceMethodref)){ printf("invoked method ref is invalid, index is %d\n",index); } CONSTANT_Ref_info methodOrInterfaceRef = frame->classRef->constant_pool[index-1].info.ref_info; CONSTANT_NameAndType_info* nameAndType = &frame->classRef->constant_pool[methodOrInterfaceRef.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info name_utf8 = frame->classRef->constant_pool[nameAndType->name_index-1].info.utf8_info; CONSTANT_Utf8_info desc_utf8 = frame->classRef->constant_pool[nameAndType->descriptor_index-1].info.utf8_info; DEBUG_PRINT( ("Method's Name: %.*s\n",name_utf8.length,name_utf8.bytes)); CONSTANT_Class_info methodClass = frame->classRef->constant_pool[methodOrInterfaceRef.class_index - 1].info.class_info; CONSTANT_Utf8_info className_utf8 = frame->classRef->constant_pool[methodClass.name_index-1].info.utf8_info; DEBUG_PRINT( ("Class's Name: %.*s\n",className_utf8.length,className_utf8.bytes)); ClassFile* cf = getClassFromUtf8(className_utf8, frame->machine); method_info* method = NULL; int numArgs = getNumArgs(frame->classRef, methodOrInterfaceRef) + 1; // +1 for ObjectRef which will be at localVars[0] uint64_t tempLocalVars[numArgs]; for (int argId = numArgs-1; argId >= 0; argId--){ tempLocalVars[argId] = *(uint64_t*) popStack(frame->operandStack); } // hack to run Println if (isUtf8EqualsToString(className_utf8,"java/io/PrintStream") && isUtf8EqualsToString(name_utf8,"println")){ handlePrintStream(tempLocalVars, frame, name_utf8, desc_utf8); return NULL; } if (isUtf8EqualsToString(className_utf8,"java/lang/StringBuilder")){ handleStringBuilder(tempLocalVars,frame, name_utf8, desc_utf8); return NULL; } //// Let C be the class of objectref. The actual method to be invoked //// is selected by the following lookup procedure: // If C contains a declaration for an instance method m that overrides (§5.4.5) // the resolved method, then m is the method to be invoked. JavaClass* obj = (JavaClass*)hGet(tempLocalVars[0],frame->machine->heap); method_info* mPointer = canClassHandleMethod(obj->classFile, name_utf8, desc_utf8); if (mPointer){ method = (mPointer)?(mPointer):method; DEBUG_PRINT(("first overrider runned\n")); }else{ // Otherwise, if C has a superclass, a search for a declaration of an instance // method that overrides the resolved method is performed, starting with the // direct superclass of C and continuing with the direct superclass of that // class, and so forth, until an overriding method is found or no further // superclasses exist. If an overriding method is found, it is the method to be invoked. ClassFile* superClass = obj->classFile; mPointer = NULL; while (superClass->super_class != 0){ CONSTANT_Class_info superClassInfo = superClass -> constant_pool [superClass->super_class-1].info.class_info; CONSTANT_Utf8_info superClassName = superClass -> constant_pool [superClassInfo.name_index-1].info.utf8_info; superClass = getClassFromUtf8 (superClassName, frame->machine); mPointer = canClassHandleMethod(superClass, name_utf8, desc_utf8); if (mPointer){ method = (mPointer)?(mPointer):method; DEBUG_PRINT(("found in superclass runned\n")); break; } } if (mPointer == NULL){ DEBUG_PRINT(("fallBack runned\n")); mPointer = canClassHandleMethod(cf, name_utf8, desc_utf8); method = (mPointer)?(mPointer):method; } } if (method->access_flags != M_ACC_NATIVE){ // todo: error? } Code_attribute code; if (method != NULL){ code = getCode_AttributeFromAttribute_info(method->attributes[0]); }else{ printf("method not found \n"); } Frame newFrame = createNewFrame(code, cf, frame->machine); memcpy(newFrame.localVariables, tempLocalVars, numArgs * sizeof(tempLocalVars[0])); pushStack(&newFrame, frame->machine->JVMSTACK); frame->pc++; return NULL; } void* OPCODE_INVOKESPECIAL(Frame* frame){ uint16_t index = read2Bytes(frame); if (!(frame->classRef->constant_pool[index-1].tag == CONSTANT_Methodref || frame->classRef->constant_pool[index-1].tag == CONSTANT_InterfaceMethodref)){ printf("invoked method ref is invalid, index is %d\n",index); } CONSTANT_Ref_info methodOrInterfaceRef = frame->classRef->constant_pool[index-1].info.ref_info; CONSTANT_NameAndType_info* nameAndType = &frame->classRef->constant_pool[methodOrInterfaceRef.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info name_utf8 = frame->classRef->constant_pool[nameAndType->name_index-1].info.utf8_info; CONSTANT_Utf8_info desc_utf8 = frame->classRef->constant_pool[nameAndType->descriptor_index-1].info.utf8_info; DEBUG_PRINT( ("Method's Name: %.*s\n",name_utf8.length,name_utf8.bytes)); CONSTANT_Class_info methodClass = frame->classRef->constant_pool[methodOrInterfaceRef.class_index - 1].info.class_info; CONSTANT_Utf8_info className_utf8 = frame->classRef->constant_pool[methodClass.name_index-1].info.utf8_info; DEBUG_PRINT( ("Class's Name: %.*s\n",className_utf8.length,className_utf8.bytes)); ClassFile* cf = getClassFromUtf8(className_utf8, frame->machine); int numArgs = getNumArgs(frame->classRef, methodOrInterfaceRef) + 1; // +1 for ObjectRef which will be at localVars[0] if (cf == NULL){ // clear stack for special methods of native classes. for (int i=0; ioperandStack); // pop numArgs times return NULL; } method_info* method = canClassHandleMethod(cf, name_utf8, desc_utf8); Code_attribute code; if (method != NULL){ code = getCode_AttributeFromAttribute_info(method->attributes[0]); }else{ printf("method not found \n"); } Frame newFrame = createNewFrame(code, cf, frame->machine); if (method->access_flags != M_ACC_NATIVE){ for (int argId = numArgs-1; argId >= 0; argId--){ newFrame.localVariables[argId] = *(uint64_t*) popStack(frame->operandStack); } } pushStack(&newFrame, frame->machine->JVMSTACK); frame->pc++; return NULL; } void* OPCODE_INVOKESTATIC(Frame* frame){ uint16_t index = read2Bytes(frame); if (!(frame->classRef->constant_pool[index-1].tag == CONSTANT_Methodref || frame->classRef->constant_pool[index-1].tag == CONSTANT_InterfaceMethodref)){ // index must be a symbolic reference to a method or an interface method // if the resolved method is an instance method, the invokestatic instruction throws an IncompatibleClassChangeError. printf("invoked method ref is invalid, index is %d\n",index); } CONSTANT_Ref_info methodOrInterfaceRef = frame->classRef->constant_pool[index-1].info.ref_info; //DEBUG_PRINT( ("Method's Class Name: %.*s\n",name_utf8.length,name_utf8.bytes)); CONSTANT_Class_info methodClass = frame->classRef->constant_pool[methodOrInterfaceRef.class_index - 1].info.class_info; CONSTANT_Utf8_info className_utf8 = frame->classRef->constant_pool[methodClass.name_index-1].info.utf8_info; DEBUG_PRINT( ("Class's Name: %.*s\n",className_utf8.length,className_utf8.bytes)); ClassFile* cf = getClassFromUtf8(className_utf8, frame->machine); CONSTANT_NameAndType_info* nameAndType = &frame->classRef->constant_pool[methodOrInterfaceRef.name_and_type_index-1].info.nameAndType_info; CONSTANT_Utf8_info name_utf8 = frame->classRef->constant_pool[nameAndType->name_index-1].info.utf8_info; CONSTANT_Utf8_info desc_utf8 = frame->classRef->constant_pool[nameAndType->descriptor_index-1].info.utf8_info; method_info* method = canClassHandleMethod(cf, name_utf8, desc_utf8); Code_attribute code; if (method != NULL){ code = getCode_AttributeFromAttribute_info(method->attributes[0]); //method->attributes[0] }else{ // todo method not found printf("method not found \n"); } Frame newFrame = createNewFrame(code, cf, frame->machine); if (method->access_flags != M_ACC_NATIVE){ // On successful resolution of the method, the class or interface that declared the resolved method is initialized (§5.5) if that class or interface has not already been initialized. int numArgs = getNumArgs(frame->classRef, methodOrInterfaceRef); for (int argId = numArgs-1; argId >= 0; argId--){ newFrame.localVariables[argId] = *(uint64_t*) popStack(frame->operandStack); } } pushStack(&newFrame, frame->machine->JVMSTACK); frame->pc++; return NULL; } OPCODE** initOpcodes(){ // jumptable OPCODE** opcodes = malloc (sizeof(OPCODE*) * 255); // leak, consider static opcodes[0x1] = OPCODE_ICONST_0; // null opcodes[0x2] = OPCODE_ICONST_M1; opcodes[0x3] = OPCODE_ICONST_0; opcodes[0x4] = OPCODE_ICONST_1; opcodes[0x5] = OPCODE_ICONST_2; opcodes[0x6] = OPCODE_ICONST_3; opcodes[0x7] = OPCODE_ICONST_4; opcodes[0x8] = OPCODE_ICONST_5; opcodes[0x9] = OPCODE_ICONST_0; opcodes[0xa] = OPCODE_ICONST_1; opcodes[0xb] = OPCODE_DCONST_0; opcodes[0xc] = OPCODE_DCONST_1; opcodes[0xd] = OPCODE_DCONST_2; opcodes[0xe] = OPCODE_DCONST_0; // double opcodes[0xf] = OPCODE_DCONST_1; opcodes[0x10] = OPCODE_BIPUSH; opcodes[0x11] = OPCODE_SIPUSH; opcodes[0x12] = OPCODE_LDC; opcodes[0x13] = OPCODE_LDC_W; opcodes[0x14] = OPCODE_LDC_W; // same but for 8 bytes. opcodes[0x15] = OPCODE_NLOAD; //iload opcodes[0x16] = OPCODE_NLOAD; //lload opcodes[0x18] = OPCODE_NLOAD; //dload opcodes[0x19] = OPCODE_NLOAD; //aload // iload opcodes[0x1a] = OPCODE_NLOAD_0; opcodes[0x1b] = OPCODE_NLOAD_1; opcodes[0x1c] = OPCODE_NLOAD_2; opcodes[0x1d] = OPCODE_NLOAD_3; // lload opcodes[0x1e] = OPCODE_NLOAD_0; opcodes[0x1f] = OPCODE_NLOAD_1; opcodes[0x20] = OPCODE_NLOAD_2; opcodes[0x21] = OPCODE_NLOAD_3; // dload opcodes[0x26] = OPCODE_NLOAD_0; opcodes[0x27] = OPCODE_NLOAD_1; opcodes[0x28] = OPCODE_NLOAD_2; opcodes[0x29] = OPCODE_NLOAD_3; // aload opcodes[0x2a] = OPCODE_NLOAD_0; opcodes[0x2b] = OPCODE_NLOAD_1; opcodes[0x2c] = OPCODE_NLOAD_2; opcodes[0x2d] = OPCODE_NLOAD_3; opcodes[0x36] = OPCODE_NSTORE; // istore opcodes[0x37] = OPCODE_NSTORE; // lstore opcodes[0x3a] = OPCODE_NSTORE; // astore // istore opcodes[0x3b] = OPCODE_NSTORE_0; opcodes[0x3c] = OPCODE_NSTORE_1; opcodes[0x3d] = OPCODE_NSTORE_2; opcodes[0x3e] = OPCODE_NSTORE_3; // lstore opcodes[0x3f] = OPCODE_NSTORE_0; opcodes[0x39] = OPCODE_NSTORE; // dstore opcodes[0x40] = OPCODE_NSTORE_1; opcodes[0x41] = OPCODE_NSTORE_2; opcodes[0x42] = OPCODE_NSTORE_3; // dstore opcodes[0x47] = OPCODE_NSTORE_0; opcodes[0x48] = OPCODE_NSTORE_1; opcodes[0x49] = OPCODE_NSTORE_2; opcodes[0x4a] = OPCODE_NSTORE_3; // astore opcodes[0x4b] = OPCODE_NSTORE_0; opcodes[0x4c] = OPCODE_NSTORE_1; opcodes[0x4d] = OPCODE_NSTORE_2; opcodes[0x4e] = OPCODE_NSTORE_3; opcodes[0x57] = OPCODE_POP; opcodes[0x59] = OPCODE_DUP; opcodes[0x61] = OPCODE_LADD; opcodes[0x67] = OPCODE_DSUB; opcodes[0x68] = OPCODE_IMUL; opcodes[0x6b] = OPCODE_DMUL; opcodes[0x6d] = OPCODE_LDIV; opcodes[0x6f] = OPCODE_DDIV; opcodes[0x73] = OPCODE_DREM; opcodes[0x77] = OPCODE_DNEG; opcodes[0x84] = OPCODE_IINC; opcodes[0x85] = OPCODE_I2L; opcodes[0x8e] = OPCODE_D2I; opcodes[0x8f] = OPCODE_D2L; opcodes[0x90] = OPCODE_D2F; opcodes[0x94] = OPCODE_LCMP; opcodes[0x97] = OPCODE_DCMPG; //dcmpl opcodes[0x98] = OPCODE_DCMPG; //dcmpg opcodes[0x99] = OPCODE_IFEQ; opcodes[0x9a] = OPCODE_IFNE; opcodes[0x9b] = OPCODE_IFLT; opcodes[0x9c] = OPCODE_IFGE; opcodes[0x9d] = OPCODE_IFGT; opcodes[0x9e] = OPCODE_IFLE; opcodes[0xa2] = OPCODE_IF_ICMPGE; opcodes[0xa3] = OPCODE_IF_ICMPGT; opcodes[0xa7] = OPCODE_GOTO; opcodes[0xaf] = OPCODE_NRETURN; // d opcodes[0xb0] = OPCODE_NRETURN; opcodes[0xb1] = OPCODE_RETURN; opcodes[0xb2] = OPCODE_GETSTATIC; opcodes[0xb4] = OPCODE_GETFIELD; opcodes[0xb5] = OPCODE_PUTFIELD; opcodes[0xb6] = OPCODE_INVOKEVIRTUAL; opcodes[0xb7] = OPCODE_INVOKESPECIAL; opcodes[0xb8] = OPCODE_INVOKESTATIC; opcodes[0xbb] = OPCODE_NEW; opcodes[0x60] = OPCODE_IADD; opcodes[0xac] = OPCODE_NRETURN; return opcodes; } ================================================ FILE: src/printStream.c ================================================ #include "printStream.h" #include #include "constantPool.h" #include "frame.h" #include void handlePrintStream(uint64_t* localVariables, Frame* frame, CONSTANT_Utf8_info name_utf8, CONSTANT_Utf8_info desc_utf8){ if (isUtf8EqualsToString(desc_utf8,"(I)V")){ printf("%d\n",localVariables[1]); return; } cp_info* printArg = (cp_info*)localVariables[1]; // 0 is ref, 1 is arg1 switch (printArg->tag) { case CONSTANT_Utf8: // created from native toStrings, needs to be freed after use. printf("%.*s\n",printArg->info.utf8_info.length,printArg->info.utf8_info.bytes); free(printArg); break; case CONSTANT_String:; CONSTANT_Utf8_info str = frame->classRef->constant_pool[printArg->info.string_info.string_index-1].info.utf8_info; printf("%.*s\n",str.length,str.bytes); break; default: printf("OBJECT, TYPE: %d, @ %p\n", printArg->tag, printArg); break; } } ================================================ FILE: src/stack.c ================================================ #include "stack.h" #include "frame.h" #include #include #include "javaClass.h" Stack initStack(uint64_t numItems, StackType stackType){ // size is in operands //The maximum depth of the operand stack of a frame is determined at compile-time and //is supplied along with the code for the method associated with the frame Stack stack; stack.maxSize = stackType * numItems; // in bytes stack.top = 0; // in bytes, again stack.stackType = stackType; stack.stack = malloc(stackType * numItems); // afaik all stacks in here are frame sized. leak return stack; } void pushStack(void* val, Stack* stack){ if (stack->top + stack->stackType <= stack->maxSize){ if (stack->stackType == TYPE_JVMSTACK){ *(Frame*)(&stack->stack[stack->top])=*(Frame*)val; }else if(stack->stackType == TYPE_OPERANDSTACK){ *(uint64_t*)(&stack->stack[stack->top])=*(uint64_t*)val; // this stack is implemented at DWORD size on 32 bit machines // QWORD size on 64 bit machines. On the other hand, the values we // push on operand stack always 16 bit. } stack->top += stack->stackType; }else{ // todo stackoverflow err printf("StackOverFlow Error\n"); } } void* popStack(Stack* stack){ if ((int64_t)stack->top - stack->stackType >= 0){ void* val; stack->top -= stack->stackType; if (stack->stackType == TYPE_JVMSTACK){ val = (Frame*)(&stack->stack[stack->top]); }else if(stack->stackType == TYPE_OPERANDSTACK){ val = (uint64_t*)(&stack->stack[stack->top]); } return val; }else{ // todo stack empty return NULL; } } void* peekStack(Stack* stack){ return (stack->top > 0)?(Stack*)(stack->stack + stack->top - stack->stackType):(NULL); } void destroyStack(Stack* stack){ free(stack->stack); free(stack); } ================================================ FILE: src/stringBuilder.c ================================================ #include "stringBuilder.h" #include #include "constantPool.h" #include "frame.h" #include #include #include #include "debug.h" void handleStringBuilder(uint64_t* localVariables, Frame* frame, CONSTANT_Utf8_info name_utf8, CONSTANT_Utf8_info desc_utf8){ cp_info* strRef = (cp_info*)hGet(localVariables[0],frame->machine->heap); if (isUtf8EqualsToString(name_utf8,"append")){ if (isUtf8EqualsToString(desc_utf8,"(I)Ljava/lang/StringBuilder;") || isUtf8EqualsToString(desc_utf8,"(J)Ljava/lang/StringBuilder;")){ int stringLength = floor((*(int64_t*)&localVariables[1]==0)?0:log10(abs(*(int64_t*)&localVariables[1]))) + 1; strRef->info.utf8_info.bytes = (char*)hExtend((uint64_t)(strRef->info.utf8_info.bytes), stringLength, frame->machine->heap); char* charRef = hGet((uint64_t)strRef->info.utf8_info.bytes, frame->machine->heap); sprintf(charRef,"%s%d",charRef,*(int64_t*)&localVariables[1]); strRef->info.utf8_info.length += stringLength; DEBUG_PRINT(("append test: %s\n", charRef)); }else if(isUtf8EqualsToString(desc_utf8,"(Ljava/lang/String;)Ljava/lang/StringBuilder;")){ cp_info* appendRef = (cp_info*)localVariables[1]; CONSTANT_Utf8_info appendStrRef; if (appendRef->tag == CONSTANT_String){ appendStrRef = frame->classRef->constant_pool[appendRef->info.string_info.string_index-1].info.utf8_info; }else{ appendStrRef = appendRef->info.utf8_info; } strRef->info.utf8_info.bytes = (char*)hExtend((uint64_t)(strRef->info.utf8_info.bytes), appendStrRef.length, frame->machine->heap); char* charRef = hGet((uint64_t)strRef->info.utf8_info.bytes, frame->machine->heap); strncat(charRef,appendStrRef.bytes,appendStrRef.length); strRef->info.utf8_info.length += appendStrRef.length; DEBUG_PRINT(("append test: %s, strLen: %d, ldc: %.*s\n", charRef,frame->machine->heap->heap[(uint64_t)strRef->info.utf8_info.bytes].size, appendStrRef.length, appendStrRef.bytes)); } pushStack(&localVariables[0], frame->operandStack); }else if(isUtf8EqualsToString(name_utf8,"toString")){ cp_info* asString = malloc(sizeof(cp_info)); asString->tag = CONSTANT_Utf8; asString->info.utf8_info.length = strRef->info.utf8_info.length; asString->info.utf8_info.bytes = hGet((uint64_t)strRef->info.utf8_info.bytes, frame->machine->heap); pushStack(&asString, frame->operandStack); } } ================================================ FILE: test/Factorial.class.txt ================================================ a13294 ================================================ FILE: test/Factorial.java ================================================ public class Factorial { public static void main(String[] args) { final int NUM_FACTS = 12; long test=0; for (long a=0; a<100; a++){ for(int i = 0; i < NUM_FACTS; i++){ test += a/factorial(i); } } System.out.println("a"+test); } public static int factorial(int n) { int result = 1; for(int i = 2; i <= n; i++) result *= i; return result; } } ================================================ FILE: test/HelloWorld.class.txt ================================================ Hello World! ================================================ FILE: test/HelloWorld.java ================================================ /* HelloWorld.java */ public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } } ================================================ FILE: test/InstanceTest.class.txt ================================================ 12345 12345 54321 ================================================ FILE: test/InstanceTest.java ================================================ class InstanceTest { public int num; public static void main(String[] args) { System.out.println(""+test_single()); System.out.println(""+test_multiple_1()); System.out.println(""+test_multiple_2()); } public InstanceTest() { num = 0; } public void set_num(int n) { num = n; } public int get_num() { return num; } public static int test_single() { InstanceTest i = new InstanceTest(); i.set_num(12345); return i.get_num(); } public static int test_multiple_1() { InstanceTest i1 = new InstanceTest(); InstanceTest i2 = new InstanceTest(); i1.set_num(12345); i2.set_num(54321); return i1.get_num(); } public static int test_multiple_2() { InstanceTest i1 = new InstanceTest(); InstanceTest i2 = new InstanceTest(); i1.set_num(12345); i2.set_num(54321); return i2.get_num(); } } ================================================ FILE: test/StringExample.class.txt ================================================ s1: Computer Science s2: Computer Science 307 s4: is fun s5: Computer Science 307is fun s6: 8total s7: total 35 s8: 35total ================================================ FILE: test/StringExample.java ================================================ public class StringExample { public static void main(String[] args) { String s1 = "Computer Science"; int x = 307; String s2 = s1 + " " + x; //String s3 = s2.substring(10,17); String s4 = "is fun"; String s5 = s2 + s4; System.out.println("s1: " + s1); System.out.println("s2: " + s2); //System.out.println("s3: " + s3); System.out.println("s4: " + s4); System.out.println("s5: " + s5); //showing effect of precedence x = 3; int y = 5; String s6 = x + y + "total"; String s7 = "total " + x + y; String s8 = " " + x + y + "total"; System.out.println("s6: " + s6); System.out.println("s7: " + s7); System.out.println("s8: " + s8); } } ================================================ FILE: test/returnTest.class.txt ================================================ 75 ================================================ FILE: test/returnTest.java ================================================ public class returnTest { public static void main(String[] args) { System.out.println(topla(11,4)); } public static int topla(int a, int b){ int var = 0; for (int i=0; i<5; i++){ var += a+b; } return var; } } ================================================ FILE: test.sh ================================================ #!/bin/sh cd test rm *.class rm test/*.out javac *.java cd .. for classFile in test/*.class; do echo "Running ${classFile}" ./bin/main "${classFile%.class}" > "${classFile}.out" if diff "${classFile}.txt" "${classFile}.out"; then echo "succeeded" else echo "${classFile} failed" exit 1 fi done